@@ -156,6 +156,25 @@ static void btrfs_repair_done(struct btrfs_failed_bio *fbio)
}
}
+static struct folio *bio_vec_get_folio(const struct bio_vec *bv)
+{
+ return page_folio(bv->bv_page + (bv->bv_offset >> PAGE_SHIFT));
+}
+
+static unsigned long bio_vec_get_folio_offset(const struct bio_vec *bv)
+{
+ struct folio *folio = bio_vec_get_folio(bv);
+
+ /*
+ * There can be multiple physically contiguous folios queued
+ * into the bio_vec.
+ * Thus the first page of our folio should be at or beyond
+ * the first page of the bio_vec.
+ */
+ ASSERT(&folio->page >= bv->bv_page);
+ return bv->bv_offset - ((&folio->page - bv->bv_page) << PAGE_SHIFT);
+}
+
static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct btrfs_device *dev)
{
@@ -165,12 +184,6 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct bio_vec *bv = bio_first_bvec_all(&repair_bbio->bio);
int mirror = repair_bbio->mirror_num;
- /*
- * We can only trigger this for data bio, which doesn't support larger
- * folios yet.
- */
- ASSERT(folio_order(page_folio(bv->bv_page)) == 0);
-
if (repair_bbio->bio.bi_status ||
!btrfs_data_csum_ok(repair_bbio, dev, 0, bv)) {
bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ);
@@ -192,7 +205,8 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
btrfs_repair_io_failure(fs_info, btrfs_ino(inode),
repair_bbio->file_offset, fs_info->sectorsize,
repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
- page_folio(bv->bv_page), bv->bv_offset, mirror);
+ bio_vec_get_folio(bv), bio_vec_get_folio_offset(bv),
+ mirror);
} while (mirror != fbio->bbio->mirror_num);
done:
The function btrfs_end_repair_bio() has an ASSERT() making sure the folio is page sized. The reason is mostly related to the fact that later we pass a folio and its offset into btrfs_repair_io_failure(). If we have larger folios passed in, later calculation of the folio and its offset can go wrong, as we have extra offset to the bv_page. Change the behavior by: - Doing a proper folio grab Instead of just page_folio(bv_page), we should get the real page (as the bv_offset can be larger than page size), then call page_folio(). - Do extra folio offset calculation We can have the following cases of a bio_vec (this bv is moved forward by btrfs read verification): bv_page bv_offset | | | | | | | | | | | | | | | | | | | |<- folio_a ->|<- folio_b ->| | | = a page. In above case, the real folio should be folio_b, and offset inside that folio should be: bv_offset - ((&folio_b->page - &folio_a->page) << PAGE_SHIFT). With these changes, now btrfs_end_repair_bio() is able to handle larger folios properly. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/bio.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)