Pavel reported that iov_iter_npages is a bit heavy in case of bvec iter. Turns out it isn't necessary to iterate every page in the bvec iter, and we call iov_iter_npages() just for figuring out how many bio vecs need to be allocated. And we can simply map each vector in bvec iter to bio's vec, so just return iter->nr_segs from bio_iov_iter_nvecs() for bvec iter. This patch is based on Mathew's post: https://lore.kernel.org/linux-block/20201120123931.GN29991@xxxxxxxxxxxxxxxxxxxx/ Reviewed-by: Pavel Begunkov <asml.silence@xxxxxxxxx> Reviewed-by: Christoph Hellwig <hch@xxxxxxxxxxxxx> Cc: Matthew Wilcox <willy@xxxxxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Signed-off-by: Ming Lei <ming.lei@xxxxxxxxxx> --- fs/block_dev.c | 4 ++-- fs/iomap/direct-io.c | 4 ++-- include/linux/bio.h | 10 ++++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 9e56ee1f2652..fbcc900229af 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -417,7 +417,7 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages) dio->size += bio->bi_iter.bi_size; pos += bio->bi_iter.bi_size; - nr_pages = iov_iter_npages(iter, BIO_MAX_PAGES); + nr_pages = bio_iov_iter_nvecs(iter, BIO_MAX_PAGES); if (!nr_pages) { bool polled = false; @@ -482,7 +482,7 @@ blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { int nr_pages; - nr_pages = iov_iter_npages(iter, BIO_MAX_PAGES + 1); + nr_pages = bio_iov_iter_nvecs(iter, BIO_MAX_PAGES + 1); if (!nr_pages) return 0; if (is_sync_kiocb(iocb) && nr_pages <= BIO_MAX_PAGES) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 933f234d5bec..0635469e811a 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -250,7 +250,7 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length, orig_count = iov_iter_count(dio->submit.iter); iov_iter_truncate(dio->submit.iter, length); - nr_pages = iov_iter_npages(dio->submit.iter, BIO_MAX_PAGES); + nr_pages = bio_iov_iter_nvecs(dio->submit.iter, BIO_MAX_PAGES); if (nr_pages <= 0) { ret = nr_pages; goto out; @@ -308,7 +308,7 @@ iomap_dio_bio_actor(struct inode *inode, loff_t pos, loff_t length, dio->size += n; copied += n; - nr_pages = iov_iter_npages(dio->submit.iter, BIO_MAX_PAGES); + nr_pages = bio_iov_iter_nvecs(dio->submit.iter, BIO_MAX_PAGES); iomap_dio_submit_bio(dio, iomap, bio, pos); pos += n; } while (nr_pages); diff --git a/include/linux/bio.h b/include/linux/bio.h index 1edda614f7ce..8a7f44006f65 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -10,6 +10,7 @@ #include <linux/ioprio.h> /* struct bio, bio_vec and BIO_* flags are defined in blk_types.h */ #include <linux/blk_types.h> +#include <linux/uio.h> #define BIO_DEBUG @@ -820,4 +821,13 @@ static inline void bio_set_polled(struct bio *bio, struct kiocb *kiocb) bio->bi_opf |= REQ_NOWAIT; } +static inline int bio_iov_iter_nvecs(const struct iov_iter *i, int maxvecs) +{ + if (!iov_iter_count(i)) + return 0; + if (iov_iter_is_bvec(i)) + return min_t(int, maxvecs, i->nr_segs); + return iov_iter_npages(i, maxvecs); +} + #endif /* __LINUX_BIO_H */ -- 2.28.0