]> git.hungrycats.org Git - linux/commitdiff
btrfs: enhance btrfs_data_csum_ok() to use bio for page iteration
authorQu Wenruo <wqu@suse.com>
Wed, 19 Aug 2026 01:06:17 +0000 (10:36 +0930)
committerDavid Sterba <dsterba@suse.com>
Mon, 14 Sep 2026 11:23:36 +0000 (13:23 +0200)
Currently btrfs_data_csum_ok() requires a @paddr[] array to iterate all
possible pages for bs > ps cases.

However for all btrfs_data_csum_ok() call sites, we already have a
btrfs_bio, and the bio infrastructure has many flexible ways to iterate
multiple pages already.

Change btrfs_data_csum_ok() to make full use of btrfs_bio by:

- Change the parameter list to require a @bvec_iter pointer
  And remove @bio_offset, which can be calculated through @bvec_iter and
  bbio->saved_iter.

  Also remove paddrs[], we will iterate all the pages using bio
  interfaces.

- Make the same parameter changes to repair_one_sector()

- Use bio interfaces to iterate pages from a bio

- Rename the function to btrfs_bio_data_csum_ok()

- Remove on-stack paddrs[] array usage

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/bio.c
fs/btrfs/btrfs_inode.h
fs/btrfs/inode.c

index f8d4c2d550073adad5dce769db954367e26f30b5..19b4855969f53611411b7fbdeb02a6c9cd082114 100644 (file)
@@ -180,29 +180,13 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
        struct btrfs_failed_bio *fbio = repair_bbio->private;
        struct btrfs_inode *inode = repair_bbio->inode;
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
-       /*
-        * We can not move forward the saved_iter, as it will be later
-        * utilized by repair_bbio again.
-        */
-       struct bvec_iter saved_iter = repair_bbio->saved_iter;
-       const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
-       const u32 nr_steps = repair_bbio->saved_iter.bi_size / step;
        int mirror = repair_bbio->mirror_num;
-       phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
-       phys_addr_t paddr;
-       unsigned int slot = 0;
 
-       /* Repair bbio should be eaxctly one block sized. */
+       /* Repair bbio should be exactly one block sized. */
        ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize);
 
-       btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) {
-               ASSERT(slot < nr_steps);
-               paddrs[slot] = paddr;
-               slot++;
-       }
-
        if (repair_bbio->bio.bi_status ||
-           !btrfs_data_csum_ok(repair_bbio, dev, 0, paddrs)) {
+           !btrfs_bio_data_csum_ok(repair_bbio, &repair_bbio->saved_iter, dev)) {
                bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ);
                repair_bbio->bio.bi_iter = repair_bbio->saved_iter;
 
@@ -236,25 +220,21 @@ done:
  * read succeeded to restore the redundancy.
  */
 static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
-                                                 u32 bio_offset,
-                                                 phys_addr_t paddrs[],
+                                                 const struct bvec_iter *orig_iter,
                                                  struct btrfs_failed_bio *fbio)
 {
        struct btrfs_inode *inode = failed_bbio->inode;
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
-       const u32 sectorsize = fs_info->sectorsize;
-       const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
-       const u32 nr_steps = sectorsize / step;
-       /*
-        * For bs > ps cases, the saved_iter can be partially moved forward.
-        * In that case we should round it down to the block boundary.
-        */
-       const u64 logical = round_down(failed_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
-                                      sectorsize);
        struct btrfs_bio *repair_bbio;
        struct bio *repair_bio;
+       struct bvec_iter iter = *orig_iter;
+       const u32 sectorsize = fs_info->sectorsize;
+       const u32 bio_offset = ((iter.bi_sector - failed_bbio->saved_iter.bi_sector) <<
+                               SECTOR_SHIFT);
+       const u64 logical = (iter.bi_sector << SECTOR_SHIFT);
        int num_copies;
        int mirror;
+       u32 cur = 0;
 
        btrfs_debug(fs_info, "repair read error: read error at %llu",
                    failed_bbio->file_offset + bio_offset);
@@ -275,17 +255,21 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
 
        atomic_inc(&fbio->repair_count);
 
-       repair_bio = bio_alloc_bioset(NULL, nr_steps, REQ_OP_READ, GFP_NOFS,
-                                     &btrfs_repair_bioset);
+       repair_bio = bio_alloc_bioset(NULL, max(1, sectorsize >> PAGE_SHIFT),
+                                     REQ_OP_READ, GFP_NOFS, &btrfs_repair_bioset);
        repair_bio->bi_iter.bi_sector = logical >> SECTOR_SHIFT;
-       for (int i = 0; i < nr_steps; i++) {
+       while (cur < sectorsize) {
+               struct page *page = bio_iter_page(&failed_bbio->bio, iter);
+               const u32 pg_off = bio_iter_offset(&failed_bbio->bio, iter);
+               const u32 cur_len = min(bio_iter_len(&failed_bbio->bio, iter),
+                                       sectorsize - cur);
                int ret;
 
-               ASSERT(offset_in_page(paddrs[i]) + step <= PAGE_SIZE);
+               ret = bio_add_page(repair_bio, page, cur_len, pg_off);
+               ASSERT(ret == cur_len);
 
-               ret = bio_add_page(repair_bio, phys_to_page(paddrs[i]), step,
-                                  offset_in_page(paddrs[i]));
-               ASSERT(ret == step);
+               bio_advance_iter_single(&failed_bbio->bio, &iter, cur_len);
+               cur += cur_len;
        }
 
        repair_bbio = btrfs_bio(repair_bio);
@@ -303,18 +287,16 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
        struct btrfs_inode *inode = bbio->inode;
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
        const u32 sectorsize = fs_info->sectorsize;
-       const u32 step = min(sectorsize, PAGE_SIZE);
-       const u32 nr_steps = sectorsize / step;
-       struct bvec_iter *iter = &bbio->saved_iter;
+       struct bvec_iter iter;
        blk_status_t status = bbio->bio.bi_status;
        struct btrfs_failed_bio *fbio = NULL;
-       phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
-       phys_addr_t paddr;
-       u32 offset = 0;
 
        /* Read-repair requires the inode field to be set by the submitter. */
        ASSERT(inode);
 
+       /* The original bbio should be sectorsize aligned. */
+       ASSERT(IS_ALIGNED(bbio->saved_iter.bi_size, sectorsize));
+
        /*
         * Hand off repair bios to the repair code as there is no upper level
         * submitter for them.
@@ -327,16 +309,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
        /* Clear the I/O error. A failed repair will reset it. */
        bbio->bio.bi_status = BLK_STS_OK;
 
-       btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) {
-               paddrs[(offset / step) % nr_steps] = paddr;
-               offset += step;
-
-               if (IS_ALIGNED(offset, sectorsize)) {
-                       if (status ||
-                           !btrfs_data_csum_ok(bbio, dev, offset - sectorsize, paddrs))
-                               fbio = repair_one_sector(bbio, offset - sectorsize,
-                                                        paddrs, fbio);
-               }
+       for (iter = bbio->saved_iter; iter.bi_size;
+            bio_advance_iter(&bbio->bio, &iter, sectorsize)) {
+               if (status || !btrfs_bio_data_csum_ok(bbio, &iter, dev))
+                       fbio = repair_one_sector(bbio, &iter, fbio);
        }
        if (bbio->csum != bbio->csum_inline)
                kvfree(bbio->csum);
@@ -924,7 +900,7 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
  * freeing the bio.
  *
  * @bbio:      Original bbio where the repair is needed
- * @orig_iter: Points to where the repair start is
+ * @orig_iter: Points to where the repair starts
  * @length:    Length of the repair write
  * @mirror_num: Mirror number to write to. Must not be zero
  */
index 1082fa92c1457a5618d33b3410c25cb882e704fe..171f96bdb8aa76110df3eb52f81a4b013bcdb424 100644 (file)
@@ -513,8 +513,8 @@ void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
                                      const phys_addr_t paddrs[], u8 *dest);
 int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
                           const u8 * const csum_expected);
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
-                       u32 bio_offset, const phys_addr_t paddrs[]);
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+                           struct btrfs_device *dev);
 noinline int can_nocow_extent(struct btrfs_inode *inode, u64 offset, u64 *len,
                              struct btrfs_file_extent *file_extent,
                              bool nowait);
