bcachefs: More btree iterator fixes
authorKent Overstreet <kent.overstreet@gmail.com>
Thu, 7 Oct 2021 18:56:56 +0000 (14:56 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:13 +0000 (17:09 -0400)
 - check for getting to the end of the btree in bch2_path_verify_locks
   and __btree_path_traverse_all(), this fixes an infinite loop in
   __btree_path_traverse_all().
 - relax requirement in bch2_btree_node_upgrade() that we must want an
   intent lock, this fixes bugs with paths that point to interior nodes
   (nonzero level).
 - bch2_btree_node_update_key(): fix it to upgrade the path to an intent
   lock, if necessary

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/btree_iter.c
fs/bcachefs/btree_update_interior.c

index 042b8bdf44453821abf571fc72adfb81cdc4076b..b086f05b117bf5b2207f11f1321e1f855bc43706 100644 (file)
@@ -170,11 +170,20 @@ static bool bch2_btree_node_upgrade(struct btree_trans *trans,
 {
        struct btree *b = path->l[level].b;
 
-       EBUG_ON(btree_lock_want(path, level) != BTREE_NODE_INTENT_LOCKED);
-
        if (!is_btree_node(path, level))
                return false;
 
+       switch (btree_lock_want(path, level)) {
+       case BTREE_NODE_UNLOCKED:
+               BUG_ON(btree_node_locked(path, level));
+               return true;
+       case BTREE_NODE_READ_LOCKED:
+               BUG_ON(btree_node_intent_locked(path, level));
+               return bch2_btree_node_relock(trans, path, level);
+       case BTREE_NODE_INTENT_LOCKED:
+               break;
+       }
+
        if (btree_node_intent_locked(path, level))
                return true;
 
@@ -368,7 +377,8 @@ static void bch2_btree_path_verify_locks(struct btree_path *path)
        unsigned l;
 
        if (!path->nodes_locked) {
-               BUG_ON(path->uptodate == BTREE_ITER_UPTODATE);
+               BUG_ON(path->uptodate == BTREE_ITER_UPTODATE &&
+                      btree_path_node(path, path->level));
                return;
        }
 
@@ -1356,7 +1366,8 @@ retry_all:
 
                EBUG_ON(!(trans->paths_allocated & (1ULL << path->idx)));
 
-               if (path->nodes_locked)
+               if (path->nodes_locked ||
+                   !btree_path_node(path, path->level))
                        i++;
        }
 
index 978bb56275de7ee78b2a54b61bbd216a04a99af4..4ca2de360b2267c14adf00fec48d5a6e0eebc558 100644 (file)
@@ -1945,9 +1945,16 @@ 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 closure cl;
        int ret = 0;
 
+       if (!btree_node_intent_locked(path, b->c.level) &&
+           !bch2_btree_path_upgrade(trans, path, b->c.level + 1)) {
+               btree_trans_restart(trans);
+               return -EINTR;
+       }
+
        closure_init_stack(&cl);
 
        /*
@@ -1966,8 +1973,10 @@ int bch2_btree_node_update_key(struct btree_trans *trans, struct btree_iter *ite
                new_hash = bch2_btree_node_mem_alloc(c);
        }
 
+       path->intent_ref++;
        ret = __bch2_btree_node_update_key(trans, iter, b, new_hash,
                                           new_key, skip_triggers);
+       --path->intent_ref;
 
        if (new_hash) {
                mutex_lock(&c->btree_cache.lock);