diff options
Diffstat (limited to 'libbcachefs/btree_gc.c')
-rw-r--r-- | libbcachefs/btree_gc.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/libbcachefs/btree_gc.c b/libbcachefs/btree_gc.c index e8abc193..8771ef1f 100644 --- a/libbcachefs/btree_gc.c +++ b/libbcachefs/btree_gc.c @@ -902,6 +902,7 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); const struct bch_extent_ptr *ptr; + percpu_down_read(&c->mark_lock); bkey_for_each_ptr(ptrs, ptr) { struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev); struct bucket *g = PTR_BUCKET(ca, ptr, false); @@ -914,6 +915,7 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) } } + percpu_up_read(&c->mark_lock); } bch2_trans_exit(&trans); @@ -923,17 +925,25 @@ static int bch2_gc_btree_gens(struct bch_fs *c, enum btree_id id) int bch2_gc_gens(struct bch_fs *c) { struct bch_dev *ca; + struct bucket_array *buckets; + struct bucket *g; 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) { - struct bucket_array *buckets = bucket_array(ca); - struct bucket *g; + down_read(&ca->bucket_lock); + buckets = bucket_array(ca); for_each_bucket(g, buckets) g->gc_gen = g->mark.gen; + up_read(&ca->bucket_lock); } for (i = 0; i < BTREE_ID_NR; i++) @@ -944,14 +954,15 @@ int bch2_gc_gens(struct bch_fs *c) } for_each_member_device(ca, c, i) { - struct bucket_array *buckets = bucket_array(ca); - struct bucket *g; + down_read(&ca->bucket_lock); + buckets = bucket_array(ca); for_each_bucket(g, buckets) g->oldest_gen = g->gc_gen; + up_read(&ca->bucket_lock); } err: - up_read(&c->state_lock); + up_read(&c->gc_lock); return ret; } |