On Wed, Dec 11, 2024 at 09:53:44AM +0100, Christoph Hellwig wrote: > Provide helpers for file systems to split bios in the direct I/O and > writeback I/O submission handlers. > > This Follows btrfs' lead and don't try to build bios to hardware limits > for zone append commands, but instead build them as normal unconstrained > bios and split them to the hardware limits in the I/O submission handler. > > Signed-off-by: Christoph Hellwig <hch@xxxxxx> > --- > fs/iomap/Makefile | 1 + > fs/iomap/buffered-io.c | 43 ++++++++++++++----------- > fs/iomap/ioend.c | 73 ++++++++++++++++++++++++++++++++++++++++++ > include/linux/iomap.h | 9 ++++++ > 4 files changed, 108 insertions(+), 18 deletions(-) > create mode 100644 fs/iomap/ioend.c > ... > diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c > new file mode 100644 > index 000000000000..f3d98121c593 > --- /dev/null > +++ b/fs/iomap/ioend.c ... It might be useful to add a small comment here to point out this splits from the front of the ioend (i.e. akin to bio_split()), documents the params, and maybe mentions the ioend relationship requirements (i.e. according to bio_split(), the split ioend bio refers to the vectors in the original ioend bio). Brian > +struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, bool is_append, > + unsigned int *alloc_len) > +{ > + struct bio *bio = &ioend->io_bio; > + struct iomap_ioend *split_ioend; > + struct bio *split; > + int sector_offset; > + unsigned int nr_segs; > + > + if (is_append) { > + struct queue_limits *lim = bdev_limits(bio->bi_bdev); > + > + sector_offset = bio_split_rw_at(bio, lim, &nr_segs, > + min(lim->max_zone_append_sectors << SECTOR_SHIFT, > + *alloc_len)); > + if (!sector_offset) > + return NULL; > + } else { > + if (bio->bi_iter.bi_size <= *alloc_len) > + return NULL; > + sector_offset = *alloc_len >> SECTOR_SHIFT; > + } > + > + /* ensure the split ioend is still block size aligned */ > + sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, > + i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; > + > + split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); > + if (!split) > + return NULL; > + split->bi_private = bio->bi_private; > + split->bi_end_io = bio->bi_end_io; > + > + split_ioend = iomap_init_ioend(ioend->io_inode, split, ioend->io_offset, > + ioend->io_flags); > + split_ioend->io_parent = ioend; > + > + atomic_inc(&ioend->io_remaining); > + ioend->io_offset += split_ioend->io_size; > + ioend->io_size -= split_ioend->io_size; > + > + split_ioend->io_sector = ioend->io_sector; > + if (!is_append) > + ioend->io_sector += (split_ioend->io_size >> SECTOR_SHIFT); > + > + *alloc_len -= split->bi_iter.bi_size; > + return split_ioend; > +} > +EXPORT_SYMBOL_GPL(iomap_split_ioend); > diff --git a/include/linux/iomap.h b/include/linux/iomap.h > index 173d490c20ba..eaa8cb9083eb 100644 > --- a/include/linux/iomap.h > +++ b/include/linux/iomap.h > @@ -354,6 +354,9 @@ struct iomap_ioend { > struct list_head io_list; /* next ioend in chain */ > u16 io_flags; /* IOMAP_IOEND_* */ > struct inode *io_inode; /* file being written to */ > + atomic_t io_remaining; /* completetion defer count */ > + int io_error; /* stashed away status */ > + struct iomap_ioend *io_parent; /* parent for completions */ > size_t io_size; /* size of the extent */ > loff_t io_offset; /* offset in the file */ > sector_t io_sector; /* start sector of ioend */ > @@ -404,6 +407,10 @@ struct iomap_writepage_ctx { > u32 nr_folios; /* folios added to the ioend */ > }; > > +struct iomap_ioend *iomap_init_ioend(struct inode *inode, struct bio *bio, > + loff_t file_offset, u16 flags); > +struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, bool is_append, > + unsigned int *alloc_len); > void iomap_finish_ioends(struct iomap_ioend *ioend, int error); > void iomap_ioend_try_merge(struct iomap_ioend *ioend, > struct list_head *more_ioends); > @@ -475,4 +482,6 @@ int iomap_swapfile_activate(struct swap_info_struct *sis, > # define iomap_swapfile_activate(sis, swapfile, pagespan, ops) (-EIO) > #endif /* CONFIG_SWAP */ > > +extern struct bio_set iomap_ioend_bioset; > + > #endif /* LINUX_IOMAP_H */ > -- > 2.45.2 > >