]>
Commit | Line | Data |
---|---|---|
ebe9a58d GKH |
1 | From 57a0da28ced8707cb9f79f071a016b9d005caf5a Mon Sep 17 00:00:00 2001 |
2 | From: Lukas Czerner <lczerner@redhat.com> | |
3 | Date: Fri, 10 May 2019 21:45:33 -0400 | |
4 | Subject: ext4: fix data corruption caused by overlapping unaligned and aligned IO | |
5 | ||
6 | From: Lukas Czerner <lczerner@redhat.com> | |
7 | ||
8 | commit 57a0da28ced8707cb9f79f071a016b9d005caf5a upstream. | |
9 | ||
10 | Unaligned AIO must be serialized because the zeroing of partial blocks | |
11 | of unaligned AIO can result in data corruption in case it's overlapping | |
12 | another in flight IO. | |
13 | ||
14 | Currently we wait for all unwritten extents before we submit unaligned | |
15 | AIO which protects data in case of unaligned AIO is following overlapping | |
16 | IO. However if a unaligned AIO is followed by overlapping aligned AIO we | |
17 | can still end up corrupting data. | |
18 | ||
19 | To fix this, we must make sure that the unaligned AIO is the only IO in | |
20 | flight by waiting for unwritten extents conversion not just before the | |
21 | IO submission, but right after it as well. | |
22 | ||
23 | This problem can be reproduced by xfstest generic/538 | |
24 | ||
25 | Signed-off-by: Lukas Czerner <lczerner@redhat.com> | |
26 | Signed-off-by: Theodore Ts'o <tytso@mit.edu> | |
27 | Cc: stable@kernel.org | |
28 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
29 | ||
30 | --- | |
31 | fs/ext4/file.c | 7 +++++++ | |
32 | 1 file changed, 7 insertions(+) | |
33 | ||
34 | --- a/fs/ext4/file.c | |
35 | +++ b/fs/ext4/file.c | |
36 | @@ -264,6 +264,13 @@ ext4_file_write_iter(struct kiocb *iocb, | |
37 | } | |
38 | ||
39 | ret = __generic_file_write_iter(iocb, from); | |
40 | + /* | |
41 | + * Unaligned direct AIO must be the only IO in flight. Otherwise | |
42 | + * overlapping aligned IO after unaligned might result in data | |
43 | + * corruption. | |
44 | + */ | |
45 | + if (ret == -EIOCBQUEUED && unaligned_aio) | |
46 | + ext4_unwritten_wait(inode); | |
47 | inode_unlock(inode); | |
48 | ||
49 | if (ret > 0) |