[PATCH 5/7] lib/iov_iter: account for iov_offset in iov_iter_gap_alignment()

Josh Law posted 7 patches 3 weeks, 3 days ago
[PATCH 5/7] lib/iov_iter: account for iov_offset in iov_iter_gap_alignment()
Posted by Josh Law 3 weeks, 3 days ago
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@objecting.org>
---
 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