btrfs: scrub: introduce a helper to locate an extent item
authorQu Wenruo <wqu@suse.com>
Fri, 11 Mar 2022 07:38:42 +0000 (15:38 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 16 May 2022 15:17:30 +0000 (17:17 +0200)
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>
fs/btrfs/scrub.c

index 530913d..456df1b 100644 (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,