]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/btree_update_interior.c
Update bcachefs sources to 5d0a6c2b32f1 bcachefs: check_directory_structure() can...
[bcachefs-tools-debian] / libbcachefs / btree_update_interior.c
index 9affcb22d9cb7025453cfd528f13738ff9514879..99e9902fc1e08496af33913dd70625559ce142c3 100644 (file)
 #include <linux/random.h>
 
 static int bch2_btree_insert_node(struct btree_update *, struct btree_trans *,
-                                 struct btree_path *, struct btree *,
+                                 btree_path_idx_t, struct btree *,
                                  struct keylist *, unsigned);
 static void bch2_btree_update_add_new_node(struct btree_update *, struct btree *);
 
-static struct btree_path *get_unlocked_mut_path(struct btree_trans *trans,
-                                               enum btree_id btree_id,
-                                               unsigned level,
-                                               struct bpos pos)
+static btree_path_idx_t get_unlocked_mut_path(struct btree_trans *trans,
+                                             enum btree_id btree_id,
+                                             unsigned level,
+                                             struct bpos pos)
 {
-       struct btree_path *path;
-
-       path = bch2_path_get(trans, btree_id, pos, level + 1, level,
+       btree_path_idx_t path_idx = bch2_path_get(trans, btree_id, pos, level + 1, level,
                             BTREE_ITER_NOPRESERVE|
                             BTREE_ITER_INTENT, _RET_IP_);
-       path = bch2_btree_path_make_mut(trans, path, true, _RET_IP_);
+       path_idx = bch2_btree_path_make_mut(trans, path_idx, true, _RET_IP_);
+
+       struct btree_path *path = trans->paths + path_idx;
        bch2_btree_path_downgrade(trans, path);
        __bch2_btree_path_unlock(trans, path);
-       return path;
+       return path_idx;
 }
 
 /* Debug code: */
@@ -99,7 +99,7 @@ static void btree_node_interior_verify(struct bch_fs *c, struct btree *b)
 
 /* Calculate ideal packed bkey format for new btree nodes: */
 
-void __bch2_btree_calc_format(struct bkey_format_state *s, struct btree *b)
+static void __bch2_btree_calc_format(struct bkey_format_state *s, struct btree *b)
 {
        struct bkey_packed *k;
        struct bset_tree *t;
@@ -125,21 +125,20 @@ static struct bkey_format bch2_btree_calc_format(struct btree *b)
        return bch2_bkey_format_done(&s);
 }
 
-static size_t btree_node_u64s_with_format(struct btree *b,
+static size_t btree_node_u64s_with_format(struct btree_nr_keys nr,
+                                         struct bkey_format *old_f,
                                          struct bkey_format *new_f)
 {
-       struct bkey_format *old_f = &b->format;
-
        /* stupid integer promotion rules */
        ssize_t delta =
            (((int) new_f->key_u64s - old_f->key_u64s) *
-            (int) b->nr.packed_keys) +
+            (int) nr.packed_keys) +
            (((int) new_f->key_u64s - BKEY_U64s) *
-            (int) b->nr.unpacked_keys);
+            (int) nr.unpacked_keys);
 
-       BUG_ON(delta + b->nr.live_u64s < 0);
+       BUG_ON(delta + nr.live_u64s < 0);
 
-       return b->nr.live_u64s + delta;
+       return nr.live_u64s + delta;
 }
 
 /**
@@ -147,25 +146,29 @@ static size_t btree_node_u64s_with_format(struct btree *b,
  *
  * @c:         filesystem handle
  * @b:         btree node to rewrite
+ * @nr:                number of keys for new node (i.e. b->nr)
  * @new_f:     bkey format to translate keys to
  *
  * Returns: true if all re-packed keys will be able to fit in a new node.
  *
  * Assumes all keys will successfully pack with the new format.
  */
