bcachefs: Fix hang while shutting down
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 16 Apr 2019 19:13:16 +0000 (15:13 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:20 +0000 (17:08 -0400)
If the allocator thread exited before bch2_dev_allocator_stop() was
called (because of an error), bch2_dev_allocator_quiesce() could hang.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/alloc_background.c
fs/bcachefs/bcachefs.h
fs/bcachefs/movinggc.c

index c254c08af9d174d15ddc3171b3ab8d3fe24122fd..4a8f6fa3db1e258c287c3ca7e950d498c0955e6d 100644 (file)
@@ -598,6 +598,9 @@ static int wait_buckets_available(struct bch_fs *c, struct bch_dev *ca)
        unsigned long gc_count = c->gc_count;
        int ret = 0;
 
+       ca->allocator_state = ALLOCATOR_BLOCKED;
+       closure_wake_up(&c->freelist_wait);
+
        while (1) {
                set_current_state(TASK_INTERRUPTIBLE);
                if (kthread_should_stop()) {
@@ -620,6 +623,9 @@ static int wait_buckets_available(struct bch_fs *c, struct bch_dev *ca)
        }
 
        __set_current_state(TASK_RUNNING);
+       ca->allocator_state = ALLOCATOR_RUNNING;
+       closure_wake_up(&c->freelist_wait);
+
        return ret;
 }
 
@@ -1119,14 +1125,14 @@ static int push_invalidated_bucket(struct bch_fs *c, struct bch_dev *ca, size_t
                                fifo_pop(&ca->free_inc, bucket);
 
                                closure_wake_up(&c->freelist_wait);
-                               ca->allocator_blocked_full = false;
+                               ca->allocator_state = ALLOCATOR_RUNNING;
 
                                spin_unlock(&c->freelist_lock);
                                goto out;
                        }
 
-               if (!ca->allocator_blocked_full) {
-                       ca->allocator_blocked_full = true;
+               if (ca->allocator_state != ALLOCATOR_BLOCKED_FULL) {
+                       ca->allocator_state = ALLOCATOR_BLOCKED_FULL;
                        closure_wake_up(&c->freelist_wait);
                }
 
@@ -1184,6 +1190,7 @@ static int bch2_allocator_thread(void *arg)
        int ret;
 
        set_freezable();
+       ca->allocator_state = ALLOCATOR_RUNNING;
 
        while (1) {
                cond_resched();
@@ -1242,9 +1249,6 @@ static int bch2_allocator_thread(void *arg)
                        if (!nr ||
                            (nr < ALLOC_SCAN_BATCH(ca) &&
                             !fifo_full(&ca->free[RESERVE_MOVINGGC]))) {
-                               ca->allocator_blocked = true;
-                               closure_wake_up(&c->freelist_wait);
-
                                ret = wait_buckets_available(c, ca);
                                if (ret) {
                                        up_read(&c->gc_lock);
@@ -1253,7 +1257,6 @@ static int bch2_allocator_thread(void *arg)
                        }
                } while (!nr);
 
-               ca->allocator_blocked = false;
                up_read(&c->gc_lock);
 
                pr_debug("%zu buckets to invalidate", nr);
@@ -1266,6 +1269,8 @@ static int bch2_allocator_thread(void *arg)
 
 stop:
        pr_debug("alloc thread stopping (ret %i)", ret);
+       ca->allocator_state = ALLOCATOR_STOPPED;
+       closure_wake_up(&c->freelist_wait);
        return 0;
 }
 
@@ -1457,7 +1462,8 @@ void bch2_dev_allocator_add(struct bch_fs *c, struct bch_dev *ca)
 void bch2_dev_allocator_quiesce(struct bch_fs *c, struct bch_dev *ca)
 {
        if (ca->alloc_thread)
-               closure_wait_event(&c->freelist_wait, ca->allocator_blocked_full);
+               closure_wait_event(&c->freelist_wait,
+                                  ca->allocator_state != ALLOCATOR_RUNNING);
 }
 
 /* stop allocator thread: */
index 8acdc7ffeca346fdffb5bb09ab54b48407e4d6a7..72f9f5f9abe908970a2f74e1b2b9fcbf58a6b391 100644 (file)
@@ -447,8 +447,12 @@ struct bch_dev {
         * XXX: this should be an enum for allocator state, so as to include
         * error state
         */
-       bool                    allocator_blocked;
-       bool                    allocator_blocked_full;
+       enum {
+               ALLOCATOR_STOPPED,
+               ALLOCATOR_RUNNING,
+               ALLOCATOR_BLOCKED,
+               ALLOCATOR_BLOCKED_FULL,
+       }                       allocator_state;
 
        alloc_heap              alloc_heap;
 
index aba13e6ea4ffc72870e8efdd4973497b72b69544..d97be76da58fc00d118e226b11a15595ce9186c1 100644 (file)
@@ -116,7 +116,7 @@ static bool have_copygc_reserve(struct bch_dev *ca)
 
        spin_lock(&ca->freelist_lock);
        ret = fifo_full(&ca->free[RESERVE_MOVINGGC]) ||
-               ca->allocator_blocked;
+               ca->allocator_state != ALLOCATOR_RUNNING;
        spin_unlock(&ca->freelist_lock);
 
        return ret;