[PATCH 5/7] lib/iov_iter: account for iov_offset in iov_iter_gap_alignment()
From: Josh Law
Date: Fri Mar 13 2026 - 14:11:18 EST
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 <objecting@xxxxxxxxxxxxx>
---
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