1 ext4: use private version of page_zero_new_buffers() for data=journal mode
3 If there is a error while copying data from userspace into the page
4 cache during a write(2) system call, in data=journal mode, in
5 ext4_journalled_write_end() were using page_zero_new_buffers() from
6 fs/buffer.c. Unfortunately, this sets the buffer dirty flag, which is
7 no good if journalling is enabled. This is a long-standing bug that
8 goes back for years and years in ext3, but a combination of (a)
9 data=journal not being very common, (b) in many case it only results
10 in a warning message. and (c) only very rarely causes the kernel hang,
11 means that we only really noticed this as a problem when commit
12 998ef75ddb caused this failure to happen frequently enough to cause
13 generic/208 to fail when run in data=journal mode.
15 The fix is to have our own version of this function that doesn't call
16 mark_dirty_buffer(), since we will end up calling
17 ext4_handle_dirty_metadata() on the buffer head(s) in questions very
18 shortly afterwards in ext4_journalled_write_end().
20 Thanks to Dave Hansen and Linus Torvalds for helping to identify the
21 root cause of the problem.
23 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
24 Reviewed-by: Jan Kara <jack@suse.com>
26 fs/ext4/inode.c | 34 +++++++++++++++++++++++++++++++++-
27 1 file changed, 33 insertions(+), 1 deletion(-)
29 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
30 index ae52e32..0a589bb 100644
33 @@ -1181,6 +1181,38 @@ errout:
34 return ret ? ret : copied;
38 + * This is a private version of page_zero_new_buffers() which doesn't
39 + * set the buffer to be dirty, since in data=journalled mode we need
40 + * to call ext4_handle_dirty_metadata() instead.
42 +static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
44 + unsigned int block_start = 0, block_end;
45 + struct buffer_head *head, *bh;
47 + bh = head = page_buffers(page);
49 + block_end = block_start + bh->b_size;
50 + if (buffer_new(bh)) {
51 + if (block_end > from && block_start < to) {
52 + if (!PageUptodate(page)) {
53 + unsigned start, size;
55 + start = max(from, block_start);
56 + size = min(to, block_end) - start;
58 + zero_user(page, start, size);
59 + set_buffer_uptodate(bh);
61 + clear_buffer_new(bh);
64 + block_start = block_end;
65 + bh = bh->b_this_page;
66 + } while (bh != head);
69 static int ext4_journalled_write_end(struct file *file,
70 struct address_space *mapping,
71 loff_t pos, unsigned len, unsigned copied,
72 @@ -1207,7 +1239,7 @@ static int ext4_journalled_write_end(struct file *file,
74 if (!PageUptodate(page))
76 - page_zero_new_buffers(page, from+copied, to);
77 + zero_new_buffers(page, from+copied, to);
80 ret = ext4_walk_page_buffers(handle, page_buffers(page), from,