When the block layer checks gap alignment constraints for a partially consumed iovec iterator — as can happen during large direct I/O submissions that get split across multiple bio's — iov_iter_gap_alignment() uses the raw iov_base and iov_len of the first segment without adjusting for iov_offset. This reports the alignment of already-consumed data rather than the remaining portion, which can cause the block layer to apply incorrect gap alignment restrictions. Found by comparing the first-segment handling in iov_iter_gap_alignment() against iov_iter_alignment_iovec(), which correctly applies iov_offset via its skip variable. Apply iov_offset to the base address and length of the first segment so that gap alignment reflects the actual remaining data. Signed-off-by: Josh Law --- lib/iov_iter.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 375512beefc5..18561108aa3a 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -883,14 +883,16 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i) for (k = 0; k < i->nr_segs; k++) { const struct iovec *iov = iter_iov(i) + k; - if (iov->iov_len) { - unsigned long base = (unsigned long)iov->iov_base; + size_t skip = k ? 0 : i->iov_offset; + size_t len = iov->iov_len - skip; + if (len) { + unsigned long base = (unsigned long)iov->iov_base + skip; if (v) // if not the first one res |= base | v; // this start | previous end - v = base + iov->iov_len; - if (size <= iov->iov_len) + v = base + len; + if (size <= len) break; - size -= iov->iov_len; + size -= len; } } return res; -- 2.34.1