From d2a15ea5068f34880e056469b3fa9817a0a7dc90 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 15 Apr 2020 16:24:51 -0700 Subject: [PATCH] scoutfs: fix depth-first radix next bit search The radix block next bit search could return a spurious -ENOENT if it ran out of references in a parent block further down the tree. It needs to bubble up to try the next ref in its parent so that it keeps performing a depth-first search of the entire tree. This lead to an assertion being tripped in _radix_merge. Getting an early -ENOENT caused it to start searching from 0 again. When it's iterating over a read-only input it could find the same leaf and try to clear source bits that were already cleared. Signed-off-by: Zach Brown --- kmod/src/radix.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/kmod/src/radix.c b/kmod/src/radix.c index 09ab5c4f..c9d51a73 100644 --- a/kmod/src/radix.c +++ b/kmod/src/radix.c @@ -901,9 +901,29 @@ static int get_path(struct super_block *sb, struct scoutfs_radix_root *root, ind++; } + /* + * Didn't find a ref in the rest of the block at + * this level. If we're the root block there's no + * more next bits to return. If we're further down + * we bubble up a level and continue on a depth-first + * search. We check the next ref from our parent and reset + * all the child inds to the left spine of the new + * subtree. + */ if (ind >= SCOUTFS_RADIX_REFS) { - ret = -ENOENT; - goto out; + if (level == root->height - 1) { + ret = -ENOENT; + goto out; + } + path->inds[level + 1]++; + for (i = level; i >= 0; i--) + path->inds[i] = 0; + for (i = level; i <= level + 1; i++) { + scoutfs_block_put(sb, path->bls[i]); + path->bls[i] = NULL; + } + level += 2; + continue; } /* reset all lower indices if we searched */