Skip to content

Commit

Permalink
ext4: move keep_towrite handling to ext4_bio_write_page()
Browse files Browse the repository at this point in the history
When we are writing back page but we cannot for some reason write all
its buffers (e.g. because we cannot allocate blocks in current context) we
have to keep TOWRITE tag set in the mapping as otherwise racing
WB_SYNC_ALL writeback that could write these buffers can skip the page
and result in data loss.  We will need this logic for writeback during
transaction commit so move the logic from ext4_writepage() to
ext4_bio_write_page().

Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20221207112722.22220-2-jack@suse.cz
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Jan Kara authored and Theodore Ts'o committed Dec 9, 2022
1 parent 04e568a commit dff4ac7
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 21 deletions.
3 changes: 1 addition & 2 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -3757,8 +3757,7 @@ extern void ext4_end_io_rsv_work(struct work_struct *work);
extern void ext4_io_submit(struct ext4_io_submit *io);
extern int ext4_bio_write_page(struct ext4_io_submit *io,
struct page *page,
int len,
bool keep_towrite);
int len);
extern struct ext4_io_end_vec *ext4_alloc_io_end_vec(ext4_io_end_t *io_end);
extern struct ext4_io_end_vec *ext4_last_io_end_vec(ext4_io_end_t *io_end);

Expand Down
6 changes: 2 additions & 4 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,6 @@ static int ext4_writepage(struct page *page,
struct buffer_head *page_bufs = NULL;
struct inode *inode = page->mapping->host;
struct ext4_io_submit io_submit;
bool keep_towrite = false;

if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
folio_invalidate(folio, 0, folio_size(folio));
Expand Down Expand Up @@ -2074,7 +2073,6 @@ static int ext4_writepage(struct page *page,
unlock_page(page);
return 0;
}
keep_towrite = true;
}

if (PageChecked(page) && ext4_should_journal_data(inode))
Expand All @@ -2091,7 +2089,7 @@ static int ext4_writepage(struct page *page,
unlock_page(page);
return -ENOMEM;
}
ret = ext4_bio_write_page(&io_submit, page, len, keep_towrite);
ret = ext4_bio_write_page(&io_submit, page, len);
ext4_io_submit(&io_submit);
/* Drop io_end reference we got from init */
ext4_put_io_end_defer(io_submit.io_end);
Expand Down Expand Up @@ -2125,7 +2123,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
len = size & ~PAGE_MASK;
else
len = PAGE_SIZE;
err = ext4_bio_write_page(&mpd->io_submit, page, len, false);
err = ext4_bio_write_page(&mpd->io_submit, page, len);
if (!err)
mpd->wbc->nr_to_write--;
mpd->first_page++;
Expand Down
36 changes: 21 additions & 15 deletions fs/ext4/page-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,7 @@ static void io_submit_add_bh(struct ext4_io_submit *io,

int ext4_bio_write_page(struct ext4_io_submit *io,
struct page *page,
int len,
bool keep_towrite)
int len)
{
struct page *bounce_page = NULL;
struct inode *inode = page->mapping->host;
Expand All @@ -441,14 +440,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
int nr_submitted = 0;
int nr_to_submit = 0;
struct writeback_control *wbc = io->io_wbc;
bool keep_towrite = false;

BUG_ON(!PageLocked(page));
BUG_ON(PageWriteback(page));

if (keep_towrite)
set_page_writeback_keepwrite(page);
else
set_page_writeback(page);
ClearPageError(page);

/*
Expand Down Expand Up @@ -483,12 +479,17 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (!buffer_mapped(bh))
clear_buffer_dirty(bh);
/*
* Keeping dirty some buffer we cannot write? Make
* sure to redirty the page. This happens e.g. when
* doing writeout for transaction commit.
* Keeping dirty some buffer we cannot write? Make sure
* to redirty the page and keep TOWRITE tag so that
* racing WB_SYNC_ALL writeback does not skip the page.
* This happens e.g. when doing writeout for
* transaction commit.
*/
if (buffer_dirty(bh) && !PageDirty(page))
redirty_page_for_writepage(wbc, page);
if (buffer_dirty(bh)) {
if (!PageDirty(page))
redirty_page_for_writepage(wbc, page);
keep_towrite = true;
}
if (io->io_bio)
ext4_io_submit(io);
continue;
Expand All @@ -500,6 +501,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
nr_to_submit++;
} while ((bh = bh->b_this_page) != head);

/* Nothing to submit? Just unlock the page... */
if (!nr_to_submit)
goto unlock;

bh = head = page_buffers(page);

/*
Expand Down Expand Up @@ -550,6 +555,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
}
}

if (keep_towrite)
set_page_writeback_keepwrite(page);
else
set_page_writeback(page);

/* Now submit buffers to write */
do {
if (!buffer_async_write(bh))
Expand All @@ -558,11 +568,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
bounce_page ? bounce_page : page, bh);
nr_submitted++;
} while ((bh = bh->b_this_page) != head);

unlock:
unlock_page(page);
/* Nothing submitted - we have to end page writeback */
if (!nr_submitted)
end_page_writeback(page);
return ret;
}

0 comments on commit dff4ac7

Please sign in to comment.