FAILED: patch "[PATCH] block: validate user space vectors during extraction" failed to apply to 6.18-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 14b007e178811db72fbb1ebb3535160db6ec1e6a
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090313-revolver-python-ce1a@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 14b007e178811db72fbb1ebb3535160db6ec1e6a Mon Sep 17 00:00:00 2001
From: Keith Busch <[email protected]>
Date: Mon, 20 Jul 2026 13:10:57 -0700
Subject: [PATCH] block: validate user space vectors during extraction
The bio-based drivers don't necessarily check the alignment split, and stacking block drivers don't always handle a misalignment detected after submitting the bio. Validate user vectors against the device's dma_alignment as the bio is built from the iov_iter, rejecting misaligned early with -EINVAL.
block/bio.c +53 -3
@@ -1221,10 +1221,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,return 0 ; } +#ifdef CONFIG_DEBUG_KERNEL +static inline bool bio_iov_bvec_aligned (const struct bio *bio, +unsigned mem_align_mask) +{ +struct bvec_iter iter; +struct bio_vec bv; + +/* +* Correct callers never break the alignment requirements, so this +* exhaustive check is only paid for in debug builds. +*/ +for_each_mp_bvec (bv, bio->bi_io_vec, iter, bio->bi_iter) +if ((bv.bv_offset | bv.bv_len) & mem_align_mask) +return false; +return true; +} +#else +static inline bool bio_iov_bvec_aligned (const struct bio *bio, +unsigned mem_align_mask) +{ +/* +* We forward the bio_vec as-is, so ITER_BVEC callers must provide +* segments already aligned to the device's DMA alignment. The only +* unchecked user-controllable offset that reaches here is an io_uring +* registered buffer where just the first segment can be unaligned +* (the rest is virtually contiguous), so checking only that one is +* sufficient to know if the entire vector is valid. +*/ +return !(mp_bvec_iter_offset (bio->bi_io_vec, bio->bi_iter) & + mem_align_mask); +} +#endif +/** * 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 +* @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 * * This takes either an iterator pointing to user memory, or one pointing to
@@ -1243,7 +1278,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,* is returned only if 0 pages could be pinned. */ int bio_iov_iter_get_pages (struct bio *bio,struct iov_iter *iter, -unsigned len_align_mask) +unsigned mem_align_mask,unsigned len_align_mask) {iov_iter_extraction_t flags =0 ;
@@ -1252,6 +1287,10 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,if (iov_iter_is_bvec (iter)) {bio_iov_bvec_set (bio, iter); + +if (!bio_iov_bvec_aligned (bio, mem_align_mask)) +return -EINVAL ; +iov_iter_advance (iter, bio->bi_iter.bi_size);return 0 ; }
@@ -1266,8 +1305,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, ret =iov_iter_extract_bvecs (iter, bio->bi_io_vec,BIO_MAX_SIZE - bio->bi_iter.bi_size, - &bio->bi_vcnt, bio->bi_max_vecs, flags); + &bio->bi_vcnt, bio->bi_max_vecs, + mem_align_mask, flags);if (ret <=0 ) { +/* +* A misaligned vector fails the whole I/O. Release any +* pages pinned by earlier iterations before returning +* since this bio won't be submitted to release them. +*/ +if (ret == -EINVAL ) { +bio_release_pages (bio, false); +bio_clear_flag (bio,BIO_PAGE_PINNED ); + bio->bi_vcnt =0 ; + }if (!bio->bi_vcnt)return ret;break ;
@@ -1380,7 +1430,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, do { ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len, - &bio->bi_vcnt, bio->bi_max_vecs - 1, 0); + &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0); if (ret <= 0) { if (!bio->bi_vcnt) goto out_folio_put;
block/blk-map.c +1 -1
@@ -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 ); + ret =bio_iov_iter_get_pages (bio, iter,0 ,0 );if (ret) goto out_put; ret =blk_rq_append_bio (rq, bio);
block/fops.c +1 -1
@@ -46,7 +46,7 @@ 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, +return bio_iov_iter_get_pages (bio, iter,bdev_dma_alignment (bdev),bdev_logical_block_size (bdev) -1 ); }
fs/iomap/direct-io.c +1 -0
@@ -358,6 +358,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, iomap_max_bio_size(&iter->iomap), alignment); else ret =bio_iov_iter_get_pages (bio, dio->submit.iter, +bdev_dma_alignment (bio->bi_bdev), alignment -1 );if (unlikely (ret)) goto out_put_bio;
include/linux/bio.h +1 -1
@@ -477,7 +477,7 @@ 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 len_align_mask); +unsigned mem_align_mask,unsigned len_align_mask);void bio_iov_bvec_set (struct bio *bio,const struct iov_iter *iter);void __bio_release_pages (struct bio *bio,bool mark_dirty);
include/linux/uio.h +9 -1
@@ -389,9 +389,17 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages, size_t maxsize, unsigned int maxpages, iov_iter_extraction_t extraction_flags, size_t *offset0); +/* +* Block-layer consumers (e.g. bio_iov_iter_get_pages()) require that the +* segments of an ITER_BVEC iterator are already aligned to the target device's +* DMA alignment, and forward them as-is. In-kernel users that build their own +* bvecs must not create sub-aligned segments; iov_iter_extract_bvecs() enforces +* the same for the segments it extracts via @mem_align_mask. +*/ ssize_t iov_iter_extract_bvecs (struct iov_iter *iter,struct bio_vec *bv,size_t max_size,unsigned short *nr_vecs, -unsigned short max_vecs,iov_iter_extraction_t extraction_flags); +unsigned short max_vecs,unsigned mem_align_mask, +iov_iter_extraction_t extraction_flags);/** * iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained
lib/iov_iter.c +8 -1
@@ -1904,6 +1904,8 @@ static unsigned int get_contig_folio_len(struct page **pages,* @max_size: maximum size to extract from @iter * @nr_vecs: number of vectors in @bv (on in and output) * @max_vecs: maximum vectors in @bv, including those filled before calling +* @mem_align_mask: reject with -EINVAL if the source address or +* length is not aligned to this mask * @extraction_flags: flags to qualify request * * Like iov_iter_extract_pages(), but returns physically contiguous ranges
@@ -1915,14 +1917,19 @@ static unsigned int get_contig_folio_len(struct page **pages,*/ ssize_t iov_iter_extract_bvecs (struct iov_iter *iter,struct bio_vec *bv,size_t max_size,unsigned short *nr_vecs, -unsigned short max_vecs,iov_iter_extraction_t extraction_flags) +unsigned short max_vecs,unsigned mem_align_mask, +iov_iter_extraction_t extraction_flags) { +unsigned long start = (unsigned long )iter_iov_addr (iter);unsigned short entries_left = max_vecs - *nr_vecs;unsigned short nr_pages, i =0 ;size_t left, offset, len;struct page **pages;ssize_t size; +if ((start |iter_iov_len (iter)) & mem_align_mask) +return -EINVAL ; +/* * Move page array up in the allocated memory for the bio vecs as far as * possible so that we can start filling biovecs from the beginning
Sasha Levin
maintainerPATCH 1/6 iov_iter: extract a iov_iter_extract_bvecs helper from bio code
From: Christoph Hellwig <[email protected]>
[ Upstream commit 91b73c458182801a8c9cf6135335e064567d1013 ]
Massage __bio_iov_iter_get_pages so that it doesn't need the bio, and move it to lib/iov_iter.c so that it can be used by block code for other things than filling a bio and by other subsystems like netfs.
Stable-dep-of: 14b007e17881 ("block: validate user space vectors during extraction")
--- block/bio.c | 141 ++++++-------------------------------------- include/linux/uio.h | 3 + lib/iov_iter.c | 98 ++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 122 deletions(-)
block/bio.c +19 -122
@@ -1166,122 +1166,6 @@ void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter)bio_set_flag (bio,BIO_CLONED ); } -static unsigned int get_contig_folio_len (unsigned int *num_pages, -struct page **pages,unsigned int i, -struct folio *folio,size_t left, -size_t offset) -{ -size_t bytes = left; -size_t contig_sz =min_t (size_t,PAGE_SIZE - offset, bytes); -unsigned int j; - -/* -* We might COW a single page in the middle of -* a large folio, so we have to check that all -* pages belong to the same folio. -*/ - bytes -= contig_sz; -for (j = i +1 ; j < i + *num_pages; j++) { -size_t next =min_t (size_t,PAGE_SIZE , bytes); - -if (page_folio (pages[j]) != folio || - pages[j] != pages[j -1 ] +1 ) { -break ; - } - contig_sz += next; - bytes -= next; - } - *num_pages = j - i; - -return contig_sz; -} - -#define PAGE_PTRS_PER_BVEC (sizeof(struct bio_vec) / sizeof(struct page *)) - -/** -* __bio_iov_iter_get_pages - pin user or kernel pages and add them to a bio -* @bio: bio to add pages to -* @iter: iov iterator describing the region to be mapped -* -* Extracts pages from *iter and appends them to @bio's bvec array. The pages -* will have to be cleaned up in the way indicated by the BIO_PAGE_PINNED flag. -* For a multi-segment *iter, this function only adds pages from the next -* non-empty segment of the iov iterator. -*/ -static int __bio_iov_iter_get_pages (struct bio *bio,struct iov_iter *iter) -{ -iov_iter_extraction_t extraction_flags =0 ; -unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt; -unsigned short entries_left = bio->bi_max_vecs - bio->bi_vcnt; -struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt; -struct page **pages = (struct page **)bv; -ssize_t size; -unsigned int num_pages, i =0 ; -size_t offset, folio_offset, left, len; -int ret =0 ; - -/* -* Move page array up in the allocated memory for the bio vecs as far as -* possible so that we can start filling biovecs from the beginning -* without overwriting the temporary page array. -*/ -BUILD_BUG_ON (PAGE_PTRS_PER_BVEC <2 ); - pages += entries_left * (PAGE_PTRS_PER_BVEC -1 ); - -if (bio->bi_bdev &&blk_queue_pci_p2pdma (bio->bi_bdev->bd_disk->queue)) - extraction_flags |=ITER_ALLOW_P2PDMA ; - - size =iov_iter_extract_pages (iter, &pages, -UINT_MAX - bio->bi_iter.bi_size, - nr_pages, extraction_flags, &offset); -if (unlikely (size <=0 )) -return size ? size : -EFAULT ; - - nr_pages =DIV_ROUND_UP (offset + size,PAGE_SIZE ); -for (left = size, i =0 ; left >0 ; left -= len, i += num_pages) { -struct page *page = pages[i]; -struct folio *folio =page_folio (page); -unsigned int old_vcnt = bio->bi_vcnt; - - folio_offset = ((size_t )folio_page_idx (folio, page) << -PAGE_SHIFT ) + offset; - - len =min (folio_size (folio) - folio_offset, left); - - num_pages =DIV_ROUND_UP (offset + len,PAGE_SIZE ); - -if (num_pages >1 ) - len =get_contig_folio_len (&num_pages, pages, i, - folio, left, offset); - -if (!bio_add_folio (bio, folio, len, folio_offset)) { -WARN_ON_ONCE (1 ); - ret = -EINVAL ; - goto out; - } - -if (bio_flagged (bio,BIO_PAGE_PINNED )) { -/* -* We're adding another fragment of a page that already -* was part of the last segment. Undo our pin as the -* page was pinned when an earlier fragment of it was -* added to the bio and __bio_release_pages expects a -* single pin per page. -*/ -if (offset && bio->bi_vcnt == old_vcnt) -unpin_user_folio (folio,1 ); - } - offset =0 ; - } - -iov_iter_revert (iter, left); -out: -while (i < nr_pages) -bio_release_page (bio, pages[i++]); - -return ret; -} -/* * Aligns the bio size to the len_align_mask, releasing excessive bio vecs that * __bio_iov_iter_get_pages may have inserted, and reverts the trimmed length
@@ -1339,7 +1223,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,int bio_iov_iter_get_pages (struct bio *bio,struct iov_iter *iter,unsigned len_align_mask) { -int ret =0 ; +iov_iter_extraction_t flags =0 ;if (WARN_ON_ONCE (bio_flagged (bio,BIO_CLONED )))return -EIO ;
@@ -1352,13 +1236,26 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,if (iov_iter_extract_will_pin (iter))bio_set_flag (bio,BIO_PAGE_PINNED ); +if (bio->bi_bdev &&blk_queue_pci_p2pdma (bio->bi_bdev->bd_disk->queue)) + flags |=ITER_ALLOW_P2PDMA ; +do { - ret =__bio_iov_iter_get_pages (bio, iter); - }while (!ret &&iov_iter_count (iter) && !bio_full (bio,0 )); +ssize_t ret; + + ret =iov_iter_extract_bvecs (iter, bio->bi_io_vec, +UINT_MAX - bio->bi_iter.bi_size, + &bio->bi_vcnt, bio->bi_max_vecs, flags); +if (ret <=0 ) { +if (!bio->bi_vcnt) +return ret; +break ; + } + bio->bi_iter.bi_size += ret; + }while (iov_iter_count (iter) && !bio_full (bio,0 )); -if (bio->bi_vcnt) -return bio_iov_iter_align_down (bio, iter, len_align_mask); -return ret; +if (is_pci_p2pdma_page (bio->bi_io_vec->bv_page)) + bio->bi_opf |=REQ_NOMERGE ; +return bio_iov_iter_align_down (bio, iter, len_align_mask); }static void submit_bio_wait_endio (struct bio *bio)
include/linux/uio.h +3 -0
@@ -389,6 +389,9 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages, size_t maxsize, unsigned int maxpages, iov_iter_extraction_t extraction_flags, size_t *offset0); +ssize_t iov_iter_extract_bvecs (struct iov_iter *iter,struct bio_vec *bv, +size_t max_size,unsigned short *nr_vecs, +unsigned short max_vecs,iov_iter_extraction_t extraction_flags);/** * iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained
lib/iov_iter.c +98 -0
101 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/6 block: add helpers to bounce buffer an iov_iter into bios
From: Christoph Hellwig <[email protected]>
[ Upstream commit 8dd5e7c75d7bb2635c7efd219ff20693fc24096a ]
Add helpers to implement bounce buffering of data into a bio to implement direct I/O for cases where direct user access is not possible because stable in-flight data is required. These are intended to be used as easily as bio_iov_iter_get_pages for the zero-copy path.
The write side is trivial and just copies data into the bounce buffer. The read side is a lot more complex because it needs to perform the copy from the completion context, and without preserving the iov_iter through the call chain. It steals a trick from the integrity data user interface and uses the first vector in the bio for the bounce buffer data that is fed to the block I/O stack, and uses the others to record the user buffer fragments.
Stable-dep-of: 14b007e17881 ("block: validate user space vectors during extraction")
--- block/bio.c | 179 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/bio.h | 26 +++++++ 2 files changed, 205 insertions(+)
block/bio.c +179 -0
@@ -1258,6 +1258,185 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,return bio_iov_iter_align_down (bio, iter, len_align_mask); } +static struct folio *folio_alloc_greedy (gfp_t gfp,size_t *size) +{ +struct folio *folio; + +while (*size >PAGE_SIZE ) { + folio =folio_alloc (gfp | __GFP_NORETRY,get_order (*size)); +if (folio) +return folio; + *size =rounddown_pow_of_two (*size -1 ); + } + +return folio_alloc (gfp,get_order (*size)); +} + +static void bio_free_folios (struct bio *bio) +{ +struct bio_vec *bv; +int i; + +bio_for_each_bvec_all (bv, bio, i) { +struct folio *folio =page_folio (bv->bv_page); + +if (!is_zero_folio (folio)) +folio_put (folio); + } +} + +static int bio_iov_iter_bounce_write (struct bio *bio,struct iov_iter *iter) +{ +size_t total_len =iov_iter_count (iter); + +if (WARN_ON_ONCE (bio_flagged (bio,BIO_CLONED ))) +return -EINVAL ; +if (WARN_ON_ONCE (bio->bi_iter.bi_size)) +return -EINVAL ; +if (WARN_ON_ONCE (bio->bi_vcnt >= bio->bi_max_vecs)) +return -EINVAL ; + +do { +size_t this_len =min (total_len,SZ_1M ); +struct folio *folio; + +if (this_len >PAGE_SIZE *2 ) + this_len =rounddown_pow_of_two (this_len); + +if (bio->bi_iter.bi_size >BIO_MAX_SIZE - this_len) +break ; + + folio =folio_alloc_greedy (GFP_KERNEL , &this_len); +if (!folio) +break ; +bio_add_folio_nofail (bio, folio, this_len,0 ); + +if (copy_from_iter (folio_address (folio), this_len, iter) != + this_len) { +bio_free_folios (bio); +return -EFAULT ; + } + + total_len -= this_len; + }while (total_len && bio->bi_vcnt < bio->bi_max_vecs); + +if (!bio->bi_iter.bi_size) +return -ENOMEM ; +return 0 ; +} + +static int bio_iov_iter_bounce_read (struct bio *bio,struct iov_iter *iter) +{ +size_t len =min (iov_iter_count (iter),SZ_1M ); +struct folio *folio; + + folio =folio_alloc_greedy (GFP_KERNEL , &len); +if (!folio) +return -ENOMEM ; + +do { +ssize_t ret; + + ret =iov_iter_extract_bvecs (iter, bio->bi_io_vec +1 , len, + &bio->bi_vcnt, bio->bi_max_vecs -1 ,0 ); +if (ret <=0 ) { +if (!bio->bi_vcnt) +return ret; +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 ); +return 0 ; +} + +/** +* bio_iov_iter_bounce - bounce buffer data from an iter into a bio +* @bio: bio to send +* @iter: iter to read from / write into +* +* 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. +*/ +int bio_iov_iter_bounce (struct bio *bio,struct iov_iter *iter) +{ +if (op_is_write (bio_op (bio))) +return bio_iov_iter_bounce_write (bio, iter); +return bio_iov_iter_bounce_read (bio, iter); +} + +static void bvec_unpin (struct bio_vec *bv,bool mark_dirty) +{ +struct folio *folio =page_folio (bv->bv_page); +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) +{ +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; + +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; + } + +if (bio_flagged (bio,BIO_PAGE_PINNED )) { +int i; + +for (i =0 ; i < bio->bi_vcnt; i++) +bvec_unpin (&bio->bi_io_vec[1 + i], mark_dirty); + } + +folio_put (page_folio (bio->bi_io_vec[0 ].bv_page)); +} + +/** +* 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); +} +static void submit_bio_wait_endio (struct bio *bio) {complete (bio->bi_private);
include/linux/bio.h +26 -0
@@ -401,6 +401,29 @@ static inline int bio_iov_vecs_to_alloc(struct iov_iter *iter, int max_segs)return iov_iter_npages (iter, max_segs); } +/** +* bio_iov_bounce_nr_vecs - calculate number of bvecs for a bounce bio +* @iter: iter to bounce from +* @op: REQ_OP_* for the bio +* +* Calculates how many bvecs are needed for the next bio to bounce from/to +* @iter. +*/ +static inline unsigned short +bio_iov_bounce_nr_vecs (struct iov_iter *iter,blk_opf_t op) +{ +/* +* We still need to bounce bvec iters, so don't special case them +* here unlike in bio_iov_vecs_to_alloc. +* +* For reads we need to use a vector for the bounce buffer, account +* for that here. +*/ +if (op_is_write (op)) +return iov_iter_npages (iter,BIO_MAX_VECS ); +return iov_iter_npages (iter,BIO_MAX_VECS -1 ) +1 ; +} +struct request_queue ; void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,
@@ -454,6 +477,9 @@ 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); +void bio_iov_iter_unbounce (struct bio *bio,bool is_error,bool mark_dirty); +extern void bio_copy_data_iter (struct bio *dst,struct bvec_iter *dst_iter,struct bio *src,struct bvec_iter *src_iter);extern void bio_copy_data (struct bio *dst,struct bio *src);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/6 iomap: simplify iomap_dio_bio_iter
In reply to Sasha LevinFrom: Christoph Hellwig <[email protected]>
[ Upstream commit 6e7a6c80198ead08b11aa6cdc92e60a42fc5895f ]
Use iov_iter_count to check if we need to continue as that just reads a field in the iov_iter, and only use bio_iov_vecs_to_alloc to calculate the actual number of vectors to allocate for the bio.
Stable-dep-of: 14b007e17881 ("block: validate user space vectors during extraction")
--- fs/iomap/direct-io.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
fs/iomap/direct-io.c +6 -7
@@ -344,7 +344,7 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)blk_opf_t bio_opf =REQ_SYNC |REQ_IDLE ;struct bio *bio;bool need_zeroout = false; -int nr_pages, ret =0 ; +int ret =0 ;u64 copied =0 ;size_t orig_count;
@@ -426,7 +426,6 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) goto out; } - nr_pages =bio_iov_vecs_to_alloc (dio->submit.iter,BIO_MAX_VECS ); do { size_t n;
@@ -439,7 +438,9 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) goto out; } - bio =iomap_dio_alloc_bio (iter, dio, nr_pages, bio_opf); + bio =iomap_dio_alloc_bio (iter, dio, +bio_iov_vecs_to_alloc (dio->submit.iter, +BIO_MAX_VECS ), bio_opf);fscrypt_set_bio_crypt_ctx (bio, inode, pos >> inode->i_blkbits,GFP_KERNEL ); bio->bi_iter.bi_sector =iomap_sector (iomap, pos);
@@ -481,16 +482,14 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) dio->size += n; copied += n; - nr_pages =bio_iov_vecs_to_alloc (dio->submit.iter, -BIO_MAX_VECS );/* * We can only poll for single bio I/Os. */ -if (nr_pages) +if (iov_iter_count (dio->submit.iter)) dio->iocb->ki_flags &= ~IOCB_HIPRI ;iomap_dio_submit_bio (iter, dio, bio, pos); pos += n; - }while (nr_pages); + }while (iov_iter_count (dio->submit.iter));/* * We need to zeroout the tail of a sub-block write if the extent type
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/6 iomap: split out the per-bio logic from iomap_dio_bio_iter
In reply to Sasha LevinFrom: Christoph Hellwig <[email protected]>
[ Upstream commit 2631c94602297090febd8f93d6f96d9d2045466d ]
Factor out a separate helper that builds and submits a single bio.
Stable-dep-of: 14b007e17881 ("block: validate user space vectors during extraction")
--- block/bio.c | 2 +- fs/iomap/direct-io.c | 157 ++++++++++++++++++++++---------------- include/linux/blk_types.h | 3 +- include/linux/iomap.h | 9 +++ 4 files changed, 104 insertions(+), 67 deletions(-)
block/bio.c +1 -1
@@ -1243,7 +1243,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, -UINT_MAX - bio->bi_iter.bi_size, +BIO_MAX_SIZE - bio->bi_iter.bi_size, &bio->bi_vcnt, bio->bi_max_vecs, flags);if (ret <=0 ) {if (!bio->bi_vcnt)
fs/iomap/direct-io.c +92 -65
238 lines not on this page · raw message
include/linux/blk_types.h +2 -1
@@ -269,7 +269,8 @@ struct bio { };#define BIO_RESET_BYTES offsetof(struct bio, bi_max_vecs) -#define BIO_MAX_SECTORS (UINT_MAX >> SECTOR_SHIFT) +#define BIO_MAX_SIZE UINT_MAX +#define BIO_MAX_SECTORS (BIO_MAX_SIZE >> SECTOR_SHIFT)static inline struct bio_vec *bio_inline_vecs (struct bio *bio) {
include/linux/iomap.h +9 -0
@@ -518,6 +518,15 @@ struct iomap_dio_ops {*/ #define IOMAP_DIO_PARTIAL (1 << 2) +/* +* Bounce buffer instead of using zero copy access. +* +* This is needed if the device needs stable data to checksum or generate +* parity. The file system must hook into the I/O submission and offload +* completions to user context for reads when this is set. +*/ +#define IOMAP_DIO_BOUNCE (1 << 4) +ssize_t iomap_dio_rw (struct kiocb *iocb,struct iov_iter *iter,const struct iomap_ops *ops,const struct iomap_dio_ops *dops,unsigned int dio_flags,void *private,size_t done_before);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/6 iomap: rename IOMAP_DIO_DIRTY to IOMAP_DIO_USER_BACKED
In reply to Sasha LevinFrom: Christoph Hellwig <[email protected]>
[ Upstream commit c96b8b220271024c04289d6d9779dc2ccbd12be2 ]
Match the more descriptive iov_iter terminology instead of encoding what we do with them for reads only.
Stable-dep-of: 14b007e17881 ("block: validate user space vectors during extraction")
--- fs/iomap/direct-io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
fs/iomap/direct-io.c +5 -5
@@ -22,7 +22,7 @@#define IOMAP_DIO_WRITE_THROUGH (1U << 28)#define IOMAP_DIO_NEED_SYNC (1U << 29)#define IOMAP_DIO_WRITE (1U << 30) -#define IOMAP_DIO_DIRTY (1U << 31) +#define IOMAP_DIO_USER_BACKED (1U << 31)/* * Used for sub block zeroing in iomap_dio_zero()
@@ -234,7 +234,7 @@ void iomap_dio_bio_end_io(struct bio *bio) {struct iomap_dio *dio = bio->bi_private;bool should_bounce = (dio->flags &IOMAP_DIO_BOUNCE ); -bool should_dirty = (dio->flags &IOMAP_DIO_DIRTY ); +bool should_dirty = (dio->flags &IOMAP_DIO_USER_BACKED );if (bio->bi_status)iomap_dio_set_error (dio,blk_status_to_errno (bio->bi_status));
@@ -258,7 +258,7 @@ u32 iomap_finish_ioend_direct(struct iomap_ioend *ioend) {struct iomap_dio *dio = ioend->io_bio.bi_private;bool should_bounce = (dio->flags &IOMAP_DIO_BOUNCE ); -bool should_dirty = (dio->flags &IOMAP_DIO_DIRTY ); +bool should_dirty = (dio->flags &IOMAP_DIO_USER_BACKED );u32 vec_count = ioend->io_bio.bi_vcnt;if (ioend->io_error)
@@ -385,7 +385,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,if (dio->flags &IOMAP_DIO_WRITE )task_io_account_write (ret); -else if ((dio->flags &IOMAP_DIO_DIRTY ) && +else if ((dio->flags &IOMAP_DIO_USER_BACKED ) && !(dio->flags &IOMAP_DIO_BOUNCE ))bio_set_pages_dirty (bio);
@@ -688,7 +688,7 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, goto out_free_dio;if (user_backed_iter (iter)) - dio->flags |=IOMAP_DIO_DIRTY ; + dio->flags |=IOMAP_DIO_USER_BACKED ; ret =kiocb_write_and_wait (iocb, iomi.len);if (ret)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 6/6 block: validate user space vectors during extraction
In reply to Sasha LevinFrom: Keith Busch <[email protected]>
[ Upstream commit 14b007e178811db72fbb1ebb3535160db6ec1e6a ]
The bio-based drivers don't necessarily check the alignment split, and stacking block drivers don't always handle a misalignment detected after submitting the bio. Validate user vectors against the device's dma_alignment as the bio is built from the iov_iter, rejecting misaligned early with -EINVAL.
--- block/bio.c | 56 +++++++++++++++++++++++++++++++++++++++++--- block/blk-map.c | 2 +- block/fops.c | 2 +- fs/iomap/direct-io.c | 1 + include/linux/bio.h | 2 +- include/linux/uio.h | 10 +++++++- lib/iov_iter.c | 9 ++++++- 7 files changed, 74 insertions(+), 8 deletions(-)
block/bio.c +53 -3
@@ -1199,10 +1199,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,return 0 ; } +#ifdef CONFIG_DEBUG_KERNEL +static inline bool bio_iov_bvec_aligned (const struct bio *bio, +unsigned mem_align_mask) +{ +struct bvec_iter iter; +struct bio_vec bv; + +/* +* Correct callers never break the alignment requirements, so this +* exhaustive check is only paid for in debug builds. +*/ +for_each_mp_bvec (bv, bio->bi_io_vec, iter, bio->bi_iter) +if ((bv.bv_offset | bv.bv_len) & mem_align_mask) +return false; +return true; +} +#else +static inline bool bio_iov_bvec_aligned (const struct bio *bio, +unsigned mem_align_mask) +{ +/* +* We forward the bio_vec as-is, so ITER_BVEC callers must provide +* segments already aligned to the device's DMA alignment. The only +* unchecked user-controllable offset that reaches here is an io_uring +* registered buffer where just the first segment can be unaligned +* (the rest is virtually contiguous), so checking only that one is +* sufficient to know if the entire vector is valid. +*/ +return !(mp_bvec_iter_offset (bio->bi_io_vec, bio->bi_iter) & + mem_align_mask); +} +#endif +/** * 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 +* @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 * * This takes either an iterator pointing to user memory, or one pointing to
@@ -1221,7 +1256,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,* is returned only if 0 pages could be pinned. */ int bio_iov_iter_get_pages (struct bio *bio,struct iov_iter *iter, -unsigned len_align_mask) +unsigned mem_align_mask,unsigned len_align_mask) {iov_iter_extraction_t flags =0 ;
@@ -1230,6 +1265,10 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,if (iov_iter_is_bvec (iter)) {bio_iov_bvec_set (bio, iter); + +if (!bio_iov_bvec_aligned (bio, mem_align_mask)) +return -EINVAL ; +iov_iter_advance (iter, bio->bi_iter.bi_size);return 0 ; }
@@ -1244,8 +1283,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, ret =iov_iter_extract_bvecs (iter, bio->bi_io_vec,BIO_MAX_SIZE - bio->bi_iter.bi_size, - &bio->bi_vcnt, bio->bi_max_vecs, flags); + &bio->bi_vcnt, bio->bi_max_vecs, + mem_align_mask, flags);if (ret <=0 ) { +/* +* A misaligned vector fails the whole I/O. Release any +* pages pinned by earlier iterations before returning +* since this bio won't be submitted to release them. +*/ +if (ret == -EINVAL ) { +bio_release_pages (bio, false); +bio_clear_flag (bio,BIO_PAGE_PINNED ); + bio->bi_vcnt =0 ; + }if (!bio->bi_vcnt)return ret;break ;
@@ -1338,7 +1388,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter)ssize_t ret; ret =iov_iter_extract_bvecs (iter, bio->bi_io_vec +1 , len, - &bio->bi_vcnt, bio->bi_max_vecs -1 ,0 ); + &bio->bi_vcnt, bio->bi_max_vecs -1 ,0 ,0 );if (ret <=0 ) {if (!bio->bi_vcnt)return ret;
block/blk-map.c +1 -1
@@ -287,7 +287,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 ); + ret =bio_iov_iter_get_pages (bio, iter,0 ,0 );if (ret) goto out_put; ret =blk_rq_append_bio (rq, bio);
block/fops.c +1 -1
@@ -46,7 +46,7 @@ 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, +return bio_iov_iter_get_pages (bio, iter,bdev_dma_alignment (bdev),bdev_logical_block_size (bdev) -1 ); }
fs/iomap/direct-io.c +1 -0
@@ -369,6 +369,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, ret =bio_iov_iter_bounce (bio, dio->submit.iter); else ret =bio_iov_iter_get_pages (bio, dio->submit.iter, +bdev_dma_alignment (bio->bi_bdev), alignment -1 );if (unlikely (ret)) goto out_put_bio;
include/linux/bio.h +1 -1
@@ -470,7 +470,7 @@ 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 len_align_mask); +unsigned mem_align_mask,unsigned len_align_mask);void bio_iov_bvec_set (struct bio *bio,const struct iov_iter *iter);void __bio_release_pages (struct bio *bio,bool mark_dirty);
include/linux/uio.h +9 -1
@@ -389,9 +389,17 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages, size_t maxsize, unsigned int maxpages, iov_iter_extraction_t extraction_flags, size_t *offset0); +/* +* Block-layer consumers (e.g. bio_iov_iter_get_pages()) require that the +* segments of an ITER_BVEC iterator are already aligned to the target device's +* DMA alignment, and forward them as-is. In-kernel users that build their own +* bvecs must not create sub-aligned segments; iov_iter_extract_bvecs() enforces +* the same for the segments it extracts via @mem_align_mask. +*/ ssize_t iov_iter_extract_bvecs (struct iov_iter *iter,struct bio_vec *bv,size_t max_size,unsigned short *nr_vecs, -unsigned short max_vecs,iov_iter_extraction_t extraction_flags); +unsigned short max_vecs,unsigned mem_align_mask, +iov_iter_extraction_t extraction_flags);/** * iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained
lib/iov_iter.c +8 -1
@@ -1874,6 +1874,8 @@ static unsigned int get_contig_folio_len(struct page **pages,* @max_size: maximum size to extract from @iter * @nr_vecs: number of vectors in @bv (on in and output) * @max_vecs: maximum vectors in @bv, including those filled before calling +* @mem_align_mask: reject with -EINVAL if the source address or +* length is not aligned to this mask * @extraction_flags: flags to qualify request * * Like iov_iter_extract_pages(), but returns physically contiguous ranges
@@ -1885,14 +1887,19 @@ static unsigned int get_contig_folio_len(struct page **pages,*/ ssize_t iov_iter_extract_bvecs (struct iov_iter *iter,struct bio_vec *bv,size_t max_size,unsigned short *nr_vecs, -unsigned short max_vecs,iov_iter_extraction_t extraction_flags) +unsigned short max_vecs,unsigned mem_align_mask, +iov_iter_extraction_t extraction_flags) { +unsigned long start = (unsigned long )iter_iov_addr (iter);unsigned short entries_left = max_vecs - *nr_vecs;unsigned short nr_pages, i =0 ;size_t left, offset, len;struct page **pages;ssize_t size; +if ((start |iter_iov_len (iter)) & mem_align_mask) +return -EINVAL ; +/* * Move page array up in the allocated memory for the bio vecs as far as * possible so that we can start filling biovecs from the beginning
Signature · 1 line
2.53.0