btrfs: scrub: introduce a helper to locate an extent item

The new helper, find_first_extent_item(), will locate an extent item
(either EXTENT_ITEM or METADATA_ITEM) which covers any byte of the
search range.

This helper will later be used to refactor scrub code.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2022-03-11 15:38:42 +08:00 committed by David Sterba
parent 1194a82481
commit 416bd7e7af

View File

@ -2859,6 +2859,113 @@ static void scrub_parity_put(struct scrub_parity *sparity)
scrub_parity_check_and_repair(sparity);
}
/*
* Return 0 if the extent item range covers any byte of the range.
* Return <0 if the extent item is before @search_start.
* Return >0 if the extent item is after @start_start + @search_len.
*/
static int compare_extent_item_range(struct btrfs_path *path,
u64 search_start, u64 search_len)
{
struct btrfs_fs_info *fs_info = path->nodes[0]->fs_info;
u64 len;
struct btrfs_key key;
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ASSERT(key.type == BTRFS_EXTENT_ITEM_KEY ||
key.type == BTRFS_METADATA_ITEM_KEY);
if (key.type == BTRFS_METADATA_ITEM_KEY)
len = fs_info->nodesize;
else
len = key.offset;
if (key.objectid + len <= search_start)
return -1;
if (key.objectid >= search_start + search_len)
return 1;
return 0;
}
/*
* Locate one extent item which covers any byte in range
* [@search_start, @search_start + @search_length)
*
* If the path is not initialized, we will initialize the search by doing
* a btrfs_search_slot().
* If the path is already initialized, we will use the path as the initial
* slot, to avoid duplicated btrfs_search_slot() calls.
*
* NOTE: If an extent item starts before @search_start, we will still
* return the extent item. This is for data extent crossing stripe boundary.
*
* Return 0 if we found such extent item, and @path will point to the extent item.
* Return >0 if no such extent item can be found, and @path will be released.
* Return <0 if hit fatal error, and @path will be released.
*/
static int find_first_extent_item(struct btrfs_root *extent_root,
struct btrfs_path *path,
u64 search_start, u64 search_len)
{
struct btrfs_fs_info *fs_info = extent_root->fs_info;
struct btrfs_key key;
int ret;
/* Continue using the existing path */
if (path->nodes[0])
goto search_forward;
if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
key.type = BTRFS_METADATA_ITEM_KEY;
else
key.type = BTRFS_EXTENT_ITEM_KEY;
key.objectid = search_start;
key.offset = (u64)-1;
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0)
return ret;
ASSERT(ret > 0);
/*
* Here we intentionally pass 0 as @min_objectid, as there could be
* an extent item starting before @search_start.
*/
ret = btrfs_previous_extent_item(extent_root, path, 0);
if (ret < 0)
return ret;
/*
* No matter whether we have found an extent item, the next loop will
* properly do every check on the key.
*/
search_forward:
while (true) {
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
if (key.objectid >= search_start + search_len)
break;
if (key.type != BTRFS_METADATA_ITEM_KEY &&
key.type != BTRFS_EXTENT_ITEM_KEY)
goto next;
ret = compare_extent_item_range(path, search_start, search_len);
if (ret == 0)
return ret;
if (ret > 0)
break;
next:
path->slots[0]++;
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
ret = btrfs_next_leaf(extent_root, path);
if (ret) {
/* Either no more item or fatal error */
btrfs_release_path(path);
return ret;
}
}
}
btrfs_release_path(path);
return 1;
}
static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx,
struct map_lookup *map,
struct btrfs_device *sdev,