]> www.infradead.org Git - users/hch/misc.git/commitdiff
bcachefs: Fix topology errors on split after merge
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 31 Oct 2024 06:50:55 +0000 (02:50 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 7 Nov 2024 21:48:21 +0000 (16:48 -0500)
If a btree split picks a pivot that's being deleted by a btree node
merge, we're going to have problems.

Fix this by checking if the pivot is being deleted, the same as we check
for deletions in journal replay keys.

Found by single_devic.ktest small_nodes.

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

index 64f0928e113784c5aabda46c0197d0a5882409da..c12d7b51de0c7c03ebaeef4d78c9d13c7d830a9d 100644 (file)
@@ -1434,6 +1434,15 @@ bch2_btree_insert_keys_interior(struct btree_update *as,
        }
 }
 
+static bool key_deleted_in_insert(struct keylist *insert_keys, struct bpos pos)
+{
+       if (insert_keys)
+               for_each_keylist_key(insert_keys, k)
+                       if (bkey_deleted(&k->k) && bpos_eq(k->k.p, pos))
+                               return true;
+       return false;
+}
+
 /*
  * Move keys from n1 (original replacement node, now lower node) to n2 (higher
  * node)
@@ -1441,7 +1450,8 @@ bch2_btree_insert_keys_interior(struct btree_update *as,
 static void __btree_split_node(struct btree_update *as,
                               struct btree_trans *trans,
                               struct btree *b,
-                              struct btree *n[2])
+                              struct btree *n[2],
+                              struct keylist *insert_keys)
 {
        struct bkey_packed *k;
        struct bpos n1_pos = POS_MIN;
@@ -1476,7 +1486,8 @@ static void __btree_split_node(struct btree_update *as,
                if (b->c.level &&
                    u64s < n1_u64s &&
                    u64s + k->u64s >= n1_u64s &&
-                   bch2_key_deleted_in_journal(trans, b->c.btree_id, b->c.level, uk.p))
+                   (bch2_key_deleted_in_journal(trans, b->c.btree_id, b->c.level, uk.p) ||
+                    key_deleted_in_insert(insert_keys, uk.p)))
                        n1_u64s += k->u64s;
 
                i = u64s >= n1_u64s;
@@ -1603,7 +1614,7 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
                n[0] = n1 = bch2_btree_node_alloc(as, trans, b->c.level);
                n[1] = n2 = bch2_btree_node_alloc(as, trans, b->c.level);
 
-               __btree_split_node(as, trans, b, n);
+               __btree_split_node(as, trans, b, n, keys);
 
                if (keys) {
                        btree_split_insert_keys(as, trans, path, n1, keys);