aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorNamjae Jeon <namjae.jeon@samsung.com>2014-05-12 08:12:25 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-05-12 08:12:25 -0400
commit1c8349a17137b93f0a83f276c764a6df1b9a116e (patch)
tree27a59d4a8ba3ce8cf426a3c55b07d259eb356598 /fs/ext4
parent236f5ecb4a5876977837376729a7599cc1ab2cc3 (diff)
ext4: fix data integrity sync in ordered mode
When we perform a data integrity sync we tag all the dirty pages with PAGECACHE_TAG_TOWRITE at start of ext4_da_writepages. Later we check for this tag in write_cache_pages_da and creates a struct mpage_da_data containing contiguously indexed pages tagged with this tag and sync these pages with a call to mpage_da_map_and_submit. This process is done in while loop until all the PAGECACHE_TAG_TOWRITE pages are synced. We also do journal start and stop in each iteration. journal_stop could initiate journal commit which would call ext4_writepage which in turn will call ext4_bio_write_page even for delayed OR unwritten buffers. When ext4_bio_write_page is called for such buffers, even though it does not sync them but it clears the PAGECACHE_TAG_TOWRITE of the corresponding page and hence these pages are also not synced by the currently running data integrity sync. We will end up with dirty pages although sync is completed. This could cause a potential data loss when the sync call is followed by a truncate_pagecache call, which is exactly the case in collapse_range. (It will cause generic/127 failure in xfstests) To avoid this issue, we can use set_page_writeback_keepwrite instead of set_page_writeback, which doesn't clear TOWRITE tag. Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Ashish Sangwan <a.sangwan@samsung.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Reviewed-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/ext4.h3
-rw-r--r--fs/ext4/inode.c6
-rw-r--r--fs/ext4/page-io.c8
3 files changed, 12 insertions, 5 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1d08a1b51bdd..aeda5061a59a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2772,7 +2772,8 @@ extern void ext4_io_submit(struct ext4_io_submit *io);
2772extern int ext4_bio_write_page(struct ext4_io_submit *io, 2772extern int ext4_bio_write_page(struct ext4_io_submit *io,
2773 struct page *page, 2773 struct page *page,
2774 int len, 2774 int len,
2775 struct writeback_control *wbc); 2775 struct writeback_control *wbc,
2776 bool keep_towrite);
2776 2777
2777/* mmp.c */ 2778/* mmp.c */
2778extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); 2779extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9b3c52fbe86d..04dd2de10796 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1846,6 +1846,7 @@ static int ext4_writepage(struct page *page,
1846 struct buffer_head *page_bufs = NULL; 1846 struct buffer_head *page_bufs = NULL;
1847 struct inode *inode = page->mapping->host; 1847 struct inode *inode = page->mapping->host;
1848 struct ext4_io_submit io_submit; 1848 struct ext4_io_submit io_submit;
1849 bool keep_towrite = false;
1849 1850
1850 trace_ext4_writepage(page); 1851 trace_ext4_writepage(page);
1851 size = i_size_read(inode); 1852 size = i_size_read(inode);
@@ -1876,6 +1877,7 @@ static int ext4_writepage(struct page *page,
1876 unlock_page(page); 1877 unlock_page(page);
1877 return 0; 1878 return 0;
1878 } 1879 }
1880 keep_towrite = true;
1879 } 1881 }
1880 1882
1881 if (PageChecked(page) && ext4_should_journal_data(inode)) 1883 if (PageChecked(page) && ext4_should_journal_data(inode))
@@ -1892,7 +1894,7 @@ static int ext4_writepage(struct page *page,
1892 unlock_page(page); 1894 unlock_page(page);
1893 return -ENOMEM; 1895 return -ENOMEM;
1894 } 1896 }
1895 ret = ext4_bio_write_page(&io_submit, page, len, wbc); 1897 ret = ext4_bio_write_page(&io_submit, page, len, wbc, keep_towrite);
1896 ext4_io_submit(&io_submit); 1898 ext4_io_submit(&io_submit);
1897 /* Drop io_end reference we got from init */ 1899 /* Drop io_end reference we got from init */
1898 ext4_put_io_end_defer(io_submit.io_end); 1900 ext4_put_io_end_defer(io_submit.io_end);
@@ -1911,7 +1913,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
1911 else 1913 else
1912 len = PAGE_CACHE_SIZE; 1914 len = PAGE_CACHE_SIZE;
1913 clear_page_dirty_for_io(page); 1915 clear_page_dirty_for_io(page);
1914 err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc); 1916 err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
1915 if (!err) 1917 if (!err)
1916 mpd->wbc->nr_to_write--; 1918 mpd->wbc->nr_to_write--;
1917 mpd->first_page++; 1919 mpd->first_page++;
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index c18d95b50540..4cb2743cb2e3 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -401,7 +401,8 @@ submit_and_retry:
401int ext4_bio_write_page(struct ext4_io_submit *io, 401int ext4_bio_write_page(struct ext4_io_submit *io,
402 struct page *page, 402 struct page *page,
403 int len, 403 int len,
404 struct writeback_control *wbc) 404 struct writeback_control *wbc,
405 bool keep_towrite)
405{ 406{
406 struct inode *inode = page->mapping->host; 407 struct inode *inode = page->mapping->host;
407 unsigned block_start, blocksize; 408 unsigned block_start, blocksize;
@@ -414,7 +415,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
414 BUG_ON(!PageLocked(page)); 415 BUG_ON(!PageLocked(page));
415 BUG_ON(PageWriteback(page)); 416 BUG_ON(PageWriteback(page));
416 417
417 set_page_writeback(page); 418 if (keep_towrite)
419 set_page_writeback_keepwrite(page);
420 else
421 set_page_writeback(page);
418 ClearPageError(page); 422 ClearPageError(page);
419 423
420 /* 424 /*