summaryrefslogtreecommitdiff
authorLukas Czerner <lczerner@redhat.com>2019-05-11 01:45:33 (GMT)
committer Greg Kroah-Hartman <gregkh@linuxfoundation.org>2019-05-21 16:49:01 (GMT)
commitfa0897757db7e75dd60b63871249d9e34936f885 (patch)
tree2a02f4b1fa16dfd0e9945e46d347839ee46f4d00
parentab6d14e83db6e08c39a867a4cba378f7a774d273 (diff)
downloadcommon-fa0897757db7e75dd60b63871249d9e34936f885.zip
common-fa0897757db7e75dd60b63871249d9e34936f885.tar.gz
common-fa0897757db7e75dd60b63871249d9e34936f885.tar.bz2
ext4: fix data corruption caused by overlapping unaligned and aligned IO
commit 57a0da28ced8707cb9f79f071a016b9d005caf5a upstream. Unaligned AIO must be serialized because the zeroing of partial blocks of unaligned AIO can result in data corruption in case it's overlapping another in flight IO. Currently we wait for all unwritten extents before we submit unaligned AIO which protects data in case of unaligned AIO is following overlapping IO. However if a unaligned AIO is followed by overlapping aligned AIO we can still end up corrupting data. To fix this, we must make sure that the unaligned AIO is the only IO in flight by waiting for unwritten extents conversion not just before the IO submission, but right after it as well. This problem can be reproduced by xfstest generic/538 Signed-off-by: Lukas Czerner <lczerner@redhat.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat
-rw-r--r--fs/ext4/file.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index fe76d09..59d3ea7 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -163,6 +163,13 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
}
ret = __generic_file_write_iter(iocb, from);
+ /*
+ * Unaligned direct AIO must be the only IO in flight. Otherwise
+ * overlapping aligned IO after unaligned might result in data
+ * corruption.
+ */
+ if (ret == -EIOCBQUEUED && unaligned_aio)
+ ext4_unwritten_wait(inode);
inode_unlock(inode);
if (ret > 0)