-bool bch2_btree_node_format_fits(struct bch_fs *c, struct btree *b,
+static bool bch2_btree_node_format_fits(struct bch_fs *c, struct btree *b,
+                                struct btree_nr_keys nr,
                                 struct bkey_format *new_f)
 {
-       size_t u64s = btree_node_u64s_with_format(b, new_f);
+       size_t u64s = btree_node_u64s_with_format(nr, &b->format, new_f);
 
        return __vstruct_bytes(struct btree_node, u64s) < btree_bytes(c);
 }
 
 /* Btree node freeing/allocation: */
 
-static void __btree_node_free(struct bch_fs *c, struct btree *b)
+static void __btree_node_free(struct btree_trans *trans, struct btree *b)
 {
-       trace_and_count(c, btree_node_free, c, b);
+       struct bch_fs *c = trans->c;
+
+       trace_and_count(c, btree_node_free, trans, b);
 
        BUG_ON(btree_node_write_blocked(b));
        BUG_ON(btree_node_dirty(b));
@@ -187,15 +190,15 @@ static void bch2_btree_node_free_inmem(struct btree_trans *trans,
                                       struct btree *b)
 {
        struct bch_fs *c = trans->c;
-       unsigned level = b->c.level;
+       unsigned i, level = b->c.level;
 
        bch2_btree_node_lock_write_nofail(trans, path, &b->c);
        bch2_btree_node_hash_remove(&c->btree_cache, b);
-       __btree_node_free(c, b);
+       __btree_node_free(trans, b);
        six_unlock_write(&b->c.lock);
        mark_btree_node_locked_noreset(path, level, BTREE_NODE_INTENT_LOCKED);
 
-       trans_for_each_path(trans, path)
+       trans_for_each_path(trans, path, i)
                if (path->l[level].b == b) {
                        btree_node_unlock(trans, path, level);
                        path->l[level].b = ERR_PTR(-BCH_ERR_no_btree_node_init);
@@ -209,7 +212,7 @@ static void bch2_btree_node_free_never_used(struct btree_update *as,
        struct bch_fs *c = as->c;
        struct prealloc_nodes *p = &as->prealloc_nodes[b->c.lock.readers != NULL];
        struct btree_path *path;
-       unsigned level = b->c.level;
+       unsigned i, level = b->c.level;
 
        BUG_ON(!list_empty(&b->write_blocked));
        BUG_ON(b->will_make_reachable != (1UL|(unsigned long) as));
@@ -232,7 +235,7 @@ static void bch2_btree_node_free_never_used(struct btree_update *as,
 
        six_unlock_intent(&b->c.lock);
 
-       trans_for_each_path(trans, path)
+       trans_for_each_path(trans, path, i)
                if (path->l[level].b == b) {
                        btree_node_unlock(trans, path, level);
                        path->l[level].b = ERR_PTR(-BCH_ERR_no_btree_node_init);
@@ -362,7 +365,7 @@ static struct btree *bch2_btree_node_alloc(struct btree_update *as,
        ret = bch2_btree_node_hash_insert(&c->btree_cache, b, level, as->btree_id);
        BUG_ON(ret);
 
-       trace_and_count(c, btree_node_alloc, c, b);
+       trace_and_count(c, btree_node_alloc, trans, b);
        bch2_increment_clock(c, btree_sectors(c), WRITE);
        return b;
 }
@@ -391,7 +394,7 @@ static struct btree *bch2_btree_node_alloc_replacement(struct btree_update *as,
         * The keys might expand with the new format - if they wouldn't fit in
         * the btree node anymore, use the old format for now:
         */
-       if (!bch2_btree_node_format_fits(as->c, b, &format))
+       if (!bch2_btree_node_format_fits(as->c, b, b->nr, &format))
                format = b->format;
 
        SET_BTREE_NODE_SEQ(n->data, BTREE_NODE_SEQ(b->data) + 1);
@@ -452,7 +455,7 @@ static void bch2_btree_reserve_put(struct btree_update *as, struct btree_trans *
 
                        btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_intent);
                        btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_write);
-                       __btree_node_free(c, b);
+                       __btree_node_free(trans, b);
                        six_unlock_write(&b->c.lock);
                        six_unlock_intent(&b->c.lock);
                }
@@ -465,7 +468,6 @@ static int bch2_btree_reserve_get(struct btree_trans *trans,
                                  unsigned flags,
                                  struct closure *cl)
 {
-       struct bch_fs *c = as->c;
        struct btree *b;
        unsigned interior;
        int ret = 0;
@@ -476,7 +478,7 @@ static int bch2_btree_reserve_get(struct btree_trans *trans,
         * Protects reaping from the btree node cache and using the btree node
         * open bucket reserve:
         */
-       ret = bch2_btree_cache_cannibalize_lock(c, cl);
+       ret = bch2_btree_cache_cannibalize_lock(trans, cl);
        if (ret)
                return ret;
 
@@ -495,7 +497,7 @@ static int bch2_btree_reserve_get(struct btree_trans *trans,
                }
        }
 err:
-       bch2_btree_cache_cannibalize_unlock(c);
+       bch2_btree_cache_cannibalize_unlock(trans);
        return ret;
 }
 
@@ -555,16 +557,13 @@ static int btree_update_nodes_written_trans(struct btree_trans *trans,
                                            struct btree_update *as)
 {
        struct bkey_i *k;
-       int ret;
 
-       ret = darray_make_room(&trans->extra_journal_entries, as->journal_u64s);
+       struct jset_entry *e = bch2_trans_jset_entry_alloc(trans, as->journal_u64s);
+       int ret = PTR_ERR_OR_ZERO(e);
        if (ret)
                return ret;
 
-       memcpy(&darray_top(trans->extra_journal_entries),
-              as->journal_entries,
-              as->journal_u64s * sizeof(u64));
-       trans->extra_journal_entries.nr += as->journal_u64s;
+       memcpy(e, as->journal_entries, as->journal_u64s * sizeof(u64));
 
        trans->journal_pin = &as->journal;
 
@@ -650,10 +649,11 @@ static void btree_update_nodes_written(struct btree_update *as)
                             "%s(): error %s", __func__, bch2_err_str(ret));
 err:
        if (as->b) {
-               struct btree_path *path;
 
                b = as->b;
-               path = get_unlocked_mut_path(trans, as->btree_id, b->c.level, b->key.k.p);
+               btree_path_idx_t path_idx = get_unlocked_mut_path(trans,
+                                               as->btree_id, b->c.level, b->key.k.p);
+               struct btree_path *path = trans->paths + path_idx;
                /*
                 * @b is the node we did the final insert into:
                 *
@@ -723,7 +723,7 @@ err:
 
                btree_node_write_if_need(c, b, SIX_LOCK_intent);
                btree_node_unlock(trans, path, b->c.level);
-               bch2_path_put(trans, path, true);
+               bch2_path_put(trans, path_idx, true);
        }
 
        bch2_journal_pin_drop(&c->journal, &as->journal);
@@ -774,9 +774,9 @@ static void btree_interior_update_work(struct work_struct *work)
        }
 }
 
-static void btree_update_set_nodes_written(struct closure *cl)
+static CLOSURE_CALLBACK(btree_update_set_nodes_written)
 {
-       struct btree_update *as = container_of(cl, struct btree_update, cl);
+       closure_type(as, struct btree_update, cl);
        struct bch_fs *c = as->c;
 
        mutex_lock(&c->btree_interior_update_lock);
@@ -1054,7 +1054,6 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
        unsigned nr_nodes[2] = { 0, 0 };
        unsigned update_level = level;
        enum bch_watermark watermark = flags & BCH_WATERMARK_MASK;
-       unsigned journal_flags = 0;
        int ret = 0;
        u32 restart_count = trans->restart_count;
 
@@ -1068,9 +1067,16 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
        flags &= ~BCH_WATERMARK_MASK;
        flags |= watermark;
 
-       if (flags & BCH_TRANS_COMMIT_journal_reclaim)
-               journal_flags |= JOURNAL_RES_GET_NONBLOCK;
-       journal_flags |= watermark;
+       if (!(flags & BCH_TRANS_COMMIT_journal_reclaim) &&
+           watermark < c->journal.watermark) {
+               struct journal_res res = { 0 };
+
+               ret = drop_locks_do(trans,
+                       bch2_journal_res_get(&c->journal, &res, 1,
+                                            watermark|JOURNAL_RES_GET_CHECK));
+               if (ret)
+                       return ERR_PTR(ret);
+       }
 
        while (1) {
                nr_nodes[!!update_level] += 1 + split;
@@ -1087,8 +1093,12 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
                        break;
                }
 
+               /*
+                * Always check for space for two keys, even if we won't have to
+                * split at prior level - it might have been a merge instead:
+                */
                if (bch2_btree_node_insert_fits(c, path->l[update_level].b,
-                                       BKEY_BTREE_PTR_U64s_MAX * (1 + split)))
+                                               BKEY_BTREE_PTR_U64s_MAX * 2))
                        break;
 
                split = path->l[update_level].b->nr.live_u64s > BTREE_SPLIT_THRESHOLD(c);
@@ -1181,6 +1191,9 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
        return as;
 err:
        bch2_btree_update_free(as, trans);
+       if (!bch2_err_matches(ret, ENOSPC) &&
+           !bch2_err_matches(ret, EROFS))
+               bch_err_fn_ratelimited(c, ret);
        return ERR_PTR(ret);
 }
 
@@ -1212,7 +1225,7 @@ static void bch2_btree_set_root(struct btree_update *as,
        struct bch_fs *c = as->c;
        struct btree *old;
 
-       trace_and_count(c, btree_node_set_root, c, b);
+       trace_and_count(c, btree_node_set_root, trans, b);
 
        old = btree_node_root(c, b);
 
@@ -1344,8 +1357,11 @@ static void __btree_split_node(struct btree_update *as,
        struct bkey_packed *out[2];
        struct bkey uk;
        unsigned u64s, n1_u64s = (b->nr.live_u64s * 3) / 5;
+       struct { unsigned nr_keys, val_u64s; } nr_keys[2];
        int i;
 
+       memset(&nr_keys, 0, sizeof(nr_keys));
+
        for (i = 0; i < 2; i++) {
                BUG_ON(n[i]->nsets != 1);
 
@@ -1367,6 +1383,9 @@ static void __btree_split_node(struct btree_update *as,
                if (!i)
                        n1_pos = uk.p;
                bch2_bkey_format_add_key(&format[i], &uk);
+
+               nr_keys[i].nr_keys++;
+               nr_keys[i].val_u64s += bkeyp_val_u64s(&b->format, k);
        }
 
        btree_set_min(n[0], b->data->min_key);
@@ -1379,6 +1398,12 @@ static void __btree_split_node(struct btree_update *as,
                bch2_bkey_format_add_pos(&format[i], n[i]->data->max_key);
 
                n[i]->data->format = bch2_bkey_format_done(&format[i]);
+
+               unsigned u64s = nr_keys[i].nr_keys * n[i]->data->format.key_u64s +
+                       nr_keys[i].val_u64s;
+               if (__vstruct_bytes(struct btree_node, u64s) > btree_bytes(as->c))
+                       n[i]->data->format = b->format;
+
                btree_node_set_format(n[i], n[i]->data->format);
        }
 
@@ -1431,10 +1456,12 @@ static void __btree_split_node(struct btree_update *as,
  */
 static void btree_split_insert_keys(struct btree_update *as,
                                    struct btree_trans *trans,
-                                   struct btree_path *path,
+                                   btree_path_idx_t path_idx,
                                    struct btree *b,
                                    struct keylist *keys)
 {
+       struct btree_path *path = trans->paths + path_idx;
+
        if (!bch2_keylist_empty(keys) &&
            bpos_le(bch2_keylist_front(keys)->k.p, b->data->max_key)) {
                struct btree_node_iter node_iter;
@@ -1448,25 +1475,25 @@ static void btree_split_insert_keys(struct btree_update *as,
 }
 
 static int btree_split(struct btree_update *as, struct btree_trans *trans,
-                      struct btree_path *path, struct btree *b,
+                      btree_path_idx_t path, struct btree *b,
                       struct keylist *keys, unsigned flags)
 {
        struct bch_fs *c = as->c;
-       struct btree *parent = btree_node_parent(path, b);
+       struct btree *parent = btree_node_parent(trans->paths + path, b);
        struct btree *n1, *n2 = NULL, *n3 = NULL;
-       struct btree_path *path1 = NULL, *path2 = NULL;
+       btree_path_idx_t path1 = 0, path2 = 0;
        u64 start_time = local_clock();
        int ret = 0;
 
        BUG_ON(!parent && (b != btree_node_root(c, b)));
-       BUG_ON(parent && !btree_node_intent_locked(path, b->c.level + 1));
+       BUG_ON(parent && !btree_node_intent_locked(trans->paths + path, b->c.level + 1));
 
        bch2_btree_interior_update_will_free_node(as, b);
 
        if (b->nr.live_u64s > BTREE_SPLIT_THRESHOLD(c)) {
                struct btree *n[2];
 
-               trace_and_count(c, btree_node_split, c, b);
+               trace_and_count(c, btree_node_split, trans, b);
 
                n[0] = n1 = bch2_btree_node_alloc(as, trans, b->c.level);
                n[1] = n2 = bch2_btree_node_alloc(as, trans, b->c.level);
@@ -1487,15 +1514,15 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                six_unlock_write(&n2->c.lock);
                six_unlock_write(&n1->c.lock);
 
-               path1 = get_unlocked_mut_path(trans, path->btree_id, n1->c.level, n1->key.k.p);
+               path1 = get_unlocked_mut_path(trans, as->btree_id, n1->c.level, n1->key.k.p);
                six_lock_increment(&n1->c.lock, SIX_LOCK_intent);
-               mark_btree_node_locked(trans, path1, n1->c.level, BTREE_NODE_INTENT_LOCKED);
-               bch2_btree_path_level_init(trans, path1, n1);
+               mark_btree_node_locked(trans, trans->paths + path1, n1->c.level, BTREE_NODE_INTENT_LOCKED);
+               bch2_btree_path_level_init(trans, trans->paths + path1, n1);
 
-               path2 = get_unlocked_mut_path(trans, path->btree_id, n2->c.level, n2->key.k.p);
+               path2 = get_unlocked_mut_path(trans, as->btree_id, n2->c.level, n2->key.k.p);
                six_lock_increment(&n2->c.lock, SIX_LOCK_intent);
-               mark_btree_node_locked(trans, path2, n2->c.level, BTREE_NODE_INTENT_LOCKED);
-               bch2_btree_path_level_init(trans, path2, n2);
+               mark_btree_node_locked(trans, trans->paths + path2, n2->c.level, BTREE_NODE_INTENT_LOCKED);
+               bch2_btree_path_level_init(trans, trans->paths + path2, n2);
 
                /*
                 * Note that on recursive parent_keys == keys, so we
@@ -1512,11 +1539,11 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                        bch2_btree_update_add_new_node(as, n3);
                        six_unlock_write(&n3->c.lock);
 
-                       path2->locks_want++;
-                       BUG_ON(btree_node_locked(path2, n3->c.level));
+                       trans->paths[path2].locks_want++;
+                       BUG_ON(btree_node_locked(trans->paths + path2, n3->c.level));
                        six_lock_increment(&n3->c.lock, SIX_LOCK_intent);
-                       mark_btree_node_locked(trans, path2, n3->c.level, BTREE_NODE_INTENT_LOCKED);
-                       bch2_btree_path_level_init(trans, path2, n3);
+                       mark_btree_node_locked(trans, trans->paths + path2, n3->c.level, BTREE_NODE_INTENT_LOCKED);
+                       bch2_btree_path_level_init(trans, trans->paths + path2, n3);
 
                        n3->sib_u64s[0] = U16_MAX;
                        n3->sib_u64s[1] = U16_MAX;
@@ -1524,7 +1551,7 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                        btree_split_insert_keys(as, trans, path, n3, &as->parent_keys);
                }
        } else {
-               trace_and_count(c, btree_node_compact, c, b);
+               trace_and_count(c, btree_node_compact, trans, b);
 
                n1 = bch2_btree_node_alloc_replacement(as, trans, b);
 
@@ -1537,10 +1564,10 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                bch2_btree_update_add_new_node(as, n1);
                six_unlock_write(&n1->c.lock);
 
-               path1 = get_unlocked_mut_path(trans, path->btree_id, n1->c.level, n1->key.k.p);
+               path1 = get_unlocked_mut_path(trans, as->btree_id, n1->c.level, n1->key.k.p);
                six_lock_increment(&n1->c.lock, SIX_LOCK_intent);
-               mark_btree_node_locked(trans, path1, n1->c.level, BTREE_NODE_INTENT_LOCKED);
-               bch2_btree_path_level_init(trans, path1, n1);
+               mark_btree_node_locked(trans, trans->paths + path1, n1->c.level, BTREE_NODE_INTENT_LOCKED);
+               bch2_btree_path_level_init(trans, trans->paths + path1, n1);
 
                if (parent)
                        bch2_keylist_add(&as->parent_keys, &n1->key);
@@ -1554,10 +1581,10 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                if (ret)
                        goto err;
        } else if (n3) {
-               bch2_btree_set_root(as, trans, path, n3);
+               bch2_btree_set_root(as, trans, trans->paths + path, n3);
        } else {
                /* Root filled up but didn't need to be split */
-               bch2_btree_set_root(as, trans, path, n1);
+               bch2_btree_set_root(as, trans, trans->paths + path, n1);
        }
 
        if (n3) {
@@ -1577,13 +1604,13 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
         * node after another thread has locked and updated the new node, thus
         * seeing stale data:
         */
-       bch2_btree_node_free_inmem(trans, path, b);
+       bch2_btree_node_free_inmem(trans, trans->paths + path, b);
 
        if (n3)
-               bch2_trans_node_add(trans, n3);
+               bch2_trans_node_add(trans, trans->paths + path, n3);
        if (n2)
-               bch2_trans_node_add(trans, n2);
-       bch2_trans_node_add(trans, n1);
+               bch2_trans_node_add(trans, trans->paths + path2, n2);
+       bch2_trans_node_add(trans, trans->paths + path1, n1);
 
        if (n3)
                six_unlock_intent(&n3->c.lock);
@@ -1592,11 +1619,11 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
        six_unlock_intent(&n1->c.lock);
 out:
        if (path2) {
-               __bch2_btree_path_unlock(trans, path2);
+               __bch2_btree_path_unlock(trans, trans->paths + path2);
                bch2_path_put(trans, path2, true);
        }
        if (path1) {
-               __bch2_btree_path_unlock(trans, path1);
+               __bch2_btree_path_unlock(trans, trans->paths + path1);
                bch2_path_put(trans, path1, true);
        }
 
@@ -1624,13 +1651,14 @@ bch2_btree_insert_keys_interior(struct btree_update *as,
                                struct keylist *keys)
 {
        struct btree_path *linked;
+       unsigned i;
 
        __bch2_btree_insert_keys_interior(as, trans, path, b,
                                          path->l[b->c.level].iter, keys);
 
        btree_update_updated_node(as, b);
 
-       trans_for_each_path_with_node(trans, b, linked)
+       trans_for_each_path_with_node(trans, b, linked, i)
                bch2_btree_node_iter_peek(&linked->l[b->c.level].iter, b);
 
        bch2_trans_verify_paths(trans);
@@ -1641,7 +1669,7 @@ bch2_btree_insert_keys_interior(struct btree_update *as,
  *
  * @as:                        btree_update object
  * @trans:             btree_trans object
- * @path:              path that points to current node
+ * @path_idx:          path that points to current node
  * @b:                 node to insert keys into
  * @keys:              list of keys to insert
  * @flags:             transaction commit flags
@@ -1653,10 +1681,11 @@ bch2_btree_insert_keys_interior(struct btree_update *as,
  * for leaf nodes -- inserts into interior nodes have to be atomic.
  */
 static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *trans,
-                                 struct btree_path *path, struct btree *b,
+                                 btree_path_idx_t path_idx, struct btree *b,
                                  struct keylist *keys, unsigned flags)
 {
        struct bch_fs *c = as->c;
+       struct btree_path *path = trans->paths + path_idx;
        int old_u64s = le16_to_cpu(btree_bset_last(b)->u64s);
        int old_live_u64s = b->nr.live_u64s;
        int live_u64s_added, u64s_added;
@@ -1709,19 +1738,22 @@ split:
                return btree_trans_restart(trans, BCH_ERR_transaction_restart_split_race);
        }
 
-       return btree_split(as, trans, path, b, keys, flags);
+       return btree_split(as, trans, path_idx, b, keys, flags);
 }
 
 int bch2_btree_split_leaf(struct btree_trans *trans,
-                         struct btree_path *path,
+                         btree_path_idx_t path,
                          unsigned flags)
 {
-       struct btree *b = path_l(path)->b;
+       /* btree_split & merge may both cause paths array to be reallocated */
+
+       struct btree *b = path_l(trans->paths + path)->b;
        struct btree_update *as;
        unsigned l;
        int ret = 0;
 
-       as = bch2_btree_update_start(trans, path, path->level,
+       as = bch2_btree_update_start(trans, trans->paths + path,
+                                    trans->paths[path].level,
                                     true, flags);
        if (IS_ERR(as))
                return PTR_ERR(as);
@@ -1734,20 +1766,21 @@ int bch2_btree_split_leaf(struct btree_trans *trans,
 
        bch2_btree_update_done(as, trans);
 
-       for (l = path->level + 1; btree_node_intent_locked(path, l) && !ret; l++)
+       for (l = trans->paths[path].level + 1;
+            btree_node_intent_locked(&trans->paths[path], l) && !ret;
+            l++)
                ret = bch2_foreground_maybe_merge(trans, path, l, flags);
 
        return ret;
 }
 
 int __bch2_foreground_maybe_merge(struct btree_trans *trans,
-                                 struct btree_path *path,
+                                 btree_path_idx_t path,
                                  unsigned level,
                                  unsigned flags,
                                  enum btree_node_sibling sib)
 {
        struct bch_fs *c = trans->c;
-       struct btree_path *sib_path = NULL, *new_path = NULL;
        struct btree_update *as;
        struct bkey_format_state new_s;
        struct bkey_format new_f;
@@ -1755,13 +1788,15 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
        struct btree *b, *m, *n, *prev, *next, *parent;
        struct bpos sib_pos;
        size_t sib_u64s;
+       enum btree_id btree = trans->paths[path].btree_id;
+       btree_path_idx_t sib_path = 0, new_path = 0;
        u64 start_time = local_clock();
        int ret = 0;
 
-       BUG_ON(!path->should_be_locked);
-       BUG_ON(!btree_node_locked(path, level));
+       BUG_ON(!trans->paths[path].should_be_locked);
+       BUG_ON(!btree_node_locked(&trans->paths[path], level));
 
-       b = path->l[level].b;
+       b = trans->paths[path].l[level].b;
 
        if ((sib == btree_prev_sib && bpos_eq(b->data->min_key, POS_MIN)) ||
            (sib == btree_next_sib && bpos_eq(b->data->max_key, SPOS_MAX))) {
@@ -1773,18 +1808,18 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
                ? bpos_predecessor(b->data->min_key)
                : bpos_successor(b->data->max_key);
 
-       sib_path = bch2_path_get(trans, path->btree_id, sib_pos,
+       sib_path = bch2_path_get(trans, btree, sib_pos,
                                 U8_MAX, level, BTREE_ITER_INTENT, _THIS_IP_);
        ret = bch2_btree_path_traverse(trans, sib_path, false);
        if (ret)
                goto err;
 
-       btree_path_set_should_be_locked(sib_path);
+       btree_path_set_should_be_locked(trans->paths + sib_path);
 
-       m = sib_path->l[level].b;
+       m = trans->paths[sib_path].l[level].b;
 
-       if (btree_node_parent(path, b) !=
-           btree_node_parent(sib_path, m)) {
+       if (btree_node_parent(trans->paths + path, b) !=
+           btree_node_parent(trans->paths + sib_path, m)) {
                b->sib_u64s[sib] = U16_MAX;
                goto out;
        }
@@ -1821,8 +1856,8 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
        bch2_bkey_format_add_pos(&new_s, next->data->max_key);
        new_f = bch2_bkey_format_done(&new_s);
 
-       sib_u64s = btree_node_u64s_with_format(b, &new_f) +
-               btree_node_u64s_with_format(m, &new_f);
+       sib_u64s = btree_node_u64s_with_format(b->nr, &b->format, &new_f) +
+               btree_node_u64s_with_format(m->nr, &m->format, &new_f);
 
        if (sib_u64s > BTREE_FOREGROUND_MERGE_HYSTERESIS(c)) {
                sib_u64s -= BTREE_FOREGROUND_MERGE_HYSTERESIS(c);
@@ -1837,14 +1872,14 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
        if (b->sib_u64s[sib] > c->btree_foreground_merge_threshold)
                goto out;
 
-       parent = btree_node_parent(path, b);
-       as = bch2_btree_update_start(trans, path, level, false,
+       parent = btree_node_parent(trans->paths + path, b);
+       as = bch2_btree_update_start(trans, trans->paths + path, level, false,
                                     BCH_TRANS_COMMIT_no_enospc|flags);
        ret = PTR_ERR_OR_ZERO(as);
        if (ret)
                goto err;
 
-       trace_and_count(c, btree_node_merge, c, b);
+       trace_and_count(c, btree_node_merge, trans, b);
 
        bch2_btree_interior_update_will_free_node(as, b);
        bch2_btree_interior_update_will_free_node(as, m);
@@ -1868,10 +1903,10 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
        bch2_btree_update_add_new_node(as, n);
        six_unlock_write(&n->c.lock);
 
-       new_path = get_unlocked_mut_path(trans, path->btree_id, n->c.level, n->key.k.p);
+       new_path = get_unlocked_mut_path(trans, btree, n->c.level, n->key.k.p);
        six_lock_increment(&n->c.lock, SIX_LOCK_intent);
-       mark_btree_node_locked(trans, new_path, n->c.level, BTREE_NODE_INTENT_LOCKED);
-       bch2_btree_path_level_init(trans, new_path, n);
+       mark_btree_node_locked(trans, trans->paths + new_path, n->c.level, BTREE_NODE_INTENT_LOCKED);
+       bch2_btree_path_level_init(trans, trans->paths + new_path, n);
 
        bkey_init(&delete.k);
        delete.k.p = prev->key.k.p;
@@ -1889,10 +1924,10 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
        bch2_btree_update_get_open_buckets(as, n);
        bch2_btree_node_write(c, n, SIX_LOCK_intent, 0);
 
-       bch2_btree_node_free_inmem(trans, path, b);
-       bch2_btree_node_free_inmem(trans, sib_path, m);
+       bch2_btree_node_free_inmem(trans, trans->paths + path, b);
+       bch2_btree_node_free_inmem(trans, trans->paths + sib_path, m);
 
-       bch2_trans_node_add(trans, n);
+       bch2_trans_node_add(trans, trans->paths + path, n);
 
        bch2_trans_verify_paths(trans);
 
@@ -1920,16 +1955,16 @@ int bch2_btree_node_rewrite(struct btree_trans *trans,
                            unsigned flags)
 {
        struct bch_fs *c = trans->c;
-       struct btree_path *new_path = NULL;
        struct btree *n, *parent;
        struct btree_update *as;
+       btree_path_idx_t new_path = 0;
        int ret;
 
        flags |= BCH_TRANS_COMMIT_no_enospc;
 
-       parent = btree_node_parent(iter->path, b);
-       as = bch2_btree_update_start(trans, iter->path, b->c.level,
-                                    false, flags);
+       struct btree_path *path = btree_iter_path(trans, iter);
+       parent = btree_node_parent(path, b);
+       as = bch2_btree_update_start(trans, path, b->c.level, false, flags);
        ret = PTR_ERR_OR_ZERO(as);
        if (ret)
                goto out;
@@ -1944,27 +1979,27 @@ int bch2_btree_node_rewrite(struct btree_trans *trans,
 
        new_path = get_unlocked_mut_path(trans, iter->btree_id, n->c.level, n->key.k.p);
        six_lock_increment(&n->c.lock, SIX_LOCK_intent);
-       mark_btree_node_locked(trans, new_path, n->c.level, BTREE_NODE_INTENT_LOCKED);
-       bch2_btree_path_level_init(trans, new_path, n);
+       mark_btree_node_locked(trans, trans->paths + new_path, n->c.level, BTREE_NODE_INTENT_LOCKED);
+       bch2_btree_path_level_init(trans, trans->paths + new_path, n);
 
-       trace_and_count(c, btree_node_rewrite, c, b);
+       trace_and_count(c, btree_node_rewrite, trans, b);
 
        if (parent) {
                bch2_keylist_add(&as->parent_keys, &n->key);
-               ret = bch2_btree_insert_node(as, trans, iter->path, parent,
-                                            &as->parent_keys, flags);
+               ret = bch2_btree_insert_node(as, trans, iter->path,
+                                            parent, &as->parent_keys, flags);
                if (ret)
                        goto err;
        } else {
-               bch2_btree_set_root(as, trans, iter->path, n);
+               bch2_btree_set_root(as, trans, btree_iter_path(trans, iter), n);
        }
 
        bch2_btree_update_get_open_buckets(as, n);
        bch2_btree_node_write(c, n, SIX_LOCK_intent, 0);
 
-       bch2_btree_node_free_inmem(trans, iter->path, b);
+       bch2_btree_node_free_inmem(trans, btree_iter_path(trans, iter), b);
 
-       bch2_trans_node_add(trans, n);
+       bch2_trans_node_add(trans, trans->paths + iter->path, n);
        six_unlock_intent(&n->c.lock);
 
        bch2_btree_update_done(as, trans);
@@ -2033,8 +2068,7 @@ static void async_btree_node_rewrite_work(struct work_struct *work)
 
        ret = bch2_trans_do(c, NULL, NULL, 0,
                      async_btree_node_rewrite_trans(trans, a));
-       if (ret)
-               bch_err_fn(c, ret);
+       bch_err_fn(c, ret);
        bch2_write_ref_put(c, BCH_WRITE_REF_node_rewrite);
        kfree(a);
 }
@@ -2057,7 +2091,7 @@ void bch2_btree_node_rewrite_async(struct bch_fs *c, struct btree *b)
        a->seq          = b->data->keys.seq;
        INIT_WORK(&a->work, async_btree_node_rewrite_work);
 
-       if (unlikely(!test_bit(BCH_FS_MAY_GO_RW, &c->flags))) {
+       if (unlikely(!test_bit(BCH_FS_may_go_rw, &c->flags))) {
                mutex_lock(&c->pending_node_rewrites_lock);
                list_add(&a->list, &c->pending_node_rewrites);
                mutex_unlock(&c->pending_node_rewrites_lock);
@@ -2065,15 +2099,15 @@ void bch2_btree_node_rewrite_async(struct bch_fs *c, struct btree *b)
        }
 
        if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_node_rewrite)) {
-               if (test_bit(BCH_FS_STARTED, &c->flags)) {
+               if (test_bit(BCH_FS_started, &c->flags)) {
                        bch_err(c, "%s: error getting c->writes ref", __func__);
                        kfree(a);
                        return;
                }
 
                ret = bch2_fs_read_write_early(c);
+               bch_err_msg(c, ret, "going read-write");
                if (ret) {
-                       bch_err_msg(c, ret, "going read-write");
                        kfree(a);
                        return;
                }
@@ -2142,7 +2176,7 @@ static int __bch2_btree_node_update_key(struct btree_trans *trans,
                BUG_ON(ret);
        }
 
-       parent = btree_node_parent(iter->path, b);
+       parent = btree_node_parent(btree_iter_path(trans, iter), b);
        if (parent) {
                bch2_trans_copy_iter(&iter2, iter);
 
@@ -2150,10 +2184,11 @@ static int __bch2_btree_node_update_key(struct btree_trans *trans,
                                iter2.flags & BTREE_ITER_INTENT,
                                _THIS_IP_);
 
-               BUG_ON(iter2.path->level != b->c.level);
-               BUG_ON(!bpos_eq(iter2.path->pos, new_key->k.p));
+               struct btree_path *path2 = btree_iter_path(trans, &iter2);
+               BUG_ON(path2->level != b->c.level);
+               BUG_ON(!bpos_eq(path2->pos, new_key->k.p));
 
-               btree_path_set_level_up(trans, iter2.path);
+               btree_path_set_level_up(trans, path2);
 
                trans->paths_sorted = false;
 
@@ -2164,23 +2199,23 @@ static int __bch2_btree_node_update_key(struct btree_trans *trans,
        } else {
                BUG_ON(btree_node_root(c, b) != b);
 
-               ret = darray_make_room(&trans->extra_journal_entries,
+               struct jset_entry *e = bch2_trans_jset_entry_alloc(trans,
                                       jset_u64s(new_key->k.u64s));
+               ret = PTR_ERR_OR_ZERO(e);
                if (ret)
                        return ret;
 
-               journal_entry_set((void *) &darray_top(trans->extra_journal_entries),
+               journal_entry_set(e,
                                  BCH_JSET_ENTRY_btree_root,
                                  b->c.btree_id, b->c.level,
                                  new_key, new_key->k.u64s);
-               trans->extra_journal_entries.nr += jset_u64s(new_key->k.u64s);
        }
 
        ret = bch2_trans_commit(trans, NULL, NULL, commit_flags);
        if (ret)
                goto err;
 
-       bch2_btree_node_lock_write_nofail(trans, iter->path, &b->c);
+       bch2_btree_node_lock_write_nofail(trans, btree_iter_path(trans, iter), &b->c);
 
        if (new_hash) {
                mutex_lock(&c->btree_cache.lock);
@@ -2195,7 +2230,7 @@ static int __bch2_btree_node_update_key(struct btree_trans *trans,
                bkey_copy(&b->key, new_key);
        }
 
-       bch2_btree_node_unlock_write(trans, iter->path, b);
+       bch2_btree_node_unlock_write(trans, btree_iter_path(trans, iter), b);
 out:
        bch2_trans_iter_exit(trans, &iter2);
        return ret;
@@ -2214,7 +2249,7 @@ int bch2_btree_node_update_key(struct btree_trans *trans, struct btree_iter *ite
 {
        struct bch_fs *c = trans->c;
        struct btree *new_hash = NULL;
-       struct btree_path *path = iter->path;
+       struct btree_path *path = btree_iter_path(trans, iter);
        struct closure cl;
        int ret = 0;
 
@@ -2229,7 +2264,7 @@ int bch2_btree_node_update_key(struct btree_trans *trans, struct btree_iter *ite
         * btree_iter_traverse():
         */
        if (btree_ptr_hash_val(new_key) != b->hash_val) {
-               ret = bch2_btree_cache_cannibalize_lock(c, &cl);
+               ret = bch2_btree_cache_cannibalize_lock(trans, &cl);
                if (ret) {
                        ret = drop_locks_do(trans, (closure_sync(&cl), 0));
                        if (ret)
@@ -2253,7 +2288,7 @@ int bch2_btree_node_update_key(struct btree_trans *trans, struct btree_iter *ite
                six_unlock_intent(&new_hash->c.lock);
        }
        closure_sync(&cl);
-       bch2_btree_cache_cannibalize_unlock(c);
+       bch2_btree_cache_cannibalize_unlock(trans);
        return ret;
 }
 
@@ -2272,7 +2307,7 @@ int bch2_btree_node_update_key_get_iter(struct btree_trans *trans,
                goto out;
 
        /* has node been freed? */
-       if (iter.path->l[b->c.level].b != b) {
+       if (btree_iter_path(trans, &iter)->l[b->c.level].b != b) {
                /* node has been freed: */
                BUG_ON(!btree_node_dying(b));
                goto out;
@@ -2280,6 +2315,10 @@ int bch2_btree_node_update_key_get_iter(struct btree_trans *trans,
 
        BUG_ON(!btree_node_hashed(b));
 
+       struct bch_extent_ptr *ptr;
+       bch2_bkey_drop_ptrs(bkey_i_to_s(new_key), ptr,
+                           !bch2_bkey_has_device(bkey_i_to_s(&b->key), ptr->dev));
+
        ret = bch2_btree_node_update_key(trans, &iter, b, new_key,
                                         commit_flags, skip_triggers);
 out:
@@ -2310,12 +2349,12 @@ static int __bch2_btree_root_alloc(struct btree_trans *trans, enum btree_id id)
        closure_init_stack(&cl);
 
        do {
-               ret = bch2_btree_cache_cannibalize_lock(c, &cl);
+               ret = bch2_btree_cache_cannibalize_lock(trans, &cl);
                closure_sync(&cl);
        } while (ret);
 
        b = bch2_btree_node_mem_alloc(trans, false);
-       bch2_btree_cache_cannibalize_unlock(c);
+       bch2_btree_cache_cannibalize_unlock(trans);
 
        set_btree_node_fake(b);
        set_btree_node_need_rewrite(b);