]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/btree_io.c
New upstream release
[bcachefs-tools-debian] / libbcachefs / btree_io.c
index 48f213fcf8930a64d8b468914a8551bcbd3604c4..c049876ee80be3cc4ca5e7813ddd42e1a9194490 100644 (file)
@@ -18,9 +18,9 @@
 #include "journal_reclaim.h"
 #include "journal_seq_blacklist.h"
 #include "super-io.h"
+#include "trace.h"
 
 #include <linux/sched/mm.h>
-#include <trace/events/bcachefs.h>
 
 void bch2_btree_node_io_unlock(struct btree *b)
 {
@@ -33,7 +33,7 @@ void bch2_btree_node_io_unlock(struct btree *b)
 
 void bch2_btree_node_io_lock(struct btree *b)
 {
-       BUG_ON(lock_class_is_held(&bch2_btree_node_lock_key));
+       bch2_assert_btree_nodes_not_locked();
 
        wait_on_bit_lock_io(&b->flags, BTREE_NODE_write_in_flight,
                            TASK_UNINTERRUPTIBLE);
@@ -53,7 +53,7 @@ void __bch2_btree_node_wait_on_write(struct btree *b)
 
 void bch2_btree_node_wait_on_read(struct btree *b)
 {
-       BUG_ON(lock_class_is_held(&bch2_btree_node_lock_key));
+       bch2_assert_btree_nodes_not_locked();
 
        wait_on_bit_io(&b->flags, BTREE_NODE_read_in_flight,
                       TASK_UNINTERRUPTIBLE);
@@ -61,7 +61,7 @@ void bch2_btree_node_wait_on_read(struct btree *b)
 
 void bch2_btree_node_wait_on_write(struct btree *b)
 {
-       BUG_ON(lock_class_is_held(&bch2_btree_node_lock_key));
+       bch2_assert_btree_nodes_not_locked();
 
        wait_on_bit_io(&b->flags, BTREE_NODE_write_in_flight,
                       TASK_UNINTERRUPTIBLE);
@@ -77,13 +77,13 @@ static void verify_no_dups(struct btree *b,
        if (start == end)
                return;
 
-       for (p = start, k = bkey_next(start);
+       for (p = start, k = bkey_p_next(start);
             k != end;
-            p = k, k = bkey_next(k)) {
+            p = k, k = bkey_p_next(k)) {
                struct bkey l = bkey_unpack_key(b, p);
                struct bkey r = bkey_unpack_key(b, k);
 
-               BUG_ON(bpos_cmp(l.p, bkey_start_pos(&r)) >= 0);
+               BUG_ON(bpos_ge(l.p, bkey_start_pos(&r)));
        }
 #endif
 }
@@ -92,7 +92,7 @@ static void set_needs_whiteout(struct bset *i, int v)
 {
        struct bkey_packed *k;
 
-       for (k = i->start; k != vstruct_last(i); k = bkey_next(k))
+       for (k = i->start; k != vstruct_last(i); k = bkey_p_next(k))
                k->needs_whiteout = v;
 }
 
@@ -105,8 +105,8 @@ static void btree_bounce_free(struct bch_fs *c, size_t size,
                vpfree(p, size);
 }
 
-static void *btree_bounce_alloc(struct bch_fs *c, size_t size,
-                               bool *used_mempool)
+static void *btree_bounce_alloc_noprof(struct bch_fs *c, size_t size,
+                                      bool *used_mempool)
 {
        unsigned flags = memalloc_nofs_save();
        void *p;
@@ -114,14 +114,16 @@ static void *btree_bounce_alloc(struct bch_fs *c, size_t size,
        BUG_ON(size > btree_bytes(c));
 
        *used_mempool = false;
-       p = vpmalloc(size, __GFP_NOWARN|GFP_NOWAIT);
+       p = vpmalloc_noprof(size, __GFP_NOWARN|GFP_NOWAIT);
        if (!p) {
                *used_mempool = true;
-               p = mempool_alloc(&c->btree_bounce_pool, GFP_NOIO);
+               p = mempool_alloc(&c->btree_bounce_pool, GFP_NOFS);
        }
        memalloc_nofs_restore(flags);
        return p;
 }
+#define btree_bounce_alloc(_c, _size, _used_mempool)           \
+       alloc_hooks(btree_bounce_alloc_noprof(_c, _size, _used_mempool))
 
 static void sort_bkey_ptrs(const struct btree *bt,
                           struct bkey_packed **ptrs, unsigned nr)
@@ -175,7 +177,7 @@ static void bch2_sort_whiteouts(struct bch_fs *c, struct btree *b)
 
        for (k = unwritten_whiteouts_start(c, b);
             k != unwritten_whiteouts_end(c, b);
-            k = bkey_next(k))
+            k = bkey_p_next(k))
                *--ptrs = k;
 
        sort_bkey_ptrs(b, ptrs, ptrs_end - ptrs);
@@ -184,7 +186,7 @@ static void bch2_sort_whiteouts(struct bch_fs *c, struct btree *b)
 
        while (ptrs != ptrs_end) {
                bkey_copy(k, *ptrs);
-               k = bkey_next(k);
+               k = bkey_p_next(k);
                ptrs++;
        }
 
@@ -256,11 +258,11 @@ static bool bch2_drop_whiteouts(struct btree *b, enum compact_mode mode)
                out = i->start;
 
                for (k = start; k != end; k = n) {
-                       n = bkey_next(k);
+                       n = bkey_p_next(k);
 
                        if (!bkey_deleted(k)) {
                                bkey_copy(out, k);
-                               out = bkey_next(out);
+                               out = bkey_p_next(out);
                        } else {
                                BUG_ON(k->needs_whiteout);
                        }
@@ -483,7 +485,7 @@ void bch2_btree_init_next(struct btree_trans *trans, struct btree *b)
        struct btree_node_entry *bne;
        bool reinit_iter = false;
 
-       EBUG_ON(!(b->c.lock.state.seq & 1));
+       EBUG_ON(!six_lock_counts(&b->c.lock).n[SIX_LOCK_write]);
        BUG_ON(bset_written(b, bset(b, &b->set[1])));
        BUG_ON(btree_node_just_written(b));
 
@@ -517,7 +519,7 @@ static void btree_pos_to_text(struct printbuf *out, struct bch_fs *c,
        prt_printf(out, "%s level %u/%u\n  ",
               bch2_btree_ids[b->c.btree_id],
               b->c.level,
-              c->btree_roots[b->c.btree_id].level);
+              bch2_btree_id_root(c, b->c.btree_id)->level);
        bch2_bkey_val_to_text(out, c, bkey_i_to_s_c(&b->key));
 }
 
@@ -526,11 +528,10 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
                          struct btree *b, struct bset *i,
                          unsigned offset, int write)
 {
-       prt_printf(out, bch2_log_msg(c, ""));
-       if (!write)
-               prt_str(out, "error validating btree node ");
-       else
-               prt_str(out, "corrupt btree node before write ");
+       prt_printf(out, bch2_log_msg(c, "%s"),
+                  write == READ
+                  ? "error validating btree node "
+                  : "corrupt btree node before write ");
        if (ca)
                prt_printf(out, "on %s ", ca->name);
        prt_printf(out, "at btree ");
@@ -543,63 +544,96 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
 }
 
 enum btree_err_type {
+       /*
+        * We can repair this locally, and we're after the checksum check so
+        * there's no need to try another replica:
+        */
        BTREE_ERR_FIXABLE,
+       /*
+        * We can repair this if we have to, but we should try reading another
+        * replica if we can:
+        */
        BTREE_ERR_WANT_RETRY,
+       /*
+        * Read another replica if we have one, otherwise consider the whole
+        * node bad:
+        */
        BTREE_ERR_MUST_RETRY,
-       BTREE_ERR_FATAL,
+       BTREE_ERR_BAD_NODE,
+       BTREE_ERR_INCOMPATIBLE,
 };
 
 enum btree_validate_ret {
        BTREE_RETRY_READ = 64,
 };
 
+static int __btree_err(enum btree_err_type type,
+                      struct bch_fs *c,
+                      struct bch_dev *ca,
+                      struct btree *b,
+                      struct bset *i,
+                      int write,
+                      bool have_retry,
+                      const char *fmt, ...)
+{
+       struct printbuf out = PRINTBUF;
+       va_list args;
+       int ret = -BCH_ERR_fsck_fix;
+
+       btree_err_msg(&out, c, ca, b, i, b->written, write);
+
+       va_start(args, fmt);
+       prt_vprintf(&out, fmt, args);
+       va_end(args);
+
+       if (write == WRITE) {
+               bch2_print_string_as_lines(KERN_ERR, out.buf);
+               ret = c->opts.errors == BCH_ON_ERROR_continue
+                       ? 0
+                       : -BCH_ERR_fsck_errors_not_fixed;
+               goto out;
+       }
+
+       if (!have_retry && type == BTREE_ERR_WANT_RETRY)
+               type = BTREE_ERR_FIXABLE;
+       if (!have_retry && type == BTREE_ERR_MUST_RETRY)
+               type = BTREE_ERR_BAD_NODE;
+
+       switch (type) {
+       case BTREE_ERR_FIXABLE:
+               mustfix_fsck_err(c, "%s", out.buf);
+               ret = -BCH_ERR_fsck_fix;
+               break;
+       case BTREE_ERR_WANT_RETRY:
+       case BTREE_ERR_MUST_RETRY:
+               bch2_print_string_as_lines(KERN_ERR, out.buf);
+               ret = BTREE_RETRY_READ;
+               break;
+       case BTREE_ERR_BAD_NODE:
+               bch2_print_string_as_lines(KERN_ERR, out.buf);
+               bch2_topology_error(c);
+               ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_check_topology) ?: -EIO;
+               break;
+       case BTREE_ERR_INCOMPATIBLE:
+               bch2_print_string_as_lines(KERN_ERR, out.buf);
+               ret = -BCH_ERR_fsck_errors_not_fixed;
+               break;
+       default:
+               BUG();
+       }
+out:
+fsck_err:
+       printbuf_exit(&out);
+       return ret;
+}
+
 #define btree_err(type, c, ca, b, i, msg, ...)                         \
 ({                                                                     \
-       __label__ out;                                                  \
-       struct printbuf out = PRINTBUF;                                 \
-                                                                       \
-       btree_err_msg(&out, c, ca, b, i, b->written, write);            \
-       prt_printf(&out, msg, ##__VA_ARGS__);                           \
+       int _ret = __btree_err(type, c, ca, b, i, write, have_retry, msg, ##__VA_ARGS__);\
                                                                        \
-       if (type == BTREE_ERR_FIXABLE &&                                \
-           write == READ &&                                            \
-           !test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags)) {             \
-               mustfix_fsck_err(c, "%s", out.buf);                     \
-               goto out;                                               \
-       }                                                               \
-                                                                       \
-       bch2_print_string_as_lines(KERN_ERR, out.buf);                  \
-                                                                       \
-       switch (write) {                                                \
-       case READ:                                                      \
-               switch (type) {                                         \
-               case BTREE_ERR_FIXABLE:                                 \
-                       ret = -BCH_ERR_fsck_errors_not_fixed;           \
-                       goto fsck_err;                                  \
-               case BTREE_ERR_WANT_RETRY:                              \
-                       if (have_retry) {                               \
-                               ret = BTREE_RETRY_READ;                 \
-                               goto fsck_err;                          \
-                       }                                               \
-                       break;                                          \
-               case BTREE_ERR_MUST_RETRY:                              \
-                       ret = BTREE_RETRY_READ;                         \
-                       goto fsck_err;                                  \
-               case BTREE_ERR_FATAL:                                   \
-                       ret = -BCH_ERR_fsck_errors_not_fixed;           \
-                       goto fsck_err;                                  \
-               }                                                       \
-               break;                                                  \
-       case WRITE:                                                     \
-               if (bch2_fs_inconsistent(c)) {                          \
-                       ret = -BCH_ERR_fsck_errors_not_fixed;           \
-                       goto fsck_err;                                  \
-               }                                                       \
-               break;                                                  \
-       }                                                               \
-out:                                                                   \
-       printbuf_exit(&out);                                            \
-       true;                                                           \
+       if (_ret != -BCH_ERR_fsck_fix)                                  \
+               goto fsck_err;                                          \
+       *saw_error = true;                                              \
 })
 
 #define btree_err_on(cond, ...)        ((cond) ? btree_err(__VA_ARGS__) : false)
@@ -608,6 +642,7 @@ out:                                                                        \
  * When btree topology repair changes the start or end of a node, that might
  * mean we have to drop keys that are no longer inside the node:
  */
+__cold
 void bch2_btree_node_drop_keys_outside_node(struct btree *b)
 {
        struct bset_tree *t;
@@ -619,7 +654,7 @@ void bch2_btree_node_drop_keys_outside_node(struct btree *b)
                struct bset *i = bset(b, t);
                struct bkey_packed *k;
 
-               for (k = i->start; k != vstruct_last(i); k = bkey_next(k))
+               for (k = i->start; k != vstruct_last(i); k = bkey_p_next(k))
                        if (bkey_cmp_left_packed(b, k, &b->data->min_key) >= 0)
                                break;
 
@@ -632,7 +667,7 @@ void bch2_btree_node_drop_keys_outside_node(struct btree *b)
                        set_btree_bset_end(b, t);
                }
 
-               for (k = i->start; k != vstruct_last(i); k = bkey_next(k))
+               for (k = i->start; k != vstruct_last(i); k = bkey_p_next(k))
                        if (bkey_cmp_left_packed(b, k, &b->data->max_key) > 0)
                                break;
 
@@ -650,15 +685,15 @@ void bch2_btree_node_drop_keys_outside_node(struct btree *b)
        bch2_btree_build_aux_trees(b);
 
        for_each_btree_node_key_unpack(b, k, &iter, &unpacked) {
-               BUG_ON(bpos_cmp(k.k->p, b->data->min_key) < 0);
-               BUG_ON(bpos_cmp(k.k->p, b->data->max_key) > 0);
+               BUG_ON(bpos_lt(k.k->p, b->data->min_key));
+               BUG_ON(bpos_gt(k.k->p, b->data->max_key));
        }
 }
 
 static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                         struct btree *b, struct bset *i,
                         unsigned offset, unsigned sectors,
-                        int write, bool have_retry)
+                        int write, bool have_retry, bool *saw_error)
 {
        unsigned version = le16_to_cpu(i->version);
        const char *err;
@@ -666,11 +701,11 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
        struct printbuf buf2 = PRINTBUF;
        int ret = 0;
 
-       btree_err_on((version != BCH_BSET_VERSION_OLD &&
-                     version < bcachefs_metadata_version_min) ||
-                    version >= bcachefs_metadata_version_max,
-                    BTREE_ERR_FATAL, c, ca, b, i,
-                    "unsupported bset version");
+       btree_err_on(!bch2_version_compatible(version),
+                    BTREE_ERR_INCOMPATIBLE, c, ca, b, i,
+                    "unsupported bset version %u.%u",
+                    BCH_VERSION_MAJOR(version),
+                    BCH_VERSION_MINOR(version));
 
        if (btree_err_on(version < c->sb.version_min,
                         BTREE_ERR_FIXABLE, c, NULL, b, i,
@@ -682,7 +717,8 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                mutex_unlock(&c->sb_lock);
        }
 
-       if (btree_err_on(version > c->sb.version,
+       if (btree_err_on(BCH_VERSION_MAJOR(version) >
+                        BCH_VERSION_MAJOR(c->sb.version),
                         BTREE_ERR_FIXABLE, c, NULL, b, i,
                         "bset version %u newer than superblock version %u",
                         version, c->sb.version)) {
@@ -693,7 +729,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
        }
 
        btree_err_on(BSET_SEPARATE_WHITEOUTS(i),
-                    BTREE_ERR_FATAL, c, ca, b, i,
+                    BTREE_ERR_INCOMPATIBLE, c, ca, b, i,
                     "BSET_SEPARATE_WHITEOUTS no longer supported");
 
        if (btree_err_on(offset + sectors > btree_sectors(c),
@@ -749,7 +785,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                                b->data->max_key = b->key.k.p;
                        }
 
-                       btree_err_on(bpos_cmp(b->data->min_key, bp->min_key),
+                       btree_err_on(!bpos_eq(b->data->min_key, bp->min_key),
                                     BTREE_ERR_MUST_RETRY, c, ca, b, NULL,
                                     "incorrect min_key: got %s should be %s",
                                     (printbuf_reset(&buf1),
@@ -758,7 +794,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                                      bch2_bpos_to_text(&buf2, bp->min_key), buf2.buf));
                }
 
-               btree_err_on(bpos_cmp(bn->max_key, b->key.k.p),
+               btree_err_on(!bpos_eq(bn->max_key, b->key.k.p),
                             BTREE_ERR_MUST_RETRY, c, ca, b, i,
                             "incorrect max key %s",
                             (printbuf_reset(&buf1),
@@ -770,7 +806,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
 
                err = bch2_bkey_format_validate(&bn->format);
                btree_err_on(err,
-                            BTREE_ERR_FATAL, c, ca, b, i,
+                            BTREE_ERR_BAD_NODE, c, ca, b, i,
                             "invalid bkey format: %s", err);
 
                compat_bformat(b->c.level, b->c.btree_id, version,
@@ -795,7 +831,8 @@ static int bset_key_invalid(struct bch_fs *c, struct btree *b,
 }
 
 static int validate_bset_keys(struct bch_fs *c, struct btree *b,
-                        struct bset *i, int write, bool have_retry)
+                        struct bset *i, int write,
+                        bool have_retry, bool *saw_error)
 {
        unsigned version = le16_to_cpu(i->version);
        struct bkey_packed *k, *prev = NULL;
@@ -809,7 +846,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
                struct bkey_s u;
                struct bkey tmp;
 
-               if (btree_err_on(bkey_next(k) > vstruct_last(i),
+               if (btree_err_on(bkey_p_next(k) > vstruct_last(i),
                                 BTREE_ERR_FIXABLE, c, NULL, b, i,
                                 "key extends past end of bset")) {
                        i->u64s = cpu_to_le16((u64 *) k - i->_data);
@@ -820,7 +857,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
                                 BTREE_ERR_FIXABLE, c, NULL, b, i,
                                 "invalid bkey format %u", k->format)) {
                        i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
-                       memmove_u64s_down(k, bkey_next(k),
+                       memmove_u64s_down(k, bkey_p_next(k),
                                          (u64 *) vstruct_end(i) - (u64 *) k);
                        continue;
                }
@@ -844,7 +881,7 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
                        btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, "%s", buf.buf);
 
                        i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
-                       memmove_u64s_down(k, bkey_next(k),
+                       memmove_u64s_down(k, bkey_p_next(k),
                                          (u64 *) vstruct_end(i) - (u64 *) k);
                        continue;
                }
@@ -867,14 +904,14 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
 
                        if (btree_err(BTREE_ERR_FIXABLE, c, NULL, b, i, "%s", buf.buf)) {
                                i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
-                               memmove_u64s_down(k, bkey_next(k),
+                               memmove_u64s_down(k, bkey_p_next(k),
                                                  (u64 *) vstruct_end(i) - (u64 *) k);
                                continue;
                        }
                }
 
                prev = k;
-               k = bkey_next(k);
+               k = bkey_p_next(k);
        }
 fsck_err:
        printbuf_exit(&buf);
@@ -882,7 +919,7 @@ fsck_err:
 }
 
 int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
-                             struct btree *b, bool have_retry)
+                             struct btree *b, bool have_retry, bool *saw_error)
 {
        struct btree_node_entry *bne;
        struct sort_iter *iter;
@@ -897,13 +934,13 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
        unsigned blacklisted_written, nonblacklisted_written = 0;
        unsigned ptr_written = btree_ptr_sectors_written(&b->key);
        struct printbuf buf = PRINTBUF;
-       int ret, retry_read = 0, write = READ;
+       int ret = 0, retry_read = 0, write = READ;
 
        b->version_ondisk = U16_MAX;
        /* We might get called multiple times on read retry: */
        b->written = 0;
 
-       iter = mempool_alloc(&c->fill_iter, GFP_NOIO);
+       iter = mempool_alloc(&c->fill_iter, GFP_NOFS);
        sort_iter_init(iter, b);
        iter->size = (btree_blocks(c) + 1) * 2;
 
@@ -958,7 +995,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
 
                        btree_err_on(btree_node_type_is_extents(btree_node_type(b)) &&
                                     !BTREE_NODE_NEW_EXTENT_OVERWRITE(b->data),
-                                    BTREE_ERR_FATAL, c, NULL, b, NULL,
+                                    BTREE_ERR_INCOMPATIBLE, c, NULL, b, NULL,
                                     "btree node does not have NEW_EXTENT_OVERWRITE set");
 
                        sectors = vstruct_sectors(b->data, c->block_bits);
@@ -993,14 +1030,14 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                        le16_to_cpu(i->version));
 
                ret = validate_bset(c, ca, b, i, b->written, sectors,
-                                   READ, have_retry);
+                                   READ, have_retry, saw_error);
                if (ret)
                        goto fsck_err;
 
                if (!b->written)
                        btree_node_set_format(b, b->data->format);
 
-               ret = validate_bset_keys(c, b, i, READ, have_retry);
+               ret = validate_bset_keys(c, b, i, READ, have_retry, saw_error);
                if (ret)
                        goto fsck_err;
 
@@ -1105,7 +1142,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                        btree_keys_account_key_drop(&b->nr, 0, k);
 
                        i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
-                       memmove_u64s_down(k, bkey_next(k),
+                       memmove_u64s_down(k, bkey_p_next(k),
                                          (u64 *) vstruct_end(i) - (u64 *) k);
                        set_btree_bset_end(b, b->set);
                        continue;
@@ -1117,7 +1154,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                        bp.v->mem_ptr = 0;
                }
 
-               k = bkey_next(k);
+               k = bkey_p_next(k);
        }
 
        bch2_bset_build_aux_tree(b, b->set, false);
@@ -1140,12 +1177,10 @@ out:
        printbuf_exit(&buf);
        return retry_read;
 fsck_err:
-       if (ret == BTREE_RETRY_READ) {
+       if (ret == BTREE_RETRY_READ)
                retry_read = 1;
-       } else {
-               bch2_inconsistent_error(c);
+       else
                set_btree_node_read_error(b);
-       }
        goto out;
 }
 
@@ -1195,7 +1230,7 @@ start:
                                &failed, &rb->pick) > 0;
 
                if (!bio->bi_status &&
-                   !bch2_btree_node_read_done(c, ca, b, can_retry)) {
+                   !bch2_btree_node_read_done(c, ca, b, can_retry, &saw_error)) {
                        if (retry)
                                bch_info(c, "retry success");
                        break;
@@ -1214,8 +1249,16 @@ start:
        bio_put(&rb->bio);
        printbuf_exit(&buf);
 
-       if (saw_error && !btree_node_read_error(b))
+       if (saw_error && !btree_node_read_error(b)) {
+               struct printbuf buf = PRINTBUF;
+
+               bch2_bpos_to_text(&buf, b->key.k.p);
+               bch_info(c, "%s: rewriting btree node at btree=%s level=%u %s due to error",
+                        __func__, bch2_btree_ids[b->c.btree_id], b->c.level, buf.buf);
+               printbuf_exit(&buf);
+
                bch2_btree_node_rewrite_async(c, b);
+       }
 
        clear_btree_node_read_in_flight(b);
        wake_up_bit(&b->flags, BTREE_NODE_read_in_flight);
@@ -1243,7 +1286,7 @@ struct btree_node_read_all {
        unsigned                nr;
        void                    *buf[BCH_REPLICAS_MAX];
        struct bio              *bio[BCH_REPLICAS_MAX];
-       int                     err[BCH_REPLICAS_MAX];
+       blk_status_t            err[BCH_REPLICAS_MAX];
 };
 
 static unsigned btree_node_sectors_written(struct bch_fs *c, void *data)
@@ -1301,6 +1344,7 @@ static void btree_node_read_all_replicas_done(struct closure *cl)
        unsigned i, written = 0, written2 = 0;
        __le64 seq = b->key.k.type == KEY_TYPE_btree_ptr_v2
                ? bkey_i_to_btree_ptr_v2(&b->key)->v.seq : 0;
+       bool _saw_error = false, *saw_error = &_saw_error;
 
        for (i = 0; i < ra->nr; i++) {
                struct btree_node *bn = ra->buf[i];
@@ -1387,13 +1431,15 @@ fsck_err:
 
        if (best >= 0) {
                memcpy(b->data, ra->buf[best], btree_bytes(c));
-               ret = bch2_btree_node_read_done(c, NULL, b, false);
+               ret = bch2_btree_node_read_done(c, NULL, b, false, saw_error);
        } else {
                ret = -1;
        }
 
        if (ret)
                set_btree_node_read_error(b);
+       else if (*saw_error)
+               bch2_btree_node_rewrite_async(c, b);
 
        for (i = 0; i < ra->nr; i++) {
                mempool_free(ra->buf[i], &c->btree_bounce_pool);
@@ -1440,7 +1486,7 @@ static int btree_node_read_all_replicas(struct bch_fs *c, struct btree *b, bool
 
        ra = kzalloc(sizeof(*ra), GFP_NOFS);
        if (!ra)
-               return -ENOMEM;
+               return -BCH_ERR_ENOMEM_btree_node_read_all_replicas;
 
        closure_init(&ra->cl, NULL);
        ra->c   = c;
@@ -1522,7 +1568,8 @@ void bch2_btree_node_read(struct bch_fs *c, struct btree *b,
                btree_pos_to_text(&buf, c, b);
                bch_err(c, "%s", buf.buf);
 
-               if (test_bit(BCH_FS_TOPOLOGY_REPAIR_DONE, &c->flags))
+               if (c->recovery_passes_explicit & BIT_ULL(BCH_RECOVERY_PASS_check_topology) &&
+                   c->curr_recovery_pass > BCH_RECOVERY_PASS_check_topology)
                        bch2_fatal_error(c);
 
                set_btree_node_read_error(b);
@@ -1537,7 +1584,7 @@ void bch2_btree_node_read(struct bch_fs *c, struct btree *b,
        bio = bio_alloc_bioset(NULL,
                               buf_pages(b->data, btree_bytes(c)),
                               REQ_OP_READ|REQ_SYNC|REQ_META,
-                              GFP_NOIO,
+                              GFP_NOFS,
                               &c->btree_bio);
        rb = container_of(bio, struct btree_read_bio, bio);
        rb->c                   = c;
@@ -1573,9 +1620,10 @@ void bch2_btree_node_read(struct bch_fs *c, struct btree *b,
        }
 }
 
-int bch2_btree_root_read(struct bch_fs *c, enum btree_id id,
-                       const struct bkey_i *k, unsigned level)
+static int __bch2_btree_root_read(struct btree_trans *trans, enum btree_id id,
+                                 const struct bkey_i *k, unsigned level)
 {
+       struct bch_fs *c = trans->c;
        struct closure cl;
        struct btree *b;
        int ret;
@@ -1587,7 +1635,7 @@ int bch2_btree_root_read(struct bch_fs *c, enum btree_id id,
                closure_sync(&cl);
        } while (ret);
 
-       b = bch2_btree_node_mem_alloc(c, level != 0);
+       b = bch2_btree_node_mem_alloc(trans, level != 0);
        bch2_btree_cache_cannibalize_unlock(c);
 
        BUG_ON(IS_ERR(b));
@@ -1618,6 +1666,13 @@ err:
        return ret;
 }
 
+int bch2_btree_root_read(struct bch_fs *c, enum btree_id id,
+                       const struct bkey_i *k, unsigned level)
+{
+       return bch2_trans_run(c, __bch2_btree_root_read(&trans, id, k, level));
+
+}
+
 void bch2_btree_complete_write(struct bch_fs *c, struct btree *b,
                              struct btree_write *w)
 {
@@ -1695,7 +1750,7 @@ static void btree_node_write_work(struct work_struct *work)
        struct bch_fs *c        = wbio->wbio.c;
        struct btree *b         = wbio->wbio.bio.bi_private;
        struct bch_extent_ptr *ptr;
-       int ret;
+       int ret = 0;
 
        btree_bounce_free(c,
                wbio->data_bytes,
@@ -1715,7 +1770,11 @@ static void btree_node_write_work(struct work_struct *work)
        } else {
                ret = bch2_trans_do(c, NULL, NULL, 0,
                        bch2_btree_node_update_key_get_iter(&trans, b, &wbio->key,
-                                                           !wbio->wbio.failed.nr));
+                                       BCH_WATERMARK_reclaim|
+                                       BTREE_INSERT_JOURNAL_RECLAIM|
+                                       BTREE_INSERT_NOFAIL|
+                                       BTREE_INSERT_NOCHECK_RW,
+                                       !wbio->wbio.failed.nr));
                if (ret)
                        goto err;
        }
@@ -1725,7 +1784,8 @@ out:
        return;
 err:
        set_btree_node_noevict(b);
-       bch2_fs_fatal_error(c, "fatal error writing btree node");
+       if (!bch2_err_matches(ret, EROFS))
+               bch2_fs_fatal_error(c, "fatal error writing btree node: %s", bch2_err_str(ret));
        goto out;
 }
 
@@ -1770,6 +1830,7 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b,
                                   struct bset *i, unsigned sectors)
 {
        struct printbuf buf = PRINTBUF;
+       bool saw_error;
        int ret;
 
        ret = bch2_bkey_invalid(c, bkey_i_to_s_c(&b->key),
@@ -1781,8 +1842,8 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b,
        if (ret)
                return ret;
 
-       ret = validate_bset_keys(c, b, i, WRITE, false) ?:
-               validate_bset(c, NULL, b, i, b->written, sectors, WRITE, false);
+       ret = validate_bset_keys(c, b, i, WRITE, false, &saw_error) ?:
+               validate_bset(c, NULL, b, i, b->written, sectors, WRITE, false, &saw_error);
        if (ret) {
                bch2_inconsistent_error(c);
                dump_stack();
@@ -1795,7 +1856,7 @@ static void btree_write_submit(struct work_struct *work)
 {
        struct btree_write_bio *wbio = container_of(work, struct btree_write_bio, work);
        struct bch_extent_ptr *ptr;
-       __BKEY_PADDED(k, BKEY_BTREE_PTR_VAL_U64s_MAX) tmp;
+       BKEY_PADDED_ONSTACK(k, BKEY_BTREE_PTR_VAL_U64s_MAX) tmp;
 
        bkey_copy(&tmp.k, &wbio->key);
 
@@ -1962,9 +2023,7 @@ do_write:
        BUG_ON(BSET_BIG_ENDIAN(i) != CPU_BIG_ENDIAN);
        BUG_ON(i->seq != b->data->keys.seq);
 
-       i->version = c->sb.version < bcachefs_metadata_version_bkey_renumber
-               ? cpu_to_le16(BCH_BSET_VERSION_OLD)
-               : cpu_to_le16(c->sb.version);
+       i->version = cpu_to_le16(c->sb.version);
        SET_BSET_OFFSET(i, b->written);
        SET_BSET_CSUM_TYPE(i, bch2_meta_checksum_type(c));
 
@@ -2024,7 +2083,7 @@ do_write:
        wbio = container_of(bio_alloc_bioset(NULL,
                                buf_pages(data, sectors_to_write << 9),
                                REQ_OP_WRITE|REQ_META,
-                               GFP_NOIO,
+                               GFP_NOFS,
                                &c->btree_bio),
                            struct btree_write_bio, wbio.bio);
        wbio_init(&wbio->wbio.bio);
@@ -2177,7 +2236,7 @@ bool bch2_btree_flush_all_writes(struct bch_fs *c)
        return __bch2_btree_flush_all(c, BTREE_NODE_write_in_flight);
 }
 
-const char * const bch2_btree_write_types[] = {
+static const char * const bch2_btree_write_types[] = {
 #define x(t, n) [n] = #t,
        BCH_BTREE_WRITE_TYPES()
        NULL