summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2020-06-17 17:30:38 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2020-06-17 17:32:47 -0400
commit53413714a49db20f28442c8e2b27b508564bf4fe (patch)
tree7fc15e8284092b217e996d0ac9de408639c5c4e4
parent70a4b086a2948c539ba143a88f37c3d9b765f677 (diff)
bcachefs: Fix a deadlock in the RO path
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--fs/bcachefs/btree_gc.c9
-rw-r--r--fs/bcachefs/buckets.c5
2 files changed, 11 insertions, 3 deletions
diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c
index 7293b8fedd27..8771ef1f07cc 100644
--- a/fs/bcachefs/btree_gc.c
+++ b/fs/bcachefs/btree_gc.c
@@ -930,7 +930,12 @@ int bch2_gc_gens(struct bch_fs *c)
unsigned i;
int ret;
- down_read(&c->state_lock);
+ /*
+ * Ideally we would be using state_lock and not gc_lock here, but that
+ * introduces a deadlock in the RO path - we currently take the state
+ * lock at the start of going RO, thus the gc thread may get stuck:
+ */
+ down_read(&c->gc_lock);
for_each_member_device(ca, c, i) {
down_read(&ca->bucket_lock);
@@ -957,7 +962,7 @@ int bch2_gc_gens(struct bch_fs *c)
up_read(&ca->bucket_lock);
}
err:
- up_read(&c->state_lock);
+ up_read(&c->gc_lock);
return ret;
}
diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c
index a9935cc3df2e..f75edb3c175a 100644
--- a/fs/bcachefs/buckets.c
+++ b/fs/bcachefs/buckets.c
@@ -2019,6 +2019,7 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
bch2_copygc_stop(ca);
if (resize) {
+ down_write(&c->gc_lock);
down_write(&ca->bucket_lock);
percpu_down_write(&c->mark_lock);
}
@@ -2041,8 +2042,10 @@ int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
swap(ca->buckets_nouse, buckets_nouse);
- if (resize)
+ if (resize) {
percpu_up_write(&c->mark_lock);
+ up_write(&c->gc_lock);
+ }
spin_lock(&c->freelist_lock);
for (i = 0; i < RESERVE_NR; i++) {