From 4d25c7d68896b4002c4ab5cd646775392bb7fbb4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:09 -0800 Subject: iomap: pass the iomap_iter to ->submit_read This provides additional context for file systems. Rename the fuse instance to match the method name while we're at it. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260223132021.292832-10-hch@lst.de Tested-by: Anuj Gupta Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner --- include/linux/iomap.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 99b7209dabd7..6fbe121e2adf 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -512,7 +512,8 @@ struct iomap_read_ops { * * This is optional. */ - void (*submit_read)(struct iomap_read_folio_ctx *ctx); + void (*submit_read)(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx); }; /* -- cgit v1.2.3 From 5f4fe046cb3c84eed719f7becbe822000e1a589e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:11 -0800 Subject: iomap: allow file systems to hook into buffered read bio submission File systems such as btrfs have additional operations with bios such as verifying data checksums. Allow file systems to hook into submission of the bio to allow for this processing by replacing the direct submit_bio call in iomap_read_alloc_bio with a call into ->submit_read and exporting iomap_read_alloc_bio. Also add a new field to struct iomap_read_folio_ctx to track the file logic offset of the current read context. Based on a patch from Goldwyn Rodrigues . Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260223132021.292832-12-hch@lst.de Tested-by: Anuj Gupta Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner --- fs/iomap/bio.c | 15 +++++++++------ include/linux/iomap.h | 4 ++++ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 80bbd328bd3c..903cb9fe759e 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -32,10 +32,11 @@ static void iomap_read_alloc_bio(const struct iomap_iter *iter, struct folio *folio = ctx->cur_folio; gfp_t gfp = mapping_gfp_constraint(folio->mapping, GFP_KERNEL); gfp_t orig_gfp = gfp; - struct bio *bio = ctx->read_ctx; + struct bio *bio; - if (bio) - submit_bio(bio); + /* Submit the existing range if there was one. */ + if (ctx->read_ctx) + ctx->ops->submit_read(iter, ctx); /* Same as readahead_gfp_mask: */ if (ctx->rac) @@ -56,9 +57,10 @@ static void iomap_read_alloc_bio(const struct iomap_iter *iter, bio_add_folio_nofail(bio, folio, plen, offset_in_folio(folio, iter->pos)); ctx->read_ctx = bio; + ctx->read_ctx_file_offset = iter->pos; } -static int iomap_bio_read_folio_range(const struct iomap_iter *iter, +int iomap_bio_read_folio_range(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx, size_t plen) { struct folio *folio = ctx->cur_folio; @@ -70,10 +72,11 @@ static int iomap_bio_read_folio_range(const struct iomap_iter *iter, iomap_read_alloc_bio(iter, ctx, plen); return 0; } +EXPORT_SYMBOL_GPL(iomap_bio_read_folio_range); const struct iomap_read_ops iomap_bio_read_ops = { - .read_folio_range = iomap_bio_read_folio_range, - .submit_read = iomap_bio_submit_read, + .read_folio_range = iomap_bio_read_folio_range, + .submit_read = iomap_bio_submit_read, }; EXPORT_SYMBOL_GPL(iomap_bio_read_ops); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 6fbe121e2adf..b2b9e649a3b8 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -493,6 +493,7 @@ struct iomap_read_folio_ctx { struct folio *cur_folio; struct readahead_control *rac; void *read_ctx; + loff_t read_ctx_file_offset; }; struct iomap_read_ops { @@ -599,6 +600,9 @@ int iomap_swapfile_activate(struct swap_info_struct *sis, extern struct bio_set iomap_ioend_bioset; #ifdef CONFIG_BLOCK +int iomap_bio_read_folio_range(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx, size_t plen); + extern const struct iomap_read_ops iomap_bio_read_ops; static inline void iomap_bio_read_folio(struct folio *folio, -- cgit v1.2.3 From 57287771fa8d77841149bf847b629f29acbad35b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:13 -0800 Subject: iomap: add a bioset pointer to iomap_read_folio_ops Optionally allocate the bio from the bioset provided in iomap_read_folio_ops. If no bioset is provided, fs_bio_set is still used, which is the standard bioset for file systems. Based on a patch from Goldwyn Rodrigues . Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260223132021.292832-14-hch@lst.de Tested-by: Anuj Gupta Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner --- fs/iomap/bio.c | 14 ++++++++++++-- include/linux/iomap.h | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index 903cb9fe759e..259a2bf95a43 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -24,11 +24,19 @@ static void iomap_bio_submit_read(const struct iomap_iter *iter, submit_bio(ctx->read_ctx); } +static struct bio_set *iomap_read_bio_set(struct iomap_read_folio_ctx *ctx) +{ + if (ctx->ops && ctx->ops->bio_set) + return ctx->ops->bio_set; + return &fs_bio_set; +} + static void iomap_read_alloc_bio(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx, size_t plen) { const struct iomap *iomap = &iter->iomap; unsigned int nr_vecs = DIV_ROUND_UP(iomap_length(iter), PAGE_SIZE); + struct bio_set *bio_set = iomap_read_bio_set(ctx); struct folio *folio = ctx->cur_folio; gfp_t gfp = mapping_gfp_constraint(folio->mapping, GFP_KERNEL); gfp_t orig_gfp = gfp; @@ -47,9 +55,11 @@ static void iomap_read_alloc_bio(const struct iomap_iter *iter, * having to deal with partial page reads. This emulates what * do_mpage_read_folio does. */ - bio = bio_alloc(iomap->bdev, bio_max_segs(nr_vecs), REQ_OP_READ, gfp); + bio = bio_alloc_bioset(iomap->bdev, bio_max_segs(nr_vecs), REQ_OP_READ, + gfp, bio_set); if (!bio) - bio = bio_alloc(iomap->bdev, 1, REQ_OP_READ, orig_gfp); + bio = bio_alloc_bioset(iomap->bdev, 1, REQ_OP_READ, orig_gfp, + bio_set); if (ctx->rac) bio->bi_opf |= REQ_RAHEAD; bio->bi_iter.bi_sector = iomap_sector(iomap, iter->pos); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index b2b9e649a3b8..387a1174522f 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -515,6 +515,12 @@ struct iomap_read_ops { */ void (*submit_read)(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx); + + /* + * Optional, allows filesystem to specify own bio_set, so new bio's + * can be allocated from the provided bio_set. + */ + struct bio_set *bio_set; }; /* -- cgit v1.2.3 From 0b10a370529cbd7b918c1eef43d409e43d9e0b78 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:15 -0800 Subject: iomap: support T10 protection information Add support for generating / verifying protection information in iomap. This is done by hooking into the bio submission and then using the generic PI helpers. Compared to just using the block layer auto PI this extends the protection envelope and also prepares for eventually passing through PI from userspace at least for direct I/O. To generate or verify PI, the file system needs to set the IOMAP_F_INTEGRITY flag on the iomap for the request, and ensure the ioends are used for all integrity I/O. Additionally the file system must defer read I/O completions to user context so that the guard tag validation isn't run from interrupt context. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260223132021.292832-16-hch@lst.de Tested-by: Anuj Gupta Reviewed-by: "Darrick J. Wong" Signed-off-by: Christian Brauner --- fs/iomap/bio.c | 24 +++++++++++++++++++++--- fs/iomap/direct-io.c | 15 ++++++++++++++- fs/iomap/internal.h | 13 +++++++++++++ fs/iomap/ioend.c | 20 ++++++++++++++++++-- include/linux/iomap.h | 7 +++++++ 5 files changed, 73 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index b4de67bdd513..f989ffcaac96 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -3,6 +3,7 @@ * Copyright (C) 2010 Red Hat, Inc. * Copyright (C) 2016-2023 Christoph Hellwig. */ +#include #include #include #include "internal.h" @@ -17,6 +18,8 @@ static u32 __iomap_read_end_io(struct bio *bio, int error) iomap_finish_folio_read(fi.folio, fi.offset, fi.length, error); folio_count++; } + if (bio_integrity(bio)) + fs_bio_integrity_free(bio); bio_put(bio); return folio_count; } @@ -34,7 +37,11 @@ u32 iomap_finish_ioend_buffered_read(struct iomap_ioend *ioend) static void iomap_bio_submit_read(const struct iomap_iter *iter, struct iomap_read_folio_ctx *ctx) { - submit_bio(ctx->read_ctx); + struct bio *bio = ctx->read_ctx; + + if (iter->iomap.flags & IOMAP_F_INTEGRITY) + fs_bio_integrity_alloc(bio); + submit_bio(bio); } static struct bio_set *iomap_read_bio_set(struct iomap_read_folio_ctx *ctx) @@ -91,6 +98,7 @@ int iomap_bio_read_folio_range(const struct iomap_iter *iter, if (!bio || bio_end_sector(bio) != iomap_sector(&iter->iomap, iter->pos) || + bio->bi_iter.bi_size > iomap_max_bio_size(&iter->iomap) - plen || !bio_add_folio(bio, folio, plen, offset_in_folio(folio, iter->pos))) iomap_read_alloc_bio(iter, ctx, plen); return 0; @@ -107,11 +115,21 @@ int iomap_bio_read_folio_range_sync(const struct iomap_iter *iter, struct folio *folio, loff_t pos, size_t len) { const struct iomap *srcmap = iomap_iter_srcmap(iter); + sector_t sector = iomap_sector(srcmap, pos); struct bio_vec bvec; struct bio bio; + int error; bio_init(&bio, srcmap->bdev, &bvec, 1, REQ_OP_READ); - bio.bi_iter.bi_sector = iomap_sector(srcmap, pos); + bio.bi_iter.bi_sector = sector; bio_add_folio_nofail(&bio, folio, len, offset_in_folio(folio, pos)); - return submit_bio_wait(&bio); + if (srcmap->flags & IOMAP_F_INTEGRITY) + fs_bio_integrity_alloc(&bio); + error = submit_bio_wait(&bio); + if (srcmap->flags & IOMAP_F_INTEGRITY) { + if (!error) + error = fs_bio_integrity_verify(&bio, sector, len); + fs_bio_integrity_free(&bio); + } + return error; } diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 2cb0c0f43215..c24d94349ca5 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -3,6 +3,7 @@ * Copyright (C) 2010 Red Hat, Inc. * Copyright (c) 2016-2025 Christoph Hellwig. */ +#include #include #include #include @@ -240,6 +241,9 @@ static void __iomap_dio_bio_end_io(struct bio *bio, bool inline_completion) { struct iomap_dio *dio = bio->bi_private; + if (bio_integrity(bio)) + fs_bio_integrity_free(bio); + if (dio->flags & IOMAP_DIO_BOUNCE) { bio_iov_iter_unbounce(bio, !!dio->error, dio->flags & IOMAP_DIO_USER_BACKED); @@ -350,8 +354,10 @@ 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, BIO_MAX_SIZE); + ret = bio_iov_iter_bounce(bio, dio->submit.iter, + iomap_max_bio_size(&iter->iomap)); else ret = bio_iov_iter_get_pages(bio, dio->submit.iter, alignment - 1); @@ -368,6 +374,13 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, goto out_put_bio; } + 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) && diff --git a/fs/iomap/internal.h b/fs/iomap/internal.h index b39dbc17e3f0..74e898b196dc 100644 --- a/fs/iomap/internal.h +++ b/fs/iomap/internal.h @@ -4,6 +4,19 @@ #define IOEND_BATCH_SIZE 4096 +/* + * Normally we can build bios as big as the data structure supports. + * + * But for integrity protected I/O we need to respect the maximum size of the + * single contiguous allocation for the integrity buffer. + */ +static inline size_t iomap_max_bio_size(const struct iomap *iomap) +{ + if (iomap->flags & IOMAP_F_INTEGRITY) + return max_integrity_io_size(bdev_limits(iomap->bdev)); + return BIO_MAX_SIZE; +} + u32 iomap_finish_ioend_buffered_read(struct iomap_ioend *ioend); u32 iomap_finish_ioend_direct(struct iomap_ioend *ioend); diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 450ab002eb91..7c034b6a583e 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2016-2025 Christoph Hellwig. */ +#include #include #include #include @@ -65,6 +66,8 @@ static u32 iomap_finish_ioend_buffered_write(struct iomap_ioend *ioend) folio_count++; } + if (bio_integrity(bio)) + fs_bio_integrity_free(bio); bio_put(bio); /* frees the ioend */ return folio_count; } @@ -144,6 +147,8 @@ int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error) return error; } + if (wpc->iomap.flags & IOMAP_F_INTEGRITY) + fs_bio_integrity_generate(&ioend->io_bio); submit_bio(&ioend->io_bio); return 0; } @@ -165,10 +170,13 @@ static struct iomap_ioend *iomap_alloc_ioend(struct iomap_writepage_ctx *wpc, } static bool iomap_can_add_to_ioend(struct iomap_writepage_ctx *wpc, loff_t pos, - u16 ioend_flags) + unsigned int map_len, u16 ioend_flags) { struct iomap_ioend *ioend = wpc->wb_ctx; + if (ioend->io_bio.bi_iter.bi_size > + iomap_max_bio_size(&wpc->iomap) - map_len) + return false; if (ioend_flags & IOMAP_IOEND_BOUNDARY) return false; if ((ioend_flags & IOMAP_IOEND_NOMERGE_FLAGS) != @@ -234,7 +242,7 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY)) ioend_flags |= IOMAP_IOEND_BOUNDARY; - if (!ioend || !iomap_can_add_to_ioend(wpc, pos, ioend_flags)) { + if (!ioend || !iomap_can_add_to_ioend(wpc, pos, map_len, ioend_flags)) { new_ioend: if (ioend) { error = wpc->ops->writeback_submit(wpc, 0); @@ -311,6 +319,14 @@ 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) diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 387a1174522f..531f9ebdeeae 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -65,6 +65,8 @@ struct vm_fault; * * IOMAP_F_ATOMIC_BIO indicates that (write) I/O will be issued as an atomic * bio, i.e. set REQ_ATOMIC. + * + * IOMAP_F_INTEGRITY indicates that the filesystems handles integrity metadata. */ #define IOMAP_F_NEW (1U << 0) #define IOMAP_F_DIRTY (1U << 1) @@ -79,6 +81,11 @@ struct vm_fault; #define IOMAP_F_BOUNDARY (1U << 6) #define IOMAP_F_ANON_WRITE (1U << 7) #define IOMAP_F_ATOMIC_BIO (1U << 8) +#ifdef CONFIG_BLK_DEV_INTEGRITY +#define IOMAP_F_INTEGRITY (1U << 9) +#else +#define IOMAP_F_INTEGRITY 0 +#endif /* CONFIG_BLK_DEV_INTEGRITY */ /* * Flag reserved for file system specific usage -- cgit v1.2.3