btrfs: introduce a bitmap based csum range search function
authorQu Wenruo <wqu@suse.com>
Mon, 14 Nov 2022 00:26:32 +0000 (08:26 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 5 Dec 2022 17:00:57 +0000 (18:00 +0100)
Although we have an existing function, btrfs_lookup_csums_range(), to
find all data checksums for a range, it's based on a btrfs_ordered_sum
list.

For the incoming RAID56 data checksum verification at RMW time, we don't
want to waste time by allocating temporary memory.

So this patch will introduce a new helper, btrfs_lookup_csums_bitmap().
It will use bitmap based result, which will be a perfect fit for later
RAID56 usage.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file-item.c
fs/btrfs/file-item.h
fs/btrfs/inode.c
fs/btrfs/relocation.c
fs/btrfs/scrub.c
fs/btrfs/tree-log.c

index 654ad2494bce61b2906e9c5a9fa4326e79debda7..352bbb33b76f012ac348a0d0c2e239f3af574941 100644 (file)
@@ -527,9 +527,9 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
        return ret;
 }
 
-int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
-                            struct list_head *list, int search_commit,
-                            bool nowait)
+int btrfs_lookup_csums_list(struct btrfs_root *root, u64 start, u64 end,
+                           struct list_head *list, int search_commit,
+                           bool nowait)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_key key;
@@ -661,6 +661,127 @@ fail:
        return ret;
 }
 
