bcachefs: Don't require alloc btree to be updated before buckets are used
authorKent Overstreet <kent.overstreet@gmail.com>
Thu, 28 May 2020 19:51:50 +0000 (15:51 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:40 +0000 (17:08 -0400)
This is to break a circular dependency in the shutdown path.

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

index b3c5d82c15de0168ef301b4f6c2d49a586b3895c..38173f662d1efa3875e88915e1480569892c405f 100644 (file)
@@ -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;
index 5b827698c3e50ba10da5b842f5b694b122f8299b..ebdbdd049f50a9f8f4bc853518aef90eb50dcdba 100644 (file)
@@ -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;
        }