bcachefs: Trust in memory bucket mark
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 27 Aug 2019 21:34:03 +0000 (17:34 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:26 +0000 (17:08 -0400)
This fixes a bug in the journal replay -> extent_replay_key ->
split_compressed path, when we do an update that changes alloc info but
the alloc info in the btree isn't up to date yet.

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

index 5619dccdc011c2563c69f32b6e1dc9dc0623fe4d..c1158ce154c59ac2e8e92e6a889457b28abd7c5b 100644 (file)
@@ -205,20 +205,6 @@ void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c,
                               get_alloc_field(a.v, &d, i));
 }
 
-static inline struct bkey_alloc_unpacked
-alloc_mem_to_key(struct bucket *g, struct bucket_mark m)
-{
-       return (struct bkey_alloc_unpacked) {
-               .gen            = m.gen,
-               .oldest_gen     = g->oldest_gen,
-               .data_type      = m.data_type,
-               .dirty_sectors  = m.dirty_sectors,
-               .cached_sectors = m.cached_sectors,
-               .read_time      = g->io_time[READ],
-               .write_time     = g->io_time[WRITE],
-       };
-}
-
 int bch2_alloc_read(struct bch_fs *c, struct journal_keys *journal_keys)
 {
        struct btree_trans trans;
index 0c1a0f0dd2ab558835474ec18a65b9d21a596a00..134c6d81397c24a040a53ca4bf2772b32bc5db41 100644 (file)
@@ -17,6 +17,20 @@ struct bkey_alloc_unpacked bch2_alloc_unpack(struct bkey_s_c);
 void bch2_alloc_pack(struct bkey_i_alloc *,
                     const struct bkey_alloc_unpacked);
 
+static inline struct bkey_alloc_unpacked
+alloc_mem_to_key(struct bucket *g, struct bucket_mark m)
+{
+       return (struct bkey_alloc_unpacked) {
+               .gen            = m.gen,
+               .oldest_gen     = g->oldest_gen,
+               .data_type      = m.data_type,
+               .dirty_sectors  = m.dirty_sectors,
+               .cached_sectors = m.cached_sectors,
+               .read_time      = g->io_time[READ],
+               .write_time     = g->io_time[WRITE],
+       };
+}
+
 #define ALLOC_SCAN_BATCH(ca)           max_t(size_t, 1, (ca)->mi.nbuckets >> 9)
 
 const char *bch2_alloc_invalid(const struct bch_fs *, struct bkey_s_c);
index 78d43830d0a755a232eee559aa0ced516c356b14..4ab3b834948b630a0413ab0e9ce2c9c72c46cebf 100644 (file)
@@ -1361,7 +1361,7 @@ static int trans_get_key(struct btree_trans *trans,
                     : !bkey_cmp(pos, i->iter->pos))) {
                        *iter   = i->iter;
                        *k      = bkey_i_to_s_c(i->k);
-                       return 0;
+                       return 1;
                }
 
        *iter = __bch2_trans_get_iter(trans, btree_id, pos,
@@ -1424,18 +1424,37 @@ static int bch2_trans_mark_pointer(struct btree_trans *trans,
        ret = trans_get_key(trans, BTREE_ID_ALLOC,
                            POS(p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr)),
                            &iter, &k);
-       if (ret)
+       if (ret < 0)
                return ret;
 
-       if (k.k->type != KEY_TYPE_alloc) {
-               bch_err_ratelimited(c, "pointer to nonexistent bucket %u:%zu",
-                                   p.ptr.dev,
-                                   PTR_BUCKET_NR(ca, &p.ptr));
-               ret = -1;
-               goto out;
-       }
+       if (!ret) {
+               /*
+                * 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:
+                */
+               struct bucket *g;
+               struct bucket_mark m;
 
-       u = bch2_alloc_unpack(k);
+               percpu_down_read(&c->mark_lock);
+               g       = bucket(ca, iter->pos.offset);
+               m       = READ_ONCE(g->mark);
+               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) {
+                       bch_err_ratelimited(c, "pointer to nonexistent bucket %u:%zu",
+                                           p.ptr.dev,
+                                           PTR_BUCKET_NR(ca, &p.ptr));
+                       ret = -1;
+                       goto out;
+               }
+
+               u = bch2_alloc_unpack(k);
+       }
 
        if (gen_after(u.gen, p.ptr.gen)) {
                ret = 1;
@@ -1484,7 +1503,7 @@ static int bch2_trans_mark_stripe_ptr(struct btree_trans *trans,
        int ret = 0;
 
        ret = trans_get_key(trans, BTREE_ID_EC, POS(0, p.idx), &iter, &k);
-       if (ret)
+       if (ret < 0)
                return ret;
 
        if (k.k->type != KEY_TYPE_stripe) {
@@ -1599,7 +1618,7 @@ static int __bch2_trans_mark_reflink_p(struct btree_trans *trans,
 
        ret = trans_get_key(trans, BTREE_ID_REFLINK,
                            POS(0, idx), &iter, &k);
-       if (ret)
+       if (ret < 0)
                return ret;
 
        if (k.k->type != KEY_TYPE_reflink_v) {