From baeed3c3c028359c625fbe92bc5924edaea91ec4 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Thu, 28 May 2020 15:51:50 -0400 Subject: [PATCH] bcachefs: Don't require alloc btree to be updated before buckets are used This is to break a circular dependency in the shutdown path. Signed-off-by: Kent Overstreet Signed-off-by: Kent Overstreet --- fs/bcachefs/alloc_background.c | 11 ++++++-- fs/bcachefs/buckets.c | 47 ++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c index b3c5d82c15de0..38173f662d1ef 100644 --- a/fs/bcachefs/alloc_background.c +++ b/fs/bcachefs/alloc_background.c @@ -843,7 +843,7 @@ static int bch2_invalidate_one_bucket2(struct btree_trans *trans, struct bkey_s_c k; bool invalidating_cached_data; size_t b; - int ret; + int ret = 0; BUG_ON(!ca->alloc_heap.used || !ca->alloc_heap.data[0].nr); @@ -857,11 +857,18 @@ static int bch2_invalidate_one_bucket2(struct btree_trans *trans, BUG_ON(!fifo_push(&ca->free_inc, b)); + g = bucket(ca, b); + m = READ_ONCE(g->mark); + bch2_mark_alloc_bucket(c, ca, b, true, gc_pos_alloc(c, NULL), 0); spin_unlock(&c->freelist_lock); percpu_up_read(&c->mark_lock); + invalidating_cached_data = m.cached_sectors != 0; + if (!invalidating_cached_data) + goto out; + BUG_ON(BKEY_ALLOC_VAL_U64s_MAX > 8); bch2_btree_iter_set_pos(iter, POS(ca->dev_idx, b)); @@ -915,7 +922,7 @@ retry: flags); if (ret == -EINTR) goto retry; - +out: if (!ret) { /* remove from alloc_heap: */ struct alloc_heap_entry e, *top = ca->alloc_heap.data; diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c index 5b827698c3e50..ebdbdd049f50a 100644 --- a/fs/bcachefs/buckets.c +++ b/fs/bcachefs/buckets.c @@ -1457,11 +1457,13 @@ static int bch2_trans_mark_pointer(struct btree_trans *trans, if (ret < 0) return ret; - if (!ret && unlikely(!test_bit(BCH_FS_ALLOC_WRITTEN, &c->flags))) { + if (k.k->type != KEY_TYPE_alloc || + (!ret && unlikely(!test_bit(BCH_FS_ALLOC_WRITTEN, &c->flags)))) { /* * During journal replay, and if gc repairs alloc info at * runtime, the alloc info in the btree might not be up to date - * yet - so, trust the in memory mark: + * yet - so, trust the in memory mark - unless we're already + * updating that key: */ struct bucket *g; struct bucket_mark m; @@ -1472,22 +1474,39 @@ static int bch2_trans_mark_pointer(struct btree_trans *trans, u = alloc_mem_to_key(g, m); percpu_up_read(&c->mark_lock); } else { - /* - * Unless we're already updating that key: - */ - if (k.k->type != KEY_TYPE_alloc) { - bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK, - "pointer to nonexistent bucket %llu:%llu", - iter->pos.inode, iter->pos.offset); - ret = -1; - goto out; - } - u = bch2_alloc_unpack(k); } - if (gen_after(u.gen, p.ptr.gen)) { + if (u.gen != p.ptr.gen) { ret = 1; + + if (gen_after(p.ptr.gen, u.gen)) { + bch2_fs_inconsistent(c, + "bucket %llu:%llu gen %u data type %s: ptr gen %u newer than bucket gen", + iter->pos.inode, iter->pos.offset, u.gen, + bch2_data_types[u.data_type ?: data_type], + p.ptr.gen); + ret = -EIO; + } + + if (gen_cmp(u.gen, p.ptr.gen) >= 96U) { + bch2_fs_inconsistent(c, + "bucket %llu:%llu gen %u data type %s: ptr gen %u too stale", + iter->pos.inode, iter->pos.offset, u.gen, + bch2_data_types[u.data_type ?: data_type], + p.ptr.gen); + ret = -EIO; + } + + if (!p.ptr.cached) { + bch2_fs_inconsistent(c, + "bucket %llu:%llu gen %u data type %s: stale dirty ptr (gen %u)", + iter->pos.inode, iter->pos.offset, u.gen, + bch2_data_types[u.data_type ?: data_type], + p.ptr.gen); + ret = -EIO; + } + goto out; } -- 2.30.2