Skip to content

Commit cd4842f

Browse files
namjaejeonBrad Figg
authored andcommitted
ext4: fix data integrity sync in ordered mode
BugLink: http://bugs.launchpad.net/bugs/1347088 commit 1c8349a upstream. 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. 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> Signed-off-by: Kamal Mostafa <kamal@canonical.com> Signed-off-by: Brad Figg <brad.figg@canonical.com>
1 parent 844bb0d commit cd4842f

File tree

5 files changed

+29
-11
lines changed

5 files changed

+29
-11
lines changed

fs/ext4/ext4.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2764,7 +2764,8 @@ extern void ext4_io_submit(struct ext4_io_submit *io);
27642764
extern int ext4_bio_write_page(struct ext4_io_submit *io,
27652765
struct page *page,
27662766
int len,
2767-
struct writeback_control *wbc);
2767+
struct writeback_control *wbc,
2768+
bool keep_towrite);
27682769

27692770
/* mmp.c */
27702771
extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);

fs/ext4/inode.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,7 @@ static int ext4_writepage(struct page *page,
18411841
struct buffer_head *page_bufs = NULL;
18421842
struct inode *inode = page->mapping->host;
18431843
struct ext4_io_submit io_submit;
1844+
bool keep_towrite = false;
18441845

18451846
trace_ext4_writepage(page);
18461847
size = i_size_read(inode);
@@ -1871,6 +1872,7 @@ static int ext4_writepage(struct page *page,
18711872
unlock_page(page);
18721873
return 0;
18731874
}
1875+
keep_towrite = true;
18741876
}
18751877

18761878
if (PageChecked(page) && ext4_should_journal_data(inode))
@@ -1887,7 +1889,7 @@ static int ext4_writepage(struct page *page,
18871889
unlock_page(page);
18881890
return -ENOMEM;
18891891
}
1890-
ret = ext4_bio_write_page(&io_submit, page, len, wbc);
1892+
ret = ext4_bio_write_page(&io_submit, page, len, wbc, keep_towrite);
18911893
ext4_io_submit(&io_submit);
18921894
/* Drop io_end reference we got from init */
18931895
ext4_put_io_end_defer(io_submit.io_end);
@@ -1906,7 +1908,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
19061908
else
19071909
len = PAGE_CACHE_SIZE;
19081910
clear_page_dirty_for_io(page);
1909-
err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc);
1911+
err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
19101912
if (!err)
19111913
mpd->wbc->nr_to_write--;
19121914
mpd->first_page++;

fs/ext4/page-io.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ static int io_submit_add_bh(struct ext4_io_submit *io,
401401
int ext4_bio_write_page(struct ext4_io_submit *io,
402402
struct page *page,
403403
int len,
404-
struct writeback_control *wbc)
404+
struct writeback_control *wbc,
405+
bool keep_towrite)
405406
{
406407
struct inode *inode = page->mapping->host;
407408
unsigned block_start, blocksize;
@@ -414,7 +415,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
414415
BUG_ON(!PageLocked(page));
415416
BUG_ON(PageWriteback(page));
416417

417-
set_page_writeback(page);
418+
if (keep_towrite)
419+
set_page_writeback_keepwrite(page);
420+
else
421+
set_page_writeback(page);
418422
ClearPageError(page);
419423

420424
/*

include/linux/page-flags.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,23 @@ CLEARPAGEFLAG(Uptodate, uptodate)
320320
extern void cancel_dirty_page(struct page *page, unsigned int account_size);
321321

322322
int test_clear_page_writeback(struct page *page);
323-
int test_set_page_writeback(struct page *page);
323+
int __test_set_page_writeback(struct page *page, bool keep_write);
324+
325+
#define test_set_page_writeback(page) \
326+
__test_set_page_writeback(page, false)
327+
#define test_set_page_writeback_keepwrite(page) \
328+
__test_set_page_writeback(page, true)
324329

325330
static inline void set_page_writeback(struct page *page)
326331
{
327332
test_set_page_writeback(page);
328333
}
329334

335+
static inline void set_page_writeback_keepwrite(struct page *page)
336+
{
337+
test_set_page_writeback_keepwrite(page);
338+
}
339+
330340
#ifdef CONFIG_PAGEFLAGS_EXTENDED
331341
/*
332342
* System with lots of page flags available. This allows separate

mm/page-writeback.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,7 +2398,7 @@ int test_clear_page_writeback(struct page *page)
23982398
return ret;
23992399
}
24002400

2401-
int test_set_page_writeback(struct page *page)
2401+
int __test_set_page_writeback(struct page *page, bool keep_write)
24022402
{
24032403
struct address_space *mapping = page_mapping(page);
24042404
int ret;
@@ -2423,9 +2423,10 @@ int test_set_page_writeback(struct page *page)
24232423
radix_tree_tag_clear(&mapping->page_tree,
24242424
page_index(page),
24252425
PAGECACHE_TAG_DIRTY);
2426-
radix_tree_tag_clear(&mapping->page_tree,
2427-
page_index(page),
2428-
PAGECACHE_TAG_TOWRITE);
2426+
if (!keep_write)
2427+
radix_tree_tag_clear(&mapping->page_tree,
2428+
page_index(page),
2429+
PAGECACHE_TAG_TOWRITE);
24292430
spin_unlock_irqrestore(&mapping->tree_lock, flags);
24302431
} else {
24312432
ret = TestSetPageWriteback(page);
@@ -2436,7 +2437,7 @@ int test_set_page_writeback(struct page *page)
24362437
return ret;
24372438

24382439
}
2439-
EXPORT_SYMBOL(test_set_page_writeback);
2440+
EXPORT_SYMBOL(__test_set_page_writeback);
24402441

24412442
/*
24422443
* Return true if any of the pages in the mapping are marked with the

0 commit comments

Comments
 (0)