index 93ef3cec191e3609205462cb3c560aea43ec0771..005f8f9da8b139f34428685f015e82bee598e316 100644 (file)
@@ -3536,27 +3536,31 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
  * different noncontiguous pages.
  *
  * @bbio:      btrfs_io_bio which contains the csum
- * @dev:       device the sector is on
- * @bio_offset:        offset to the beginning of the bio (in bytes)
- * @paddrs:    physical addresses which back the fs block
+ * @orig_iter: bvec iter pointing to the start of the block
+ * @dev:       device the sector is on (optional)
  *
  * Check if the checksum on a data block is valid.  When a checksum mismatch is
  * detected, report the error and fill the corrupted range with zero.
  *
  * Return %true if the sector is ok or had no checksum to start with, else %false.
  */
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
-                       u32 bio_offset, const phys_addr_t paddrs[])
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
+                           const struct bvec_iter *orig_iter,
+                           struct btrfs_device *dev)
 {
        struct btrfs_inode *inode = bbio->inode;
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
+       struct bvec_iter iter = *orig_iter;
+       struct btrfs_csum_ctx cctx;
        const u32 blocksize = fs_info->sectorsize;
-       const u32 step = min(blocksize, PAGE_SIZE);
-       const u32 nr_steps = blocksize / step;
+       const u32 bio_offset = (iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT;
        u64 file_offset = bbio->file_offset + bio_offset;
        u64 end = file_offset + blocksize - 1;
        u8 *csum_expected;
        u8 csum[BTRFS_CSUM_SIZE];
+       u32 cur = 0;
+
+       ASSERT(iter.bi_sector >= bbio->saved_iter.bi_sector);
 
        if (!bbio->csum)
                return true;
@@ -3572,7 +3576,22 @@ bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
 
        csum_expected = bbio->csum + (bio_offset >> fs_info->sectorsize_bits) *
                                fs_info->csum_size;
-       btrfs_calculate_block_csum_pages(fs_info, paddrs, csum);
+       btrfs_csum_init(&cctx, fs_info->csum_type);
+       while (cur < blocksize) {
+               struct page *page = bio_iter_page(&bbio->bio, iter);
+               const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+               const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
+               void *kaddr;
+
+               kaddr = kmap_local_page(page) + pg_off;
+               btrfs_csum_update(&cctx, kaddr, cur_len);
+               kunmap_local(kaddr);
+
+               bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+               cur += cur_len;
+       }
+       btrfs_csum_final(&cctx, csum);
+
        if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
                goto zeroit;
        return true;
@@ -3582,8 +3601,18 @@ zeroit:
                                    bbio->mirror_num);
        if (dev)
                btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
-       for (int i = 0; i < nr_steps; i++)
-               memzero_page(phys_to_page(paddrs[i]), offset_in_page(paddrs[i]), step);
+       cur = 0;
+       iter = *orig_iter;
+       while (cur < blocksize) {
+               struct page *page = bio_iter_page(&bbio->bio, iter);
+               const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+               const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
+
+               memzero_page(page, pg_off, cur_len);
+
+               bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+               cur += cur_len;
+       }
        return false;
 }