summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2026-10-30 07:21:23 +1030
committerChristian Brauner <brauner@kernel.org>2026-10-30 15:53:01 +0100
commit87d02f3e98a86abf70441b0a0418e01cb1d64b44 (patch)
treee1e3824cdd59018b8e0dba7418da8f7fc1d21eab
parent99f7d39384d6f1967f3dddf27aca47280f63d8d7 (diff)
downloadlinux-87d02f3e98a86abf70441b0a0418e01cb1d64b44.tar.gz
linux-87d02f3e98a86abf70441b0a0418e01cb1d64b44.zip
iomap: only follow dio alignment requirement if specified
After commit 4025185cf953 ("iomap: follow the alignment requirement for iomap_dio_hole_iter()"), generic/091 fails on both xfs and ext4. The reason is that ext4 and xfs (except COW write) do not require full block alignment for direct IO. They are totally fine reading a sub-block range. That commit fails any direct read of sub-block holes/prealloc, even those filesystems can handle it without any problem. Fix the regression by only follow the block alignment if the IOMAP_DIO_FSBLOCK_ALIGNED flag is set. Fixes: 4025185cf953 ("iomap: follow the alignment requirement for iomap_dio_hole_iter()") Reviewed-by: Pankaj Raghav(Samsung) <pankaj.raghav@linux.dev> Signed-off-by: Qu Wenruo <wqu@suse.com> Link: https://patch.msgid.link/0e89c49b6f7804081f4041007b81ac620a153830.1793307063.git.wqu@suse.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/iomap/direct-io.c26
-rw-r--r--include/linux/iomap.h3
2 files changed, 18 insertions, 11 deletions
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index 45bb24934f3d..ad041ac3252a 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -590,18 +590,24 @@ out:
static int iomap_dio_hole_iter(struct iomap_iter *iter, struct iomap_dio *dio)
{
loff_t copied = iov_iter_zero(iomap_length(iter), dio->submit.iter);
- unsigned int bs = i_blocksize(iter->inode);
- loff_t aligned_copied = round_down(copied, bs);
- /*
- * If fs block size is larger than page size, page fault failure
- * can cause @copied to be page aligned but not fs block aligned.
- */
- iov_iter_revert(dio->submit.iter, copied - aligned_copied);
- dio->size += aligned_copied;
- if (!aligned_copied)
+ if (dio->flags & IOMAP_DIO_FSBLOCK_ALIGNED) {
+ loff_t aligned_copied = round_down(copied,
+ i_blocksize(iter->inode));
+
+ /*
+ * For fs passing IOMAP_DIO_FSBLOCK_ALIGNED flag, if fs block
+ * size is larger than page size, page fault failure
+ * can cause @copied to be page aligned but not fs block
+ * aligned.
+ */
+ iov_iter_revert(dio->submit.iter, copied - aligned_copied);
+ copied = aligned_copied;
+ }
+ dio->size += copied;
+ if (!copied)
return -EFAULT;
- return iomap_iter_advance(iter, aligned_copied);
+ return iomap_iter_advance(iter, copied);
}
static int iomap_dio_inline_iter(struct iomap_iter *iomi, struct iomap_dio *dio)
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 59718f73c15a..5254430844b2 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -693,7 +693,8 @@ struct iomap_dio_ops {
#define IOMAP_DIO_PARTIAL (1 << 2)
/*
- * Ensure each bio is aligned to fs block size.
+ * Ensure every operation, including bio and hole zeroing, is aligned to fs
+ * block size.
*
* For filesystems which need to calculate/verify the checksum of each fs
* block. Otherwise they may not be able to handle unaligned bios.