]> git.hungrycats.org Git - linux/commitdiff
btrfs: replace btrfs_repair_io_failure() to use bio for page iteration
authorQu Wenruo <wqu@suse.com>
Wed, 19 Aug 2026 01:06:16 +0000 (10:36 +0930)
committerDavid Sterba <dsterba@suse.com>
Mon, 14 Sep 2026 11:23:36 +0000 (13:23 +0200)
Currently btrfs_repair_io_failure() uses a @paddrs[] array to iterate
pages.

Such a parameter is required for bs > ps cases, as one fs block crosses
several pages.

However there is a much simpler and existing way to iterate pages: bio
and bvec_iter.

This changes btrfs_repair_io_failure() by:

- Use a const @bvec_iter pointer to locate where the pages are
- Extract file offset/logical from the @bbio
- Require no @step parameter
  Above features allow us to shorten the parameter list.

- Rename the function to btrfs_repair_bbio_failure()

- Change the caller in btrfs_repair_eb_io_failure() to allocate a bbio
  Unlike the data read path, we do not have a handy bbio in that case.
  So we need to allocate one just for btrfs_repair_bbio_failure().

- Change the error reporting in btrfs_repair_bbio_failure() to include
  root id and use inode number directly
  Now for btree inode we will report a proper inode number (1).

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/bio.h
fs/btrfs/disk-io.c

index cc0bd03048bae645da52a9823813c1cfb8043687..f8d4c2d550073adad5dce769db954367e26f30b5 100644 (file)
@@ -186,7 +186,6 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
         */
        struct bvec_iter saved_iter = repair_bbio->saved_iter;
        const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
-       const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT;
        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];
@@ -220,9 +219,8 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
 
        do {
                mirror = prev_repair_mirror(fbio, mirror);
-               btrfs_repair_io_failure(fs_info, btrfs_ino(inode),
-                                 repair_bbio->file_offset, fs_info->sectorsize,
-                                 logical, paddrs, step, mirror);
+               btrfs_repair_bbio_failure(repair_bbio, &repair_bbio->saved_iter,
+                                         fs_info->sectorsize, mirror);
        } while (mirror != fbio->bbio->mirror_num);
 
 done:
@@ -925,21 +923,23 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
  * The I/O is issued synchronously to block the repair read completion from
  * freeing the bio.
  *
- * @ino:       Offending inode number
- * @fileoff:   File offset inside the inode
+ * @bbio:      Original bbio where the repair is needed
+ * @orig_iter: Points to where the repair start is
  * @length:    Length of the repair write