+/*
+ * Do the same work as btrfs_lookup_csums_list(), the difference is in how
+ * we return the result.
+ *
+ * This version will set the corresponding bits in @csum_bitmap to represent
+ * that there is a csum found.
+ * Each bit represents a sector. Thus caller should ensure @csum_buf passed
+ * in is large enough to contain all csums.
+ */
+int btrfs_lookup_csums_bitmap(struct btrfs_root *root, u64 start, u64 end,
+                             u8 *csum_buf, unsigned long *csum_bitmap)
+{
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_key key;
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       struct btrfs_csum_item *item;
+       const u64 orig_start = start;
+       int ret;
+
+       ASSERT(IS_ALIGNED(start, fs_info->sectorsize) &&
+              IS_ALIGNED(end + 1, fs_info->sectorsize));
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+       key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
+       key.type = BTRFS_EXTENT_CSUM_KEY;
+       key.offset = start;
+
+       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       if (ret < 0)
+               goto fail;
+       if (ret > 0 && path->slots[0] > 0) {
+               leaf = path->nodes[0];
+               btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1);
+
+               /*
+                * There are two cases we can hit here for the previous csum
+                * item:
+                *
+                *              |<- search range ->|
+                *      |<- csum item ->|
+                *
+                * Or
+                *                              |<- search range ->|
+                *      |<- csum item ->|
+                *
+                * Check if the previous csum item covers the leading part of
+                * the search range.  If so we have to start from previous csum
+                * item.
+                */
+               if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID &&
+                   key.type == BTRFS_EXTENT_CSUM_KEY) {
+                       if (bytes_to_csum_size(fs_info, start - key.offset) <
+                           btrfs_item_size(leaf, path->slots[0] - 1))
+                               path->slots[0]--;
+               }
+       }
+
+       while (start <= end) {
+               u64 csum_end;
+
+               leaf = path->nodes[0];
+               if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0)
+                               goto fail;
+                       if (ret > 0)
+                               break;
+                       leaf = path->nodes[0];
+               }
+
+               btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+               if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
+                   key.type != BTRFS_EXTENT_CSUM_KEY ||
+                   key.offset > end)
+                       break;
+
+               if (key.offset > start)
+                       start = key.offset;
+
+               csum_end = key.offset + csum_size_to_bytes(fs_info,
+                                       btrfs_item_size(leaf, path->slots[0]));
+               if (csum_end <= start) {
+                       path->slots[0]++;
+                       continue;
+               }
+
+               csum_end = min(csum_end, end + 1);
+               item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                                     struct btrfs_csum_item);
+               while (start < csum_end) {
+                       unsigned long offset;
+                       size_t size;
+                       u8 *csum_dest = csum_buf + bytes_to_csum_size(fs_info,
+                                               start - orig_start);
+
+                       size = min_t(size_t, csum_end - start, end + 1 - start);
+
+                       offset = bytes_to_csum_size(fs_info, start - key.offset);
+
+                       read_extent_buffer(path->nodes[0], csum_dest,
+                                          ((unsigned long)item) + offset,
+                                          bytes_to_csum_size(fs_info, size));
+
+                       bitmap_set(csum_bitmap,
+                               (start - orig_start) >> fs_info->sectorsize_bits,
+                               size >> fs_info->sectorsize_bits);
+
+                       start += size;
+               }
+               path->slots[0]++;
+       }
+       ret = 0;
+fail:
+       btrfs_free_path(path);
+       return ret;
+}
+
 /*
  * Calculate checksums of the data contained inside a bio.
  *
index ba12711cf93376c095d1594f9607fb9845736f4e..95b371208b30647c5c7f44ee91b1fd8cb7dd96f6 100644 (file)
@@ -18,9 +18,11 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
                           struct btrfs_ordered_sum *sums);
 blk_status_t btrfs_csum_one_bio(struct btrfs_inode *inode, struct bio *bio,
                                u64 offset, bool one_ordered);
-int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
-                            struct list_head *list, int search_commit,
-                            bool nowait);
+int btrfs_lookup_csums_list(struct btrfs_root *root, u64 start, u64 end,
+                           struct list_head *list, int search_commit,
+                           bool nowait);
+int btrfs_lookup_csums_bitmap(struct btrfs_root *root, u64 start, u64 end,
+                             u8 *csum_buf, unsigned long *csum_bitmap);
 void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
                                     const struct btrfs_path *path,
                                     struct btrfs_file_extent_item *fi,
index 83898bca39d5cbceca9f725f9a4e011ff49aaa2c..4248e6cabbdcb3e61144c29618a9a3bb00f6be4a 100644 (file)
@@ -1709,9 +1709,8 @@ static noinline int csum_exist_in_range(struct btrfs_fs_info *fs_info,
        int ret;
        LIST_HEAD(list);
 
-       ret = btrfs_lookup_csums_range(csum_root, bytenr,
-                                      bytenr + num_bytes - 1, &list, 0,
-                                      nowait);
+       ret = btrfs_lookup_csums_list(csum_root, bytenr, bytenr + num_bytes - 1,
+                                     &list, 0, nowait);
        if (ret == 0 && list_empty(&list))
                return 0;
 
index 56c8afa6f6d2bab8f6ef04fab5f28e3fa3067e39..aa80e51bc8ca4d6f16aaa2cf48aa0b16bf11bbec 100644 (file)
@@ -4357,8 +4357,8 @@ int btrfs_reloc_clone_csums(struct btrfs_inode *inode, u64 file_pos, u64 len)
 
        disk_bytenr = file_pos + inode->index_cnt;
        csum_root = btrfs_csum_root(fs_info, disk_bytenr);
-       ret = btrfs_lookup_csums_range(csum_root, disk_bytenr,
-                                      disk_bytenr + len - 1, &list, 0, false);
+       ret = btrfs_lookup_csums_list(csum_root, disk_bytenr,
+                                     disk_bytenr + len - 1, &list, 0, false);
        if (ret)
                goto out;
 
index 0ce5b773867f8ad1afdfba6082f2cee3f28f813f..52b346795f6606bc24bac0c6901b821b3e93d4ab 100644 (file)
@@ -3238,9 +3238,9 @@ static int scrub_raid56_data_stripe_for_parity(struct scrub_ctx *sctx,
                extent_dev = bioc->stripes[0].dev;
                btrfs_put_bioc(bioc);
 
-               ret = btrfs_lookup_csums_range(csum_root, extent_start,
-                                              extent_start + extent_size - 1,
-                                              &sctx->csum_list, 1, false);
+               ret = btrfs_lookup_csums_list(csum_root, extent_start,
+                                             extent_start + extent_size - 1,
+                                             &sctx->csum_list, 1, false);
                if (ret) {
                        scrub_parity_mark_sectors_error(sparity, extent_start,
                                                        extent_size);
@@ -3464,7 +3464,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
                            cur_logical;
 
                if (extent_flags & BTRFS_EXTENT_FLAG_DATA) {
-                       ret = btrfs_lookup_csums_range(csum_root, cur_logical,
+                       ret = btrfs_lookup_csums_list(csum_root, cur_logical,
                                        cur_logical + scrub_len - 1,
                                        &sctx->csum_list, 1, false);
                        if (ret)
index 4d9f6803bfbe26ca3d7c9f8c449971a00e7b2ae2..8f8d7e7dc3a806ceb7bb791fff7ad91db41dca8a 100644 (file)
@@ -826,7 +826,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
                                        btrfs_file_extent_num_bytes(eb, item);
                        }
 
-                       ret = btrfs_lookup_csums_range(root->log_root,
+                       ret = btrfs_lookup_csums_list(root->log_root,
                                                csum_start, csum_end - 1,
                                                &ordered_sums, 0, false);
                        if (ret)
@@ -4443,9 +4443,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
 
                csum_root = btrfs_csum_root(trans->fs_info, disk_bytenr);
                disk_bytenr += extent_offset;
-               ret = btrfs_lookup_csums_range(csum_root, disk_bytenr,
-                                              disk_bytenr + extent_num_bytes - 1,
-                                              &ordered_sums, 0, false);
+               ret = btrfs_lookup_csums_list(csum_root, disk_bytenr,
+                                             disk_bytenr + extent_num_bytes - 1,
+                                             &ordered_sums, 0, false);
                if (ret)
                        goto out;
 
@@ -4638,10 +4638,9 @@ static int log_extent_csums(struct btrfs_trans_handle *trans,
 
        /* block start is already adjusted for the file extent offset. */
        csum_root = btrfs_csum_root(trans->fs_info, em->block_start);
-       ret = btrfs_lookup_csums_range(csum_root,
-                                      em->block_start + csum_offset,
-                                      em->block_start + csum_offset +
-                                      csum_len - 1, &ordered_sums, 0, false);
+       ret = btrfs_lookup_csums_list(csum_root, em->block_start + csum_offset,
+                                     em->block_start + csum_offset +
+                                     csum_len - 1, &ordered_sums, 0, false);
        if (ret)
                return ret;