]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: refine the unaligned check for always COW inodes in xfs_file_dio_write
authorChristoph Hellwig <hch@lst.de>
Fri, 27 Oct 2023 07:58:24 +0000 (09:58 +0200)
committerChristoph Hellwig <hch@lst.de>
Mon, 3 Mar 2025 15:16:44 +0000 (08:16 -0700)
For always COW inodes we also must check the alignment of each individual
iovec segment, as they could end up with different I/Os due to the way
bio_iov_iter_get_pages works, and we'd then overwrite an already written
block.  The existing always_cow sysctl based code doesn't catch this
because nothing enforces that blocks aren't rewritten, but for zoned XFS
on sequential write required zones this is a hard error.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
fs/xfs/xfs_file.c

index a81c3e943f20640f4deb184227de61812c58fbb3..d66eea31b60ab88c4726d8277525d007abf8ba24 100644 (file)
@@ -721,7 +721,16 @@ xfs_file_dio_write(
        /* direct I/O must be aligned to device logical sector size */
        if ((iocb->ki_pos | count) & target->bt_logical_sectormask)
                return -EINVAL;
-       if ((iocb->ki_pos | count) & ip->i_mount->m_blockmask)
+
+       /*
+        * For always COW inodes we also must check the alignment of each
+        * individual iovec segment, as they could end up with different
+        * I/Os due to the way bio_iov_iter_get_pages works, and we'd
+        * then overwrite an already written block.
+        */
+       if (((iocb->ki_pos | count) & ip->i_mount->m_blockmask) ||
+           (xfs_is_always_cow_inode(ip) &&
+            (iov_iter_alignment(from) & ip->i_mount->m_blockmask)))
                return xfs_file_dio_write_unaligned(ip, iocb, from);
        return xfs_file_dio_write_aligned(ip, iocb, from);
 }