- * @logical:   Logical address of the range
- * @paddrs:    Physical address array of the content
- * @step:      Length of for each paddrs
  * @mirror_num: Mirror number to write to. Must not be zero
  */
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
-                           u32 length, u64 logical, const phys_addr_t paddrs[],
-                           unsigned int step, int mirror_num)
+int btrfs_repair_bbio_failure(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+                             u32 length, int mirror_num)
 {
-       const u32 nr_steps = DIV_ROUND_UP_POW2(length, step);
+       struct btrfs_inode *inode = bbio->inode;
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
        struct btrfs_io_stripe smap = { 0 };
-       struct bio *bio = NULL;
+       struct bvec_iter iter = *orig_iter;
+       struct bio *repair_bio = NULL;
+       const u64 logical = iter.bi_sector << SECTOR_SHIFT;
+       const u64 fileoff = bbio->file_offset +
+                           ((iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT);
+       u32 cur = 0;
        int ret = 0;
 
        BUG_ON(!mirror_num);
@@ -950,8 +950,9 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
        ASSERT(IS_ALIGNED(fileoff, fs_info->sectorsize));
        /* Either it's a single data or metadata block. */
        ASSERT(length <= BTRFS_MAX_BLOCKSIZE);
-       ASSERT(step <= length);
-       ASSERT(is_power_of_2(step));
+
+       /* Our current iter should not be before the original bbio saved_iter. */
+       ASSERT(iter.bi_sector >= bbio->saved_iter.bi_sector);
 
        /*
         * The fs either mounted RO or hit critical errors, no need
@@ -979,15 +980,22 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
                goto out_counter_dec;
        }
 
-       bio = bio_alloc(smap.dev->bdev, nr_steps, REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
-       bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
-       for (int i = 0; i < nr_steps; i++) {
-               ret = bio_add_page(bio, phys_to_page(paddrs[i]), step, offset_in_page(paddrs[i]));
-               /* We should have allocated enough slots to contain all the different pages. */
-               ASSERT(ret == step);
+       repair_bio = bio_alloc(smap.dev->bdev, max(1, length >> PAGE_SHIFT),
+                              REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
+       repair_bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
+       while (cur < length) {
+               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), length - cur);
+
+               ret = bio_add_page(repair_bio, page, cur_len, pg_off);
+               ASSERT(ret == cur_len);
+               bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+               cur += cur_len;
        }
-       ret = submit_bio_wait(bio);
-       bio_put(bio);
+
+       ret = submit_bio_wait(repair_bio);
+       bio_put(repair_bio);
        if (ret) {
                /* try to remap that extent elsewhere? */
                btrfs_dev_stat_inc_and_print(smap.dev, BTRFS_DEV_STAT_WRITE_ERRS);
@@ -995,8 +1003,9 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
        }
 
        btrfs_info_rl(fs_info,
-               "read error corrected: ino %llu off %llu (dev %s sector %llu)",
-                            ino, fileoff, btrfs_dev_name(smap.dev),
+               "read error corrected: root %llu ino %llu off %llu (dev %s sector %llu)",
+                            btrfs_root_id(inode->root), btrfs_ino(inode), fileoff,
+                            btrfs_dev_name(smap.dev),
                             smap.physical >> SECTOR_SHIFT);
        ret = 0;
 
index 303ed6c7103d9240344848fb9430658504d061ca..b7bd377a016249fab4709f1510d7b1d6805fe28c 100644 (file)
@@ -126,8 +126,7 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status);
 
 void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num);
 void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_replace);
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
-                           u32 length, u64 logical, const phys_addr_t paddrs[],
-                           unsigned int step, int mirror_num);
+int btrfs_repair_bbio_failure(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+                             u32 length, int mirror_num);
 
 #endif
index 819727460bcf4d10601a6c8826f306f826d99e13..466fadb1815a817b81b1b0a0a95de0afab5c6316 100644 (file)
@@ -176,19 +176,24 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
                                      int mirror_num)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       const u32 step = min(fs_info->nodesize, PAGE_SIZE);
-       const u32 nr_steps = eb->len / step;
-       phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
+       struct btrfs_bio *bbio;
+       int ret;
 
        if (sb_rdonly(fs_info->sb))
                return -EROFS;
 
+       /*
+        * This bbio is only to queue all pages for btrfs_repair_bbio_failure().
+        * Thus it will never get its endio called.
+        */
+       bbio = btrfs_bio_alloc(max(1, fs_info->nodesize >> PAGE_SHIFT), REQ_OP_READ,
+                              BTRFS_I(fs_info->btree_inode), eb->start, NULL, NULL);
+       bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
        for (int i = 0; i < num_extent_pages(eb); i++) {
                struct folio *folio = eb->folios[i];
 
                /* No large folio support yet. */
                ASSERT(folio_order(folio) == 0);
-               ASSERT(i < nr_steps);
 
                /*
                 * For nodesize < page size, there is just one paddr, with some
@@ -197,11 +202,17 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
                 * For nodesize >= page size, it's one or more paddrs, and eb->start
                 * must be aligned to page boundary.
                 */
-               paddrs[i] = page_to_phys(&folio->page) + offset_in_page(eb->start);
+               ret = bio_add_page(&bbio->bio, &folio->page, min(PAGE_SIZE, fs_info->nodesize),
+                                  offset_in_page(eb->start));
+               ASSERT(ret == min(PAGE_SIZE, fs_info->nodesize));
        }
+       /* Since the bbio is never submitted, we have to save the iter manually. */
+       bbio->saved_iter = bbio->bio.bi_iter;
 
-       return btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len,
-                                      eb->start, paddrs, step, mirror_num);
+       ret = btrfs_repair_bbio_failure(bbio, &bbio->saved_iter, fs_info->nodesize,
+                                       mirror_num);
+       bio_put(&bbio->bio);
+       return ret;
 }
 
 /*