summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2019-04-16 15:13:16 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2019-04-16 15:14:45 -0400
commit8302f6df29b10886626a55bc6581d9428d1eedb5 (patch)
tree4d38caed2999c534aa994b861d71faa59f81fd0f
parent08b175c6a2a010e92d198398fd5709033442a3ae (diff)
bcachefs: Fix hang while shutting down
If the allocator thread exited before bch2_dev_allocator_stop() was called (because of an error), bch2_dev_allocator_quiesce() could hang.
-rw-r--r--fs/bcachefs/alloc_background.c22
-rw-r--r--fs/bcachefs/bcachefs.h8
-rw-r--r--fs/bcachefs/movinggc.c2
3 files changed, 21 insertions, 11 deletions
diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c
index ee0c7e9e8246..b0162600bfe3 100644
--- a/fs/bcachefs/alloc_background.c
+++ b/fs/bcachefs/alloc_background.c
@@ -597,6 +597,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()) {
@@ -619,6 +622,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;
}
@@ -1118,14 +1124,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);
}
@@ -1183,6 +1189,7 @@ static int bch2_allocator_thread(void *arg)
int ret;
set_freezable();
+ ca->allocator_state = ALLOCATOR_RUNNING;
while (1) {
cond_resched();
@@ -1241,9 +1248,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);
@@ -1252,7 +1256,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);
@@ -1265,6 +1268,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;
}
@@ -1456,7 +1461,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: */
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 6a68376d12cc..438e646e225e 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/bcachefs.h
@@ -444,8 +444,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;
diff --git a/fs/bcachefs/movinggc.c b/fs/bcachefs/movinggc.c
index f1f51dc2587e..c6a4f5b9af0e 100644
--- a/fs/bcachefs/movinggc.c
+++ b/fs/bcachefs/movinggc.c
@@ -115,7 +115,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;