diff options
| author | Jens Axboe <axboe@kernel.dk> | 2026-09-14 07:42:36 -0600 |
|---|---|---|
| committer | Jens Axboe <axboe@kernel.dk> | 2026-09-14 07:42:36 -0600 |
| commit | 1f93fed16929648f427b881d3682bc87ee733d2e (patch) | |
| tree | 270b66bb9061d8e6bb4cbb811a7c117b71c50b6a | |
| parent | 6b84fc811f3dd2c40153233503112dda908a2b21 (diff) | |
| parent | a6c30409d10099fe4b744ae89d22c44789a3dae7 (diff) | |
| download | linux-next-1f93fed16929648f427b881d3682bc87ee733d2e.tar.gz linux-next-1f93fed16929648f427b881d3682bc87ee733d2e.zip | |
Merge branch 'for-7.4/lazy-bounce-buffering' into for-next
* for-7.4/lazy-bounce-buffering:
block,iomap: remove the old read side bounce buffering support
xfs: log a message at mount time when using integrity protection
xfs: add error injection for lazy bounce buffering
xfs: add support for lazy direct read bounce buffering
iomap,xfs: move integrity verification to the file system
xfs: use BIO_COMPLETE_IN_TASK for bounce buffered read I/Os
iomap: better read bounce buffering support
block,iomap: fix protection information verification with initial bvec offset
xfs: move PI generation into xfs_submit_zoned_bio
iomap,xfs: move T10 PI handling for direct I/O into ->submit_io
iomap: add a IOMAP_IOEND_INTEGRITY flag
iomap: add a iomap_ioend_flags helper
iomap: respect maximum I/O size in iomap_dio_bio_iter_one
block: warn on too larger integrity allocations
block: pass a maxlen argument to bio_iov_iter_get_pages
block: add a bio_prepare_reissue helper
block: export fs_bio_integrity_{alloc,free}
block: split bio_iov_iter_bounce_write
| -rw-r--r-- | block/bio-integrity-fs.c | 15 | ||||
| -rw-r--r-- | block/bio-integrity.c | 1 | ||||
| -rw-r--r-- | block/bio.c | 210 | ||||
| -rw-r--r-- | block/blk-map.c | 2 | ||||
| -rw-r--r-- | block/fops.c | 3 | ||||
| -rw-r--r-- | fs/iomap/bio.c | 4 | ||||
| -rw-r--r-- | fs/iomap/direct-io.c | 44 | ||||
| -rw-r--r-- | fs/iomap/ioend.c | 120 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_errortag.h | 6 | ||||
| -rw-r--r-- | fs/xfs/xfs_aops.c | 13 | ||||
| -rw-r--r-- | fs/xfs/xfs_buf.c | 11 | ||||
| -rw-r--r-- | fs/xfs/xfs_file.c | 11 | ||||
| -rw-r--r-- | fs/xfs/xfs_ioend.c | 137 | ||||
| -rw-r--r-- | fs/xfs/xfs_ioend.h | 2 | ||||
| -rw-r--r-- | fs/xfs/xfs_mount.h | 8 | ||||
| -rw-r--r-- | fs/xfs/xfs_super.c | 1 | ||||
| -rw-r--r-- | fs/xfs/xfs_sysfs.c | 78 | ||||
| -rw-r--r-- | fs/xfs/xfs_trace.h | 1 | ||||
| -rw-r--r-- | fs/xfs/xfs_zone_alloc.c | 4 | ||||
| -rw-r--r-- | include/linux/bio-integrity.h | 3 | ||||
| -rw-r--r-- | include/linux/bio.h | 11 | ||||
| -rw-r--r-- | include/linux/iomap.h | 38 |
22 files changed, 498 insertions, 225 deletions
diff --git a/block/bio-integrity-fs.c b/block/bio-integrity-fs.c index 692403dfa047..c8e91ada8ca6 100644 --- a/block/bio-integrity-fs.c +++ b/block/bio-integrity-fs.c @@ -31,6 +31,7 @@ unsigned int fs_bio_integrity_alloc(struct bio *bio) bio_integrity_setup_default(bio); return action; } +EXPORT_SYMBOL_GPL(fs_bio_integrity_alloc); void fs_bio_integrity_free(struct bio *bio) { @@ -43,6 +44,7 @@ void fs_bio_integrity_free(struct bio *bio) bio->bi_integrity = NULL; bio->bi_opf &= ~REQ_INTEGRITY; } +EXPORT_SYMBOL_GPL(fs_bio_integrity_free); void fs_bio_integrity_generate(struct bio *bio) { @@ -52,14 +54,10 @@ void fs_bio_integrity_generate(struct bio *bio) } EXPORT_SYMBOL_GPL(fs_bio_integrity_generate); -int fs_bio_integrity_verify(struct bio *bio, sector_t sector, unsigned int size) +int fs_bio_integrity_verify(struct bio *bio, struct bvec_iter *data_iter) { struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk); struct bio_integrity_payload *bip = bio_integrity(bio); - struct bvec_iter data_iter = { - .bi_sector = sector, - .bi_size = size, - }; if (!bip || !(bip->bip_flags & BIP_CHECK_FLAGS)) return 0; @@ -71,9 +69,10 @@ int fs_bio_integrity_verify(struct bio *bio, sector_t sector, unsigned int size) * bio. Requires the submitter to remember the sector and the size. */ memset(&bip->bip_iter, 0, sizeof(bip->bip_iter)); - bip->bip_iter.bi_sector = sector; - bip->bip_iter.bi_size = bio_integrity_bytes(bi, size >> SECTOR_SHIFT); - return blk_status_to_errno(bio_integrity_verify(bio, &data_iter)); + bip->bip_iter.bi_sector = data_iter->bi_sector; + bip->bip_iter.bi_size = + bio_integrity_bytes(bi, data_iter->bi_size >> SECTOR_SHIFT); + return blk_status_to_errno(bio_integrity_verify(bio, data_iter)); } static int __init fs_bio_integrity_init(void) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index b23e2434d80c..d3df726e0f08 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -72,6 +72,7 @@ void bio_integrity_alloc_buf(struct bio *bio, gfp_t gfp, bool zero_buffer) unsigned int len = bio_integrity_bytes(bi, bio_sectors(bio)); void *buf; + WARN_ON_ONCE(len > BLK_INTEGRITY_MAX_SIZE); buf = kmalloc(len, gfp | __GFP_NOWARN | (zero_buffer ? __GFP_ZERO : 0)); if (unlikely(!buf)) { struct page *page; diff --git a/block/bio.c b/block/bio.c index 57ee335899f3..b48091c7663f 100644 --- a/block/bio.c +++ b/block/bio.c @@ -320,6 +320,26 @@ void bio_reuse(struct bio *bio, blk_opf_t opf) } EXPORT_SYMBOL_GPL(bio_reuse); +/** + * bio_prepare_reissue - prepare a bio for reuissing the original I/O + * @bio: bio to reuse + * @bdev: block device to use the bio for + * + * Prepare @bio to be resubmitted to retry the original operation. + * The caller must reset bio->bi_iter to the original state. + */ +void bio_prepare_reissue(struct bio *bio, struct block_device *bdev) +{ + bio->bi_bdev = bdev; + bio_associate_blkg(bio); + bio->bi_flags &= + (BIO_PAGE_PINNED | BIO_CLONED | BIO_QUIET | BIO_REFFED); + bio->bi_status = BLK_STS_OK; + bio->bi_bvec_gap_bit = 0; + atomic_set(&bio->__bi_remaining, 1); +} +EXPORT_SYMBOL_GPL(bio_prepare_reissue); + static struct bio *__bio_chain_endio(struct bio *bio) { struct bio *parent = bio->bi_private; @@ -1203,8 +1223,9 @@ bool bio_iov_iter_set(struct bio *bio, const struct iov_iter *iter) * for the next iteration. */ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter, - struct bio_vec *bv, unsigned len_align_mask) + unsigned len_align_mask) { + struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; size_t nbytes = bio->bi_iter.bi_size & len_align_mask; if (!nbytes) @@ -1262,6 +1283,7 @@ static inline bool bio_iov_bvec_aligned(const struct bio *bio, * bio_iov_iter_get_pages - add user or kernel pages to a bio * @bio: bio to add pages to * @iter: iov iterator describing the region to be added + * @maxlen: maximum size to consume from @iter * @mem_align_mask: the mask the source address and length must be aligned to, * 0 for no requirement * @len_align_mask: the mask to align the total size to, 0 for any length @@ -1282,7 +1304,8 @@ static inline bool bio_iov_bvec_aligned(const struct bio *bio, * is returned only if 0 pages could be pinned. */ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, - unsigned mem_align_mask, unsigned len_align_mask) + unsigned maxlen, unsigned mem_align_mask, + unsigned len_align_mask) { iov_iter_extraction_t flags = 0; @@ -1294,6 +1317,8 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, !bio_iov_bvec_aligned(bio, mem_align_mask)) return -EINVAL; + /* Truncate to the maximum size that the caller can handle */ + bio->bi_iter.bi_size = min(bio->bi_iter.bi_size, maxlen); iov_iter_advance(iter, bio->bi_iter.bi_size); return 0; } @@ -1307,7 +1332,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, ssize_t ret; ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec, - BIO_MAX_SIZE - bio->bi_iter.bi_size, + maxlen - bio->bi_iter.bi_size, &bio->bi_vcnt, bio->bi_max_vecs, mem_align_mask, flags); if (ret <= 0) { @@ -1330,8 +1355,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, if (is_pci_p2pdma_page(bio->bi_io_vec->bv_page)) bio->bi_opf |= REQ_NOMERGE; - return bio_iov_iter_align_down(bio, iter, - &bio->bi_io_vec[bio->bi_vcnt - 1], len_align_mask); + return bio_iov_iter_align_down(bio, iter, len_align_mask); } static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size, @@ -1350,7 +1374,7 @@ static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size, return folio_alloc(gfp, get_order(*size)); } -static void bio_free_folios(struct bio *bio) +void bio_free_folios(struct bio *bio) { struct bio_vec *bv; int i; @@ -1363,11 +1387,8 @@ static void bio_free_folios(struct bio *bio) } } -static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, - size_t maxlen, size_t minsize) +int bio_alloc_bounce_folios(struct bio *bio, size_t total_len, size_t minsize) { - size_t total_len = min(maxlen, iov_iter_count(iter)); - if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) return -EINVAL; if (WARN_ON_ONCE(bio->bi_iter.bi_size)) @@ -1377,7 +1398,6 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, do { size_t this_len = min(total_len, SZ_1M); - size_t copied; struct folio *folio; if (this_len > minsize * 2) @@ -1398,27 +1418,6 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, */ this_len &= ~(minsize - 1); bio_add_folio_nofail(bio, folio, this_len, 0); - - if (iter->nofault) - copied = copy_folio_from_iter_atomic(folio, 0, this_len, - iter); - else - copied = copy_folio_from_iter(folio, 0, this_len, iter); - if (copied < this_len) { - /* - * Need to revert the iov iter for all bytes we have - * copied. - * - * However the bio size differs from the real copied - * bytes as @this_len is queued but only advanced - * less than that. - * Need to compensate that for the revert. - */ - iov_iter_revert(iter, bio->bi_iter.bi_size - this_len + - copied); - bio_free_folios(bio); - return -EFAULT; - } total_len -= this_len; } while (total_len && bio->bi_vcnt < bio->bi_max_vecs); @@ -1427,133 +1426,52 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, return 0; } -static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, - size_t maxlen, size_t minsize) -{ - size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M); - struct folio *folio; - ssize_t ret; - - folio = folio_alloc_greedy(GFP_KERNEL, &len, minsize); - if (!folio) - return -ENOMEM; - - do { - ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len, - &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0); - if (ret <= 0) { - if (!bio->bi_vcnt) - goto out_folio_put; - break; - } - len -= ret; - bio->bi_iter.bi_size += ret; - } while (len && bio->bi_vcnt < bio->bi_max_vecs - 1); - - /* - * Set the folio directly here. The above loop has already calculated - * the correct bi_size, and we use bi_vcnt for the user buffers. That - * is safe as bi_vcnt is only used by the submitter and not the actual - * I/O path. - */ - bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0); - if (iov_iter_extract_will_pin(iter)) - bio_set_flag(bio, BIO_PAGE_PINNED); - - /* The first vec stores the bounce buffer, so do not subtract 1 here. */ - ret = bio_iov_iter_align_down(bio, iter, - &bio->bi_io_vec[bio->bi_vcnt], minsize - 1); - if (ret) - goto out_folio_put; - - /* Update the bounc buffer bv_len to the aligned down size. */ - bio->bi_io_vec[0].bv_len = bio->bi_iter.bi_size; - return 0; - -out_folio_put: - folio_put(folio); - return ret; -} - /** - * bio_iov_iter_bounce - bounce buffer data from an iter into a bio + * bio_iov_iter_bounce_write - bounce buffer data from an iter into a bio * @bio: bio to send - * @iter: iter to read from / write into + * @iter: iter to read from * @maxlen: maximum size to bounce * @minsize: minimum folio allocation size * - * Helper for direct I/O implementations that need to bounce buffer because - * we need to checksum the data or perform other operations that require - * consistency. Allocates folios to back the bounce buffer, and for writes - * copies the data into it. Needs to be paired with bio_iov_iter_unbounce() - * called on completion. + * Helper for direct I/O write implementations that need to bounce buffer + * because they need need to checksum the data or perform other operations that + * require consistency. Allocates folios to back the bounce buffer, and copies + * the data into it. Needs to be paired with bio_free_folios() called on + * completion. */ -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen, - size_t minsize) -{ - if (op_is_write(bio_op(bio))) - return bio_iov_iter_bounce_write(bio, iter, maxlen, minsize); - return bio_iov_iter_bounce_read(bio, iter, maxlen, minsize); -} - -static void bvec_unpin(struct bio_vec *bv, bool mark_dirty) -{ - struct folio *folio = bvec_folio(bv); - size_t nr_pages = (bv->bv_offset + bv->bv_len - 1) / PAGE_SIZE - - bv->bv_offset / PAGE_SIZE + 1; - - if (mark_dirty) - folio_mark_dirty_lock(folio); - unpin_user_folio(folio, nr_pages); -} - -static void bio_iov_iter_unbounce_read(struct bio *bio, bool is_error, - bool mark_dirty) +int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, + size_t maxlen, size_t minsize) { - unsigned int len = bio->bi_io_vec[0].bv_len; - - if (likely(!is_error)) { - void *buf = bvec_virt(&bio->bi_io_vec[0]); - struct iov_iter to; + size_t total_len = min(maxlen, iov_iter_count(iter)); + size_t total_copied = 0; + struct bio_vec *bv; + int i, error; - iov_iter_bvec(&to, ITER_DEST, bio->bi_io_vec + 1, bio->bi_vcnt, - len); - /* copying to pinned pages should always work */ - WARN_ON_ONCE(copy_to_iter(buf, len, &to) != len); - } else { - /* No need to mark folios dirty if never copied to them */ - mark_dirty = false; - } + error = bio_alloc_bounce_folios(bio, total_len, minsize); + if (error) + return error; - if (bio_flagged(bio, BIO_PAGE_PINNED)) { - int i; + bio_for_each_bvec_all(bv, bio, i) { + struct folio *folio = page_folio(bv->bv_page); + size_t copied; - for (i = 0; i < bio->bi_vcnt; i++) - bvec_unpin(&bio->bi_io_vec[1 + i], mark_dirty); + if (iter->nofault) + copied = copy_folio_from_iter_atomic(folio, 0, + bv->bv_len, iter); + else + copied = copy_folio_from_iter(folio, 0, bv->bv_len, + iter); + total_copied += copied; + if (copied < bv->bv_len) { + iov_iter_revert(iter, total_copied); + bio_free_folios(bio); + return -EFAULT; + } } - folio_put(bvec_folio(&bio->bi_io_vec[0])); -} - -/** - * bio_iov_iter_unbounce - finish a bounce buffer operation - * @bio: completed bio - * @is_error: %true if an I/O error occurred and data should not be copied - * @mark_dirty: If %true, folios will be marked dirty. - * - * Helper for direct I/O implementations that need to bounce buffer because - * we need to checksum the data or perform other operations that require - * consistency. Called to complete a bio set up by bio_iov_iter_bounce(). - * Copies data back for reads, and marks the original folios dirty if - * requested and then frees the bounce buffer. - */ -void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty) -{ - if (op_is_write(bio_op(bio))) - bio_free_folios(bio); - else - bio_iov_iter_unbounce_read(bio, is_error, mark_dirty); + return 0; } +EXPORT_SYMBOL_GPL(bio_iov_iter_bounce_write); static void bio_wait_end_io(struct bio *bio) { diff --git a/block/blk-map.c b/block/blk-map.c index 9cb9605d1f62..81cba3af4e9c 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -274,7 +274,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, * No alignment requirements on our part to support arbitrary * passthrough commands. */ - ret = bio_iov_iter_get_pages(bio, iter, 0, 0); + ret = bio_iov_iter_get_pages(bio, iter, BIO_MAX_SIZE, 0, 0); if (ret) goto out_put; ret = blk_rq_append_bio(rq, bio); diff --git a/block/fops.c b/block/fops.c index b917bc0f6b44..b182d0b30748 100644 --- a/block/fops.c +++ b/block/fops.c @@ -60,7 +60,8 @@ static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb, static inline int blkdev_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, struct block_device *bdev) { - return bio_iov_iter_get_pages(bio, iter, bdev_dma_alignment(bdev), + return bio_iov_iter_get_pages(bio, iter, BIO_MAX_SIZE, + bdev_dma_alignment(bdev), bdev_logical_block_size(bdev) - 1); } diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 48100c614431..d46c2f8ea18c 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -169,6 +169,7 @@ int iomap_bio_read_folio_range_sync(const struct iomap_iter *iter, { const struct iomap *srcmap = iomap_iter_srcmap(iter); sector_t sector = iomap_sector(srcmap, pos); + struct bvec_iter saved_iter; struct bio_vec bvec; struct bio bio; int error; @@ -178,10 +179,11 @@ int iomap_bio_read_folio_range_sync(const struct iomap_iter *iter, bio_add_folio_nofail(&bio, folio, len, offset_in_folio(folio, pos)); if (srcmap->flags & IOMAP_F_INTEGRITY) fs_bio_integrity_alloc(&bio); + saved_iter = bio.bi_iter; error = submit_bio_wait(&bio); if (bio_integrity(&bio)) { if (!error) - error = fs_bio_integrity_verify(&bio, sector, len); + error = fs_bio_integrity_verify(&bio, &saved_iter); fs_bio_integrity_free(&bio); } bio_uninit(&bio); diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 8b4039d16ce8..41fdc90a9094 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -76,10 +76,19 @@ static void iomap_dio_submit_bio(const struct iomap_iter *iter, if (dio->dops && dio->dops->submit_io) { dio->dops->submit_io(iter, bio, pos); - } else { - WARN_ON_ONCE(iter->iomap.flags & IOMAP_F_ANON_WRITE); - blk_crypto_submit_bio(bio); + return; + } + + WARN_ON_ONCE(iter->iomap.flags & IOMAP_F_ANON_WRITE); + + if (iter->iomap.flags & IOMAP_F_INTEGRITY) { + if (dio->flags & IOMAP_DIO_WRITE) + fs_bio_integrity_generate(bio); + else + fs_bio_integrity_alloc(bio); } + + blk_crypto_submit_bio(bio); } static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio) @@ -246,8 +255,7 @@ static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion) fs_bio_integrity_free(bio); if (dio->flags & IOMAP_DIO_BOUNCE) { - bio_iov_iter_unbounce(bio, !!dio->error, - dio->flags & IOMAP_DIO_USER_BACKED); + bio_free_folios(bio); bio_put(bio); } else if (dio->flags & IOMAP_DIO_USER_BACKED) { bio_check_pages_dirty(bio); @@ -336,6 +344,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, struct iomap_dio *dio, loff_t pos, unsigned int alignment, blk_opf_t op) { + unsigned int maxsize = iomap_max_bio_size(&iter->iomap); unsigned int nr_vecs; struct bio *bio; ssize_t ret; @@ -353,14 +362,12 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, bio->bi_private = dio; bio->bi_end_io = iomap_dio_bio_end_io; - if (dio->flags & IOMAP_DIO_BOUNCE) - ret = bio_iov_iter_bounce(bio, dio->submit.iter, - iomap_max_bio_size(&iter->iomap), alignment); + ret = bio_iov_iter_bounce_write(bio, dio->submit.iter, maxsize, + alignment); else - ret = bio_iov_iter_get_pages(bio, dio->submit.iter, - bdev_dma_alignment(bio->bi_bdev), - alignment - 1); + ret = bio_iov_iter_get_pages(bio, dio->submit.iter, maxsize, + bdev_dma_alignment(bio->bi_bdev), alignment - 1); if (unlikely(ret)) goto out_put_bio; ret = bio->bi_iter.bi_size; @@ -374,13 +381,6 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, goto out_bio_release_pages; } - if (iter->iomap.flags & IOMAP_F_INTEGRITY) { - if (dio->flags & IOMAP_DIO_WRITE) - fs_bio_integrity_generate(bio); - else - fs_bio_integrity_alloc(bio); - } - if (dio->flags & IOMAP_DIO_WRITE) task_io_account_write(ret); else if ((dio->flags & IOMAP_DIO_USER_BACKED) && @@ -397,7 +397,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, out_bio_release_pages: if (dio->flags & IOMAP_DIO_BOUNCE) - bio_iov_iter_unbounce(bio, true, false); + bio_free_folios(bio); else bio_release_pages(bio, false); out_put_bio: @@ -1034,9 +1034,9 @@ ssize_t __iomap_dio_read_simple(struct kiocb *iocb, struct iov_iter *iter, bio->bi_iter.bi_sector = iomap_sector(&iomi->iomap, iomi->pos); bio->bi_ioprio = iocb->ki_ioprio; - ret = bio_iov_iter_get_pages(bio, iter, - bdev_dma_alignment(bio->bi_bdev), - alignment - 1); + ret = bio_iov_iter_get_pages(bio, iter, BIO_MAX_SIZE, + bdev_dma_alignment(bio->bi_bdev), + alignment - 1); if (unlikely(ret)) goto out_bio_put; diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 7bbbb417f915..bbebecc31670 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -25,6 +25,7 @@ struct iomap_ioend *iomap_init_ioend(struct inode *inode, ioend->io_parent = NULL; INIT_LIST_HEAD(&ioend->io_list); ioend->io_flags = ioend_flags; + ioend->io_bvec_offset = bio->bi_iter.bi_offset; ioend->io_inode = inode; ioend->io_offset = file_offset; ioend->io_size = bio->bi_iter.bi_size; @@ -149,7 +150,7 @@ int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error) return error; } - if (wpc->iomap.flags & IOMAP_F_INTEGRITY) + if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) fs_bio_integrity_generate(&ioend->io_bio); submit_bio(&ioend->io_bio); return 0; @@ -215,7 +216,7 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, { struct iomap_ioend *ioend = wpc->wb_ctx; size_t poff = offset_in_folio(folio, pos); - unsigned int ioend_flags = 0; + unsigned int ioend_flags = iomap_ioend_flags(&wpc->iomap); unsigned int map_len = min_t(u64, dirty_len, wpc->iomap.offset + wpc->iomap.length - pos); int error; @@ -225,20 +226,16 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, WARN_ON_ONCE(!folio->private && map_len < dirty_len); switch (wpc->iomap.type) { + case IOMAP_HOLE: + return map_len; case IOMAP_UNWRITTEN: - ioend_flags |= IOMAP_IOEND_UNWRITTEN; - break; case IOMAP_MAPPED: break; - case IOMAP_HOLE: - return map_len; default: WARN_ON_ONCE(1); return -EIO; } - if (wpc->iomap.flags & IOMAP_F_SHARED) - ioend_flags |= IOMAP_IOEND_SHARED; if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY)) ioend_flags |= IOMAP_IOEND_BOUNDARY; @@ -312,6 +309,16 @@ new_ioend: } EXPORT_SYMBOL_GPL(iomap_add_to_ioend); +#ifdef CONFIG_BLK_DEV_INTEGRITY +int iomap_ioend_integrity_verify(struct iomap_ioend *ioend) +{ + struct bvec_iter data_iter = BVEC_ITER_IOEND(ioend); + + return fs_bio_integrity_verify(&ioend->io_bio, &data_iter); +} +EXPORT_SYMBOL_GPL(iomap_ioend_integrity_verify); +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + static u32 iomap_finish_ioend(struct iomap_ioend *ioend, int error) { if (ioend->io_parent) { @@ -327,13 +334,6 @@ static u32 iomap_finish_ioend(struct iomap_ioend *ioend, int error) if (!atomic_dec_and_test(&ioend->io_remaining)) return 0; - if (!ioend->io_error && - bio_integrity(&ioend->io_bio) && - bio_op(&ioend->io_bio) == REQ_OP_READ) { - ioend->io_error = fs_bio_integrity_verify(&ioend->io_bio, - ioend->io_sector, ioend->io_size); - } - if (ioend->io_flags & IOMAP_IOEND_DIRECT) return iomap_finish_ioend_direct(ioend); if (bio_op(&ioend->io_bio) == REQ_OP_READ) @@ -512,6 +512,96 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, } EXPORT_SYMBOL_GPL(iomap_split_ioend); +void iomap_bounce_read(struct iomap_ioend *orig_ioend, unsigned int minsize, + void (*submit_ioend)(struct iomap_ioend *ioend)) +{ + struct inode *inode = orig_ioend->io_inode; + struct bio *orig_bio = &orig_ioend->io_bio; + loff_t file_offset = orig_ioend->io_offset; + sector_t sector = orig_ioend->io_sector; + size_t total_len = round_up(orig_ioend->io_size, minsize); + + WARN_ON_ONCE(!(orig_ioend->io_flags & IOMAP_IOEND_DIRECT)); + + /* We can't poll a bio that is not passed on to hardware */ + orig_bio->bi_opf &= ~REQ_POLLED; + + do { + struct iomap_ioend *ioend; + struct bio *bio; + int error; + + bio = bio_alloc_bioset(orig_bio->bi_bdev, + min(total_len / minsize, BIO_MAX_VECS), + orig_bio->bi_opf, GFP_KERNEL, + &iomap_ioend_split_bioset); + error = bio_alloc_bounce_folios(bio, total_len, minsize); + if (error) { + bio_put(bio); + orig_bio->bi_status = errno_to_blk_status(error); + break; + } + bio->bi_ioprio = orig_bio->bi_ioprio; + bio->bi_write_hint = orig_bio->bi_write_hint; + bio->bi_write_stream = orig_bio->bi_write_stream; + bio->bi_iter.bi_sector = sector; + + ioend = iomap_init_ioend(inode, bio, file_offset, + orig_ioend->io_flags); + + total_len -= bio->bi_iter.bi_size; + file_offset += bio->bi_iter.bi_size; + sector += (bio->bi_iter.bi_size >> SECTOR_SHIFT); + + bio->bi_private = orig_bio; + bio_inc_remaining(orig_bio); + submit_ioend(ioend); + } while (total_len > 0); + + bio_endio(&orig_ioend->io_bio); +} +EXPORT_SYMBOL_GPL(iomap_bounce_read); + +static void iomap_ioend_unbounce(struct iomap_ioend *orig_ioend, + struct iomap_ioend *ioend) +{ + struct bio *orig_bio = &orig_ioend->io_bio; + struct iov_iter to; + struct bio_vec *bv; + int i; + + iov_iter_bvec(&to, ITER_DEST, orig_bio->bi_io_vec, orig_bio->bi_vcnt, + orig_ioend->io_size); + to.iov_offset = orig_ioend->io_bvec_offset; + + if (ioend->io_offset != orig_ioend->io_offset) { + WARN_ON_ONCE(ioend->io_offset < orig_ioend->io_offset); + iov_iter_advance(&to, ioend->io_offset - orig_ioend->io_offset); + } + + /* copying to pinned pages should always work */ + bio_for_each_bvec_all(bv, &ioend->io_bio, i) + WARN_ON_ONCE(copy_to_iter(bvec_virt(bv), bv->bv_len, &to) != + bv->bv_len); +} + +void iomap_bounce_read_end_io(struct iomap_ioend *ioend, struct bio *orig_bio, + int error) +{ + if (error) + orig_bio->bi_status = errno_to_blk_status(error); + else + iomap_ioend_unbounce(iomap_ioend_from_bio(orig_bio), ioend); + + bio_free_folios(&ioend->io_bio); + if (bio_integrity(&ioend->io_bio)) + fs_bio_integrity_free(&ioend->io_bio); + bio_put(&ioend->io_bio); + + bio_endio(orig_bio); +} +EXPORT_SYMBOL_GPL(iomap_bounce_read_end_io); + static int __init iomap_ioend_init(void) { const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); diff --git a/fs/xfs/libxfs/xfs_errortag.h b/fs/xfs/libxfs/xfs_errortag.h index 6de207fed2d8..2dc441da0333 100644 --- a/fs/xfs/libxfs/xfs_errortag.h +++ b/fs/xfs/libxfs/xfs_errortag.h @@ -75,7 +75,8 @@ #define XFS_ERRTAG_METAFILE_RESV_CRITICAL 45 #define XFS_ERRTAG_FORCE_ZERO_RANGE 46 #define XFS_ERRTAG_ZONE_RESET 47 -#define XFS_ERRTAG_MAX 48 +#define XFS_ERRTAG_BOUNCE_REREAD 48 +#define XFS_ERRTAG_MAX 49 /* * Random factors for above tags, 1 means always, 2 means 1/2 time, etc. @@ -137,7 +138,8 @@ XFS_ERRTAG(WRITE_DELAY_MS, write_delay_ms, 3000) \ XFS_ERRTAG(EXCHMAPS_FINISH_ONE, exchmaps_finish_one, 1) \ XFS_ERRTAG(METAFILE_RESV_CRITICAL, metafile_resv_crit, 4) \ XFS_ERRTAG(FORCE_ZERO_RANGE, force_zero_range, 4) \ -XFS_ERRTAG(ZONE_RESET, zone_reset, 1) +XFS_ERRTAG(ZONE_RESET, zone_reset, 1) \ +XFS_ERRTAG(BOUNCE_REREAD, bounce_reread, XFS_RANDOM_DEFAULT) #endif /* XFS_ERRTAG */ #endif /* __XFS_ERRORTAG_H_ */ diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 8b6119776fb3..c30e688cfc9f 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -23,7 +23,6 @@ #include "xfs_ioend.h" #include "xfs_zone_alloc.h" #include "xfs_rtgroup.h" -#include <linux/bio-integrity.h> struct xfs_writepage_ctx { struct iomap_writepage_ctx ctx; @@ -498,8 +497,7 @@ xfs_zoned_writeback_submit( bio_endio(&ioend->io_bio); return error; } - if (wpc->iomap.flags & IOMAP_F_INTEGRITY) - fs_bio_integrity_generate(&ioend->io_bio); + xfs_zone_alloc_and_submit(ioend, &XFS_ZWPC(wpc)->open_zone); return 0; } @@ -585,11 +583,10 @@ xfs_bio_submit_read( const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx) { - struct bio *bio = ctx->read_ctx; - - /* defer read completions to the ioend workqueue */ - iomap_init_ioend(iter->inode, bio, ctx->read_ctx_file_offset, 0); - iomap_bio_submit_read_endio(iter, ctx, xfs_end_bio); + xfs_ioend_submit_read(iter->inode, ctx->read_ctx, + ctx->read_ctx_file_offset, + iomap_ioend_flags(&iter->iomap)); + ctx->read_ctx = NULL; } static const struct iomap_read_ops xfs_iomap_read_ops = { diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 8256c1d13ce2..6c93b4f5629c 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -5,6 +5,7 @@ */ #include "xfs_platform.h" #include <linux/backing-dev.h> +#include <linux/blk-integrity.h> #include <linux/dax.h> #include "xfs_shared.h" @@ -1694,6 +1695,7 @@ xfs_configure_buftarg( struct xfs_mount *mp = btp->bt_mount; if (btp->bt_bdev) { + struct blk_integrity *bi = bdev_get_integrity(btp->bt_bdev); int error; error = bdev_validate_blocksize(btp->bt_bdev, sectorsize); @@ -1706,6 +1708,15 @@ xfs_configure_buftarg( if (bdev_can_atomic_write(btp->bt_bdev)) xfs_configure_buftarg_atomic_writes(btp); + + if (!bi) + ; + else if (btp->bt_bdev == btp->bt_mount->m_super->s_bdev) + xfs_info(mp, "using %s integrity profile", + blk_integrity_profile_name(bi)); + else + xfs_info(mp, "using %s integrity profile for %pg", + blk_integrity_profile_name(bi), btp->bt_bdev); } btp->bt_meta_sectorsize = sectorsize; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index d8202da15aca..dd3782e9af93 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -37,6 +37,7 @@ #include <linux/fadvise.h> #include <linux/mount.h> #include <linux/filelock.h> +#include <linux/bio-integrity.h> static const struct vm_operations_struct xfs_file_vm_ops; @@ -222,9 +223,8 @@ xfs_dio_read_bounce_submit_io( struct bio *bio, loff_t file_offset) { - iomap_init_ioend(iter->inode, bio, file_offset, IOMAP_IOEND_DIRECT); - bio->bi_end_io = xfs_end_bio; - submit_bio(bio); + xfs_ioend_submit_read(iter->inode, bio, file_offset, + iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT); } static const struct iomap_dio_ops xfs_dio_read_bounce_ops = { @@ -252,8 +252,7 @@ xfs_file_dio_read( return ret; if (mapping_stable_writes(iocb->ki_filp->f_mapping)) { ret = iomap_dio_rw(iocb, to, &xfs_read_iomap_ops, - &xfs_dio_read_bounce_ops, IOMAP_DIO_BOUNCE, - NULL, 0); + &xfs_dio_read_bounce_ops, 0, NULL, 0); } else { ret = iomap_dio_read_simple(iocb, to, xfs_read_iomap_begin); if (ret == -ENOTBLK) @@ -713,7 +712,7 @@ xfs_dio_zoned_submit_io( bio->bi_end_io = xfs_end_bio; ioend = iomap_init_ioend(iter->inode, bio, file_offset, - IOMAP_IOEND_DIRECT); + iomap_ioend_flags(&iter->iomap) | IOMAP_IOEND_DIRECT); xfs_zone_alloc_and_submit(ioend, &ac->open_zone); } diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c index 40695d18dac0..e70be5b86f0b 100644 --- a/fs/xfs/xfs_ioend.c +++ b/fs/xfs/xfs_ioend.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016-2025 Christoph Hellwig. + * Copyright (c) 2016-2026 Christoph Hellwig. * All Rights Reserved. */ #include "xfs_platform.h" @@ -16,6 +16,135 @@ #include "xfs_reflink.h" #include "xfs_zone_alloc.h" #include "xfs_ioend.h" +#include "xfs_error.h" +#include "xfs_errortag.h" +#include <linux/bio-integrity.h> + +static void +xfs_dio_bounce_end_io( + struct bio *bio) +{ + struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); + int error = blk_status_to_errno(bio->bi_status); + struct bio *orig_bio = bio->bi_private; + + if ((ioend->io_flags & IOMAP_IOEND_INTEGRITY) && !bio->bi_status) + error = iomap_ioend_integrity_verify(ioend); + iomap_bounce_read_end_io(ioend, orig_bio, error); +} + +static void +xfs_bounce_submit_ioend( + struct iomap_ioend *ioend) +{ + if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) + fs_bio_integrity_alloc(&ioend->io_bio); + ioend->io_bio.bi_end_io = xfs_dio_bounce_end_io; + bio_set_flag(&ioend->io_bio, BIO_COMPLETE_IN_TASK); + submit_bio(&ioend->io_bio); +} + +static void +xfs_end_bio_bounced( + struct bio *bio) +{ + /* + * Just complete the original ioends as all verification is done by the + * end_io handlers for the clone bio(s). + */ + iomap_finish_ioends(iomap_ioend_from_bio(bio), + blk_status_to_errno(bio->bi_status)); +} + +static void +xfs_read_bounce_and_resubmit( + struct iomap_ioend *ioend) +{ + struct bio *bio = &ioend->io_bio; + struct xfs_inode *ip = XFS_I(ioend->io_inode); + unsigned int nofs_flag = memalloc_nofs_save(); + + trace_xfs_bounce_reread(ip, ioend->io_offset, ioend->io_size); + + /* + * Free the bio integrity data for the original bio, as we'll allocate + * a new one for each sub-I/O, which could deadlock if we keep the + * integrity data for the original bio around. + */ + if (bio_integrity(bio)) + fs_bio_integrity_free(bio); + + /* + * Resubmit the bio through the iomap bounce machinery. The original + * bio itself is not resubmitted to the block layer, but just used to + * track I/O completion of the cloned bios. + */ + bio_prepare_reissue(bio, xfs_inode_buftarg(ip)->bt_bdev); + bio->bi_iter = (struct bvec_iter) { + .bi_sector = ioend->io_sector, + .bi_size = ioend->io_size, + .bi_offset = ioend->io_bvec_offset, + }; + bio->bi_end_io = xfs_end_bio_bounced; + iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev), + xfs_bounce_submit_ioend); + memalloc_nofs_restore(nofs_flag); +} + +static void +xfs_end_io_read( + struct bio *bio) +{ + struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); + struct xfs_inode *ip = XFS_I(ioend->io_inode); + struct xfs_mount *mp = ip->i_mount; + int error = blk_status_to_errno(bio->bi_status); + + if (!error && (ioend->io_flags & IOMAP_IOEND_INTEGRITY)) { + error = iomap_ioend_integrity_verify(ioend); + if ((ioend->io_flags & IOMAP_IOEND_DIRECT) && + READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_LAZY) { + /* + * We only really need to retry for guard tag errors, + * but right now we can't distinguish them from other + * (i.e, reftag) errors. + */ + if (error || + XFS_TEST_ERROR(mp, XFS_ERRTAG_BOUNCE_REREAD)) { + xfs_read_bounce_and_resubmit(ioend); + return; + } + } + } + + iomap_finish_ioends(ioend, error); +} + +void +xfs_ioend_submit_read( + struct inode *inode, + struct bio *bio, + loff_t file_offset, + u16 ioend_flags) +{ + struct xfs_inode *ip = XFS_I(inode); + struct xfs_mount *mp = ip->i_mount; + struct iomap_ioend *ioend; + + ioend = iomap_init_ioend(inode, bio, file_offset, ioend_flags); + if ((ioend_flags & IOMAP_IOEND_DIRECT) && + READ_ONCE(mp->m_read_bounce) == XFS_READ_BOUNCE_ALWAYS) { + iomap_bounce_read(ioend, bdev_logical_block_size(bio->bi_bdev), + xfs_bounce_submit_ioend); + return; + } + + if (ioend_flags & IOMAP_IOEND_INTEGRITY) + fs_bio_integrity_alloc(bio); + bio->bi_end_io = xfs_end_io_read; + bio_set_flag(bio, BIO_COMPLETE_IN_TASK); + submit_bio(bio); +} static void xfs_ioend_put_open_zones( @@ -148,11 +277,7 @@ xfs_end_io( io_list))) { list_del_init(&ioend->io_list); iomap_ioend_try_merge(ioend, &tmp); - if (bio_op(&ioend->io_bio) == REQ_OP_READ) - iomap_finish_ioends(ioend, - blk_status_to_errno(ioend->io_bio.bi_status)); - else - xfs_end_ioend_write(ioend); + xfs_end_ioend_write(ioend); cond_resched(); } } diff --git a/fs/xfs/xfs_ioend.h b/fs/xfs/xfs_ioend.h index 525865767fca..7c2a1ea3e6ed 100644 --- a/fs/xfs/xfs_ioend.h +++ b/fs/xfs/xfs_ioend.h @@ -12,5 +12,7 @@ static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend) } void xfs_end_bio(struct bio *bio); +void xfs_ioend_submit_read(struct inode *inode, struct bio *bio, + loff_t file_offset, u16 ioend_flags); #endif /* __XFS_IOEND_H */ diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 216a38a354e7..894ff2f4ecbd 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -142,6 +142,12 @@ struct xfs_freecounter { uint64_t res_saved; }; +enum xfs_read_bounce { + XFS_READ_BOUNCE_NEVER, + XFS_READ_BOUNCE_ALWAYS, + XFS_READ_BOUNCE_LAZY, +}; + /* * The struct xfsmount layout is optimised to separate read-mostly variables * from variables that are frequently modified. We put the read-mostly variables @@ -177,6 +183,7 @@ typedef struct xfs_mount { struct workqueue_struct *m_sync_workqueue; struct workqueue_struct *m_blockgc_wq; struct workqueue_struct *m_inodegc_wq; + enum xfs_read_bounce m_read_bounce; int m_bsize; /* fs logical block size */ uint8_t m_blkbit_log; /* blocklog + NBBY */ @@ -291,6 +298,7 @@ typedef struct xfs_mount { struct xfs_zone_info *m_zone_info; /* zone allocator information */ struct dentry *m_debugfs; /* debugfs parent */ struct xfs_kobj m_kobj; + struct xfs_kobj m_csum_kobj; struct xfs_kobj m_error_kobj; struct xfs_kobj m_error_meta_kobj; struct xfs_error_cfg m_error_cfg[XFS_ERR_CLASS_MAX][XFS_ERR_ERRNO_MAX]; diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index b24db75eaedc..fce1d2905c94 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -2317,6 +2317,7 @@ xfs_init_fs_context( mp->m_logbufs = -1; mp->m_logbsize = -1; mp->m_allocsize_log = 16; /* 64k */ + mp->m_read_bounce = XFS_READ_BOUNCE_LAZY; xfs_hooks_init(&mp->m_dir_update_hooks); diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c index b62712187324..e77917ac179d 100644 --- a/fs/xfs/xfs_sysfs.c +++ b/fs/xfs/xfs_sysfs.c @@ -392,6 +392,71 @@ const struct kobj_type xfs_stats_ktype = { .default_groups = xfs_stats_groups, }; +static inline struct xfs_mount *csum_to_mp(struct kobject *kobj) +{ + return container_of(to_kobj(kobj), struct xfs_mount, m_csum_kobj); +} + +static bool +xfs_has_read_bounce( + struct xfs_mount *mp) +{ + if (bdev_has_integrity_csum(mp->m_ddev_targp->bt_bdev)) + return true; + if (mp->m_rtdev_targp && + bdev_has_integrity_csum(mp->m_rtdev_targp->bt_bdev)) + return true; + return false; +} + +static const char * const bounce_modes[] = { + [XFS_READ_BOUNCE_NEVER] = "never", + [XFS_READ_BOUNCE_ALWAYS] = "always", + [XFS_READ_BOUNCE_LAZY] = "lazy", +}; + +static ssize_t +read_bounce_show( + struct kobject *kobj, + char *buf) +{ + struct xfs_mount *mp = csum_to_mp(kobj); + + return sysfs_emit(buf, "%s\n", + bounce_modes[READ_ONCE(mp->m_read_bounce)]); +} + +static ssize_t +read_bounce_store( + struct kobject *kobj, + const char *buf, + size_t count) +{ + struct xfs_mount *mp = csum_to_mp(kobj); + int ret; + + if (!xfs_has_read_bounce(mp)) + return -EINVAL; + ret = sysfs_match_string(bounce_modes, buf); + if (ret < 0) + return ret; + WRITE_ONCE(mp->m_read_bounce, ret); + return count; +} +XFS_SYSFS_ATTR_RW(read_bounce); + +static struct attribute *xfs_csum_attrs[] = { + ATTR_LIST(read_bounce), + NULL, +}; +ATTRIBUTE_GROUPS(xfs_csum); + +static const struct kobj_type xfs_csum_ktype = { + .release = xfs_sysfs_release, + .sysfs_ops = &xfs_sysfs_ops, + .default_groups = xfs_csum_groups, +}; + /* xlog */ static inline struct xlog * @@ -817,11 +882,17 @@ xfs_mount_sysfs_init( if (error) goto out_remove_fsdir; + /* .../xfs/<dev>/csum/ */ + error = xfs_sysfs_init(&mp->m_csum_kobj, &xfs_csum_ktype, &mp->m_kobj, + "csum"); + if (error) + goto out_remove_stats_dir; + /* .../xfs/<dev>/error/ */ error = xfs_sysfs_init(&mp->m_error_kobj, &xfs_error_ktype, &mp->m_kobj, "error"); if (error) - goto out_remove_stats_dir; + goto out_remove_csum_dir; /* .../xfs/<dev>/error/fail_at_unmount */ error = sysfs_create_file(&mp->m_error_kobj.kobject, @@ -835,12 +906,14 @@ xfs_mount_sysfs_init( "metadata", &mp->m_error_meta_kobj, xfs_error_meta_init); if (error) - goto out_remove_error_dir; + goto out_remove_csum_dir; return 0; out_remove_error_dir: xfs_sysfs_del(&mp->m_error_kobj); +out_remove_csum_dir: + xfs_sysfs_del(&mp->m_csum_kobj); out_remove_stats_dir: xfs_sysfs_del(&mp->m_stats.xs_kobj); out_remove_fsdir: @@ -864,6 +937,7 @@ xfs_mount_sysfs_del( } xfs_sysfs_del(&mp->m_error_meta_kobj); xfs_sysfs_del(&mp->m_error_kobj); + xfs_sysfs_del(&mp->m_csum_kobj); xfs_sysfs_del(&mp->m_stats.xs_kobj); xfs_sysfs_del(&mp->m_kobj); } diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 6aa379c2cf0c..eed9b241aec6 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1896,6 +1896,7 @@ DEFINE_SIMPLE_IO_EVENT(xfs_zero_eof); DEFINE_SIMPLE_IO_EVENT(xfs_end_io_direct_write); DEFINE_SIMPLE_IO_EVENT(xfs_file_splice_read); DEFINE_SIMPLE_IO_EVENT(xfs_zoned_map_blocks); +DEFINE_SIMPLE_IO_EVENT(xfs_bounce_reread); DECLARE_EVENT_CLASS(xfs_itrunc_class, TP_PROTO(struct xfs_inode *ip, xfs_fsize_t new_size), diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index 28c1e48909fa..6516361a3f38 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -26,6 +26,7 @@ #include "xfs_zones.h" #include "xfs_trace.h" #include "xfs_mru_cache.h" +#include <linux/bio-integrity.h> static void xfs_open_zone_free_rcu( @@ -911,6 +912,9 @@ xfs_zone_alloc_and_submit( if (xfs_is_shutdown(mp)) goto out_error; + if (ioend->io_flags & IOMAP_IOEND_INTEGRITY) + fs_bio_integrity_generate(&ioend->io_bio); + /* * If we don't have a locally cached zone in this write context, see if * the inode is still associated with a zone and use that if so. diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h index 0ea2a8bf7efb..a954c97be0b3 100644 --- a/include/linux/bio-integrity.h +++ b/include/linux/bio-integrity.h @@ -151,7 +151,6 @@ void bio_integrity_setup_default(struct bio *bio); unsigned int fs_bio_integrity_alloc(struct bio *bio); void fs_bio_integrity_free(struct bio *bio); void fs_bio_integrity_generate(struct bio *bio); -int fs_bio_integrity_verify(struct bio *bio, sector_t sector, - unsigned int size); +int fs_bio_integrity_verify(struct bio *bio, struct bvec_iter *data_iter); #endif /* _LINUX_BIO_INTEGRITY_H */ diff --git a/include/linux/bio.h b/include/linux/bio.h index bb3235497e67..17944e44b584 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -479,6 +479,7 @@ static inline void bio_init_inline(struct bio *bio, struct block_device *bdev, extern void bio_uninit(struct bio *); void bio_reset(struct bio *bio, struct block_device *bdev, blk_opf_t opf); void bio_reuse(struct bio *bio, blk_opf_t opf); +void bio_prepare_reissue(struct bio *bio, struct block_device *bdev); void bio_chain(struct bio *, struct bio *); void bio_await(struct bio *bio, void *priv, void (*submit)(struct bio *bio, void *priv)); @@ -516,16 +517,18 @@ int bdev_rw_virt(struct block_device *bdev, sector_t sector, void *data, size_t len, enum req_op op); int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, - unsigned mem_align_mask, unsigned len_align_mask); + unsigned maxlen, unsigned mem_align_mask, + unsigned len_align_mask); bool bio_iov_iter_set(struct bio *bio, const struct iov_iter *iter); void __bio_release_pages(struct bio *bio, bool mark_dirty); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen, - size_t minsize); -void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty); +int bio_alloc_bounce_folios(struct bio *bio, size_t total_len, size_t minsize); +void bio_free_folios(struct bio *bio); +int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, + size_t maxlen, size_t minsize); extern void bio_copy_data(struct bio *dst, struct bio *src); extern void bio_free_pages(struct bio *bio); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index bc7ae6327dbf..59718f73c15a 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -483,13 +483,35 @@ sector_t iomap_bmap(struct address_space *mapping, sector_t bno, #define IOMAP_IOEND_BOUNDARY (1U << 2) /* is direct I/O */ #define IOMAP_IOEND_DIRECT (1U << 3) +/* generate integrity (PI) information */ +#ifdef CONFIG_BLK_DEV_INTEGRITY +#define IOMAP_IOEND_INTEGRITY (1U << 4) +#else +#define IOMAP_IOEND_INTEGRITY 0 +#endif /* CONFIG_BLK_DEV_INTEGRITY */ /* * Flags that if set on either ioend prevent the merge of two ioends. * (IOMAP_IOEND_BOUNDARY also prevents merges, but only one-way) */ #define IOMAP_IOEND_NOMERGE_FLAGS \ - (IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT) + (IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT | \ + IOMAP_IOEND_INTEGRITY) + +/* ioend flags directly implied by iomap flags */ +static inline u16 iomap_ioend_flags(const struct iomap *iomap) +{ + unsigned int flags = 0; + + if (iomap->type == IOMAP_UNWRITTEN) + flags |= IOMAP_IOEND_UNWRITTEN; + if (iomap->flags & IOMAP_F_SHARED) + flags |= IOMAP_IOEND_SHARED; + if (iomap->flags & IOMAP_F_INTEGRITY) + flags |= IOMAP_IOEND_INTEGRITY; + + return flags; +} /* * Structure for writeback I/O completions. @@ -500,6 +522,7 @@ sector_t iomap_bmap(struct address_space *mapping, sector_t bno, struct iomap_ioend { struct list_head io_list; /* next ioend in chain */ u16 io_flags; /* IOMAP_IOEND_* */ + u32 io_bvec_offset; /* offset into first bvec */ struct inode *io_inode; /* file being written to */ size_t io_size; /* size of the extent */ atomic_t io_remaining; /* completetion defer count */ @@ -517,6 +540,13 @@ static inline struct iomap_ioend *iomap_ioend_from_bio(struct bio *bio) return container_of(bio, struct iomap_ioend, io_bio); } +#define BVEC_ITER_IOEND(_ioend) \ +{ \ + .bi_sector = (_ioend)->io_sector, \ + .bi_size = (_ioend)->io_size, \ + .bi_offset = (_ioend)->io_bvec_offset, \ +} + struct iomap_writeback_ops { /* * Performs writeback on the passed in range @@ -565,6 +595,7 @@ void iomap_finish_ioends(struct iomap_ioend *ioend, int error); void iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends); void iomap_sort_ioends(struct list_head *ioend_list); +int iomap_ioend_integrity_verify(struct iomap_ioend *ioend); ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, loff_t pos, loff_t end_pos, unsigned int dirty_len); int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error); @@ -577,6 +608,11 @@ void iomap_finish_folio_write(struct inode *inode, struct folio *folio, int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio); int iomap_writepages(struct iomap_writepage_ctx *wpc); +void iomap_bounce_read(struct iomap_ioend *orig_ioend, unsigned int minsize, + void (*submit_ioend)(struct iomap_ioend *ioend)); +void iomap_bounce_read_end_io(struct iomap_ioend *ioend, struct bio *orig_bio, + int error); + struct iomap_read_folio_ctx { const struct iomap_read_ops *ops; struct folio *cur_folio; |
