bcache: Convert gc to a kthread
authorKent Overstreet <kmo@daterainc.com>
Fri, 25 Oct 2013 00:19:26 +0000 (17:19 -0700)
committerKent Overstreet <kmo@daterainc.com>
Mon, 11 Nov 2013 05:56:04 +0000 (21:56 -0800)
We needed a dedicated rescuer workqueue for gc anyways... and gc was
conceptually a dedicated thread, just one that wasn't running all the
time. Switch it to a dedicated thread to make the code a bit more
straightforward.

Signed-off-by: Kent Overstreet <kmo@daterainc.com>
drivers/md/bcache/alloc.c
drivers/md/bcache/bcache.h
drivers/md/bcache/btree.c
drivers/md/bcache/btree.h
drivers/md/bcache/movinggc.c
drivers/md/bcache/request.c
drivers/md/bcache/super.c
drivers/md/bcache/sysfs.c

index 1b64e662e81b2a2e5543e6fd16685a4aee13f530..b9bd5866055d5350c1e77f40678f9ddbe9b0245e 100644 (file)
@@ -210,7 +210,7 @@ static void invalidate_buckets_lru(struct cache *ca)
                         * multiple times when it can't do anything
                         */
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
 
@@ -235,7 +235,7 @@ static void invalidate_buckets_fifo(struct cache *ca)
 
                if (++checked >= ca->sb.nbuckets) {
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
        }
@@ -260,7 +260,7 @@ static void invalidate_buckets_random(struct cache *ca)
 
                if (++checked >= ca->sb.nbuckets / 2) {
                        ca->invalidate_needs_gc = 1;
-                       bch_queue_gc(ca->set);
+                       wake_up_gc(ca->set);
                        return;
                }
        }
index d3520748bc27b259d0d7817f277c67c3e0914755..09410eb07d82d85c416208157542c5caa6205526 100644 (file)
@@ -773,7 +773,7 @@ struct cache_set {
        struct gc_stat          gc_stats;
        size_t                  nbuckets;
 
-       struct closure_with_waitlist gc;
+       struct task_struct      *gc_thread;
        /* Where in the btree gc currently is */
        struct bkey             gc_done;
 
@@ -786,11 +786,10 @@ struct cache_set {
        /* Counts how many sectors bio_insert has added to the cache */
        atomic_t                sectors_to_gc;
 
-       struct closure          moving_gc;
-       struct closure_waitlist moving_gc_wait;
+       wait_queue_head_t       moving_gc_wait;
        struct keybuf           moving_gc_keys;
        /* Number of moving GC bios in flight */
-       atomic_t                in_flight;
+       struct semaphore        moving_in_flight;
 
        struct btree            *root;
 
@@ -1176,7 +1175,7 @@ bool bch_cache_set_error(struct cache_set *, const char *, ...);
 void bch_prio_write(struct cache *);
 void bch_write_bdev_super(struct cached_dev *, struct closure *);
 
-extern struct workqueue_struct *bcache_wq, *bch_gc_wq;
+extern struct workqueue_struct *bcache_wq;
 extern const char * const bch_cache_modes[];
 extern struct mutex bch_register_lock;
 extern struct list_head bch_cache_sets;
index 935d90df397b8481e3de2f2a453356d7637567d3..17bfd87fc8f423af712946e2215905df3417e0a4 100644 (file)
@@ -28,7 +28,9 @@
 
 #include <linux/slab.h>
 #include <linux/bitops.h>
+#include <linux/freezer.h>
 #include <linux/hash.h>
+#include <linux/kthread.h>
 #include <linux/prefetch.h>
 #include <linux/random.h>
 #include <linux/rcupdate.h>
@@ -105,7 +107,6 @@ static const char *op_type(struct btree_op *op)
 #define PTR_HASH(c, k)                                                 \
        (((k)->ptr[0] >> c->bucket_bits) | PTR_GEN(k, 0))
 
-struct workqueue_struct *bch_gc_wq;
 static struct workqueue_struct *btree_io_wq;
 
 void bch_btree_op_init_stack(struct btree_op *op)
@@ -732,12 +733,9 @@ int bch_btree_cache_alloc(struct cache_set *c)
 {
        unsigned i;
 
-       /* XXX: doesn't check for errors */
-
-       closure_init_unlocked(&c->gc);
-
        for (i = 0; i < mca_reserve(c); i++)
-               mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL);
+               if (!mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL))
+                       return -ENOMEM;
 
        list_splice_init(&c->btree_cache,
                         &c->btree_cache_freeable);
@@ -1456,9 +1454,8 @@ size_t bch_btree_gc_finish(struct cache_set *c)
        return available;
 }
 
-static void bch_btree_gc(struct closure *cl)
+static void bch_btree_gc(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
        int ret;
        unsigned long available;
        struct gc_stat stats;
@@ -1483,7 +1480,7 @@ static void bch_btree_gc(struct closure *cl)
 
        if (ret) {
                pr_warn("gc failed!");
-               continue_at(cl, bch_btree_gc, bch_gc_wq);
+               return;
        }
 
        /* Possibly wait for new UUIDs or whatever to hit disk */
@@ -1505,12 +1502,35 @@ static void bch_btree_gc(struct closure *cl)
 
        trace_bcache_gc_end(c);
 
-       continue_at(cl, bch_moving_gc, bch_gc_wq);
+       bch_moving_gc(c);
+}
+
+static int bch_gc_thread(void *arg)
+{
+       struct cache_set *c = arg;
+
+       while (1) {
+               bch_btree_gc(c);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (kthread_should_stop())
+                       break;
+
+               try_to_freeze();
+               schedule();
+       }
+
+       return 0;
 }
 
-void bch_queue_gc(struct cache_set *c)
+int bch_gc_thread_start(struct cache_set *c)
 {
-       closure_trylock_call(&c->gc.cl, bch_btree_gc, bch_gc_wq, &c->cl);
+       c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc");
+       if (IS_ERR(c->gc_thread))
+               return PTR_ERR(c->gc_thread);
+
+       set_task_state(c->gc_thread, TASK_INTERRUPTIBLE);
+       return 0;
 }
 
 /* Initial partial gc */
@@ -2480,14 +2500,12 @@ void bch_btree_exit(void)
 {
        if (btree_io_wq)
                destroy_workqueue(btree_io_wq);
-       if (bch_gc_wq)
-               destroy_workqueue(bch_gc_wq);
 }
 
 int __init bch_btree_init(void)
 {
-       if (!(bch_gc_wq = create_singlethread_workqueue("bch_btree_gc")) ||
-           !(btree_io_wq = create_singlethread_workqueue("bch_btree_io")))
+       btree_io_wq = create_singlethread_workqueue("bch_btree_io");
+       if (!btree_io_wq)
                return -ENOMEM;
 
        return 0;
index d691d954730e6d0235f61e5e74fd57a23732c476..fa9641aaed39bf761156dda2101e1d6a80c0cb48 100644 (file)
@@ -388,12 +388,18 @@ int bch_btree_insert(struct btree_op *, struct cache_set *, struct keylist *);
 
 int bch_btree_search_recurse(struct btree *, struct btree_op *);
 
-void bch_queue_gc(struct cache_set *);
+int bch_gc_thread_start(struct cache_set *);
 size_t bch_btree_gc_finish(struct cache_set *);
-void bch_moving_gc(struct closure *);
+void bch_moving_gc(struct cache_set *);
 int bch_btree_check(struct cache_set *, struct btree_op *);
 uint8_t __bch_btree_mark_key(struct cache_set *, int, struct bkey *);
 
+static inline void wake_up_gc(struct cache_set *c)
+{
+       if (c->gc_thread)
+               wake_up_process(c->gc_thread);
+}
+
 void bch_keybuf_init(struct keybuf *);
 void bch_refill_keybuf(struct cache_set *, struct keybuf *, struct bkey *,
                       keybuf_pred_fn *);
index 2c42377a65aad993388851ebc20c430ae1fdf47c..6ba050456ec827ad96dfc7d6edd665a0837a8031 100644 (file)
@@ -57,8 +57,7 @@ static void write_moving_finish(struct closure *cl)
 
        bch_keybuf_del(&io->s.op.c->moving_gc_keys, io->w);
 
-       atomic_dec_bug(&io->s.op.c->in_flight);
-       closure_wake_up(&io->s.op.c->moving_gc_wait);
+       up(&io->s.op.c->moving_in_flight);
 
        closure_return_with_destructor(cl, moving_io_destructor);
 }
@@ -113,7 +112,7 @@ static void write_moving(struct closure *cl)
                bch_data_insert(&s->op.cl);
        }
 
-       continue_at(cl, write_moving_finish, bch_gc_wq);
+       continue_at(cl, write_moving_finish, system_wq);
 }
 
 static void read_moving_submit(struct closure *cl)
@@ -124,15 +123,17 @@ static void read_moving_submit(struct closure *cl)
 
        bch_submit_bbio(bio, s->op.c, &io->w->key, 0);
 
-       continue_at(cl, write_moving, bch_gc_wq);
+       continue_at(cl, write_moving, system_wq);
 }
 
-static void read_moving(struct closure *cl)
+static void read_moving(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, moving_gc);
        struct keybuf_key *w;
        struct moving_io *io;
        struct bio *bio;
+       struct closure cl;
+
+       closure_init_stack(&cl);
 
        /* XXX: if we error, background writeback could stall indefinitely */
 
@@ -164,13 +165,8 @@ static void read_moving(struct closure *cl)
 
                trace_bcache_gc_copy(&w->key);
 
-               closure_call(&io->s.cl, read_moving_submit, NULL, &c->gc.cl);
-
-               if (atomic_inc_return(&c->in_flight) >= 64) {
-                       closure_wait_event(&c->moving_gc_wait, cl,
-                                          atomic_read(&c->in_flight) < 64);
-                       continue_at(cl, read_moving, bch_gc_wq);
-               }
+               down(&c->moving_in_flight);
+               closure_call(&io->s.cl, read_moving_submit, NULL, &cl);
        }
 
        if (0) {
@@ -180,7 +176,7 @@ err:                if (!IS_ERR_OR_NULL(w->private))
                bch_keybuf_del(&c->moving_gc_keys, w);
        }
 
-       closure_return(cl);
+       closure_sync(&cl);
 }
 
 static bool bucket_cmp(struct bucket *l, struct bucket *r)
@@ -193,15 +189,14 @@ static unsigned bucket_heap_top(struct cache *ca)
        return GC_SECTORS_USED(heap_peek(&ca->heap));
 }
 
-void bch_moving_gc(struct closure *cl)
+void bch_moving_gc(struct cache_set *c)
 {
-       struct cache_set *c = container_of(cl, struct cache_set, gc.cl);
        struct cache *ca;
        struct bucket *b;
        unsigned i;
 
        if (!c->copy_gc_enabled)
-               closure_return(cl);
+               return;
 
        mutex_lock(&c->bucket_lock);
 
@@ -242,13 +237,11 @@ void bch_moving_gc(struct closure *cl)
 
        c->moving_gc_keys.last_scanned = ZERO_KEY;
 
-       closure_init(&c->moving_gc, cl);
-       read_moving(&c->moving_gc);
-
-       closure_return(cl);
+       read_moving(c);
 }
 
 void bch_moving_init_cache_set(struct cache_set *c)
 {
        bch_keybuf_init(&c->moving_gc_keys);
+       sema_init(&c->moving_in_flight, 64);
 }
index 26d18f4bf4a0004a0ef04ec44b5470d0f5645f98..f779eb420d69f2ffe8d3602889430248a5f80544 100644 (file)
@@ -520,7 +520,7 @@ static void bch_data_insert_start(struct closure *cl)
 
        if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) {
                set_gc_sectors(op->c);
-               bch_queue_gc(op->c);
+               wake_up_gc(op->c);
        }
 
        /*
index 84398a82fbe34b9c6e2dc79922ffba3203ab98b9..f89e2296bde13e4444e80f04b31db176d64c4778 100644 (file)
@@ -1342,6 +1342,9 @@ static void cache_set_flush(struct closure *cl)
        kobject_put(&c->internal);
        kobject_del(&c->kobj);
 
+       if (c->gc_thread)
+               kthread_stop(c->gc_thread);
+
        if (!IS_ERR_OR_NULL(c->root))
                list_add(&c->root->list, &c->btree_cache);
 
@@ -1579,8 +1582,6 @@ static void run_cache_set(struct cache_set *c)
                bch_journal_replay(c, &journal, &op);
        } else {
                pr_notice("invalidating existing data");
-               /* Don't want invalidate_buckets() to queue a gc yet */
-               closure_lock(&c->gc, NULL);
 
                for_each_cache(ca, c, i) {
                        unsigned j;
@@ -1606,12 +1607,12 @@ static void run_cache_set(struct cache_set *c)
 
                err = "cannot allocate new UUID bucket";
                if (__uuid_write(c))
-                       goto err_unlock_gc;
+                       goto err;
 
                err = "cannot allocate new btree root";
                c->root = bch_btree_node_alloc(c, 0);
                if (IS_ERR_OR_NULL(c->root))
-                       goto err_unlock_gc;
+                       goto err;
 
                bkey_copy_key(&c->root->key, &MAX_KEY);
                bch_btree_node_write(c->root, &op.cl);
@@ -1628,12 +1629,12 @@ static void run_cache_set(struct cache_set *c)
 
                bch_journal_next(&c->journal);
                bch_journal_meta(c, &op.cl);
-
-               /* Unlock */
-               closure_set_stopped(&c->gc.cl);
-               closure_put(&c->gc.cl);
        }
 
+       err = "error starting gc thread";
+       if (bch_gc_thread_start(c))
+               goto err;
+
        closure_sync(&op.cl);
        c->sb.last_mount = get_seconds();
        bcache_write_super(c);
@@ -1644,9 +1645,6 @@ static void run_cache_set(struct cache_set *c)
        flash_devs_run(c);
 
        return;
-err_unlock_gc:
-       closure_set_stopped(&c->gc.cl);
-       closure_put(&c->gc.cl);
 err:
        closure_sync(&op.cl);
        /* XXX: test this, it's broken */
index b3a66f17231dd7d5c6a5f9af939ba8d28b917770..ab286b9b5e40a1eb9e11c36d767258b0071f0969 100644 (file)
@@ -566,7 +566,7 @@ STORE(__bch_cache_set)
        }
 
        if (attr == &sysfs_trigger_gc)
-               bch_queue_gc(c);
+               wake_up_gc(c);
 
        if (attr == &sysfs_prune_cache) {
                struct shrink_control sc;