summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2013-01-28 21:06:42 -0500
committerTheodore Ts'o <tytso@mit.edu>2013-01-28 21:06:42 -0500
commitfe386132f6731d02a45c380be0a3d339e6446cb5 (patch)
treeb28efc5faa2cad4823eaec0ef847a9446ef75a5b
parent8a850c3fb8d0f204eabc1a32b502f47d3c16eac4 (diff)
ext4: fix ext4_writepage() to achieve data=ordered guarantees
So far ext4_writepage() skipped writing pages that had any delayed or unwritten buffers attached. When blocksize < pagesize this breaks data=ordered mode guarantees as we can have a page with one freshly allocated buffer whose allocation is part of the committing transaction and another buffer in the page which is delayed or unwritten. So fix this problem by calling ext4_bio_writepage() anyway. It will submit mapped buffers and leave others alone. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/inode.c30
1 files changed, 18 insertions, 12 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 6824cb1bd1bb..86bf43d6dfcd 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1976,21 +1976,27 @@ static int ext4_writepage(struct page *page,
len = PAGE_CACHE_SIZE;
page_bufs = page_buffers(page);
+ /*
+ * We cannot do block allocation or other extent handling in this
+ * function. If there are buffers needing that, we have to redirty
+ * the page. But we may reach here when we do a journal commit via
+ * journal_submit_inode_data_buffers() and in that case we must write
+ * allocated buffers to achieve data=ordered mode guarantees.
+ */
if (ext4_walk_page_buffers(NULL, page_bufs, 0, len, NULL,
ext4_bh_delay_or_unwritten)) {
- /*
- * We don't want to do block allocation, so redirty
- * the page and return. We may reach here when we do
- * a journal commit via journal_submit_inode_data_buffers.
- * We can also reach here via shrink_page_list but it
- * should never be for direct reclaim so warn if that
- * happens
- */
- WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD)) ==
- PF_MEMALLOC);
redirty_page_for_writepage(wbc, page);
- unlock_page(page);
- return 0;
+ if (current->flags & PF_MEMALLOC) {
+ /*
+ * For memory cleaning there's no point in writing only
+ * some buffers. So just bail out. Warn if we came here
+ * from direct reclaim.
+ */
+ WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD))
+ == PF_MEMALLOC);
+ unlock_page(page);
+ return 0;
+ }
}
if (PageChecked(page) && ext4_should_journal_data(inode))