ext4: move keep_towrite handling to ext4_bio_write_page()
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>
This commit is contained in:
parent
04e568a3b3
commit
dff4ac75ee
3 changed files with 24 additions and 21 deletions
|
@ -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 void ext4_io_submit(struct ext4_io_submit *io);
|
||||||
extern int ext4_bio_write_page(struct ext4_io_submit *io,
|
extern int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
struct page *page,
|
struct page *page,
|
||||||
int len,
|
int len);
|
||||||
bool keep_towrite);
|
|
||||||
extern struct ext4_io_end_vec *ext4_alloc_io_end_vec(ext4_io_end_t *io_end);
|
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);
|
extern struct ext4_io_end_vec *ext4_last_io_end_vec(ext4_io_end_t *io_end);
|
||||||
|
|
||||||
|
|
|
@ -2016,7 +2016,6 @@ static int ext4_writepage(struct page *page,
|
||||||
struct buffer_head *page_bufs = NULL;
|
struct buffer_head *page_bufs = NULL;
|
||||||
struct inode *inode = page->mapping->host;
|
struct inode *inode = page->mapping->host;
|
||||||
struct ext4_io_submit io_submit;
|
struct ext4_io_submit io_submit;
|
||||||
bool keep_towrite = false;
|
|
||||||
|
|
||||||
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
|
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
|
||||||
folio_invalidate(folio, 0, folio_size(folio));
|
folio_invalidate(folio, 0, folio_size(folio));
|
||||||
|
@ -2074,7 +2073,6 @@ static int ext4_writepage(struct page *page,
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
keep_towrite = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageChecked(page) && ext4_should_journal_data(inode))
|
if (PageChecked(page) && ext4_should_journal_data(inode))
|
||||||
|
@ -2091,7 +2089,7 @@ static int ext4_writepage(struct page *page,
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
return -ENOMEM;
|
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);
|
ext4_io_submit(&io_submit);
|
||||||
/* Drop io_end reference we got from init */
|
/* Drop io_end reference we got from init */
|
||||||
ext4_put_io_end_defer(io_submit.io_end);
|
ext4_put_io_end_defer(io_submit.io_end);
|
||||||
|
@ -2125,7 +2123,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
|
||||||
len = size & ~PAGE_MASK;
|
len = size & ~PAGE_MASK;
|
||||||
else
|
else
|
||||||
len = PAGE_SIZE;
|
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)
|
if (!err)
|
||||||
mpd->wbc->nr_to_write--;
|
mpd->wbc->nr_to_write--;
|
||||||
mpd->first_page++;
|
mpd->first_page++;
|
||||||
|
|
|
@ -430,8 +430,7 @@ submit_and_retry:
|
||||||
|
|
||||||
int ext4_bio_write_page(struct ext4_io_submit *io,
|
int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
struct page *page,
|
struct page *page,
|
||||||
int len,
|
int len)
|
||||||
bool keep_towrite)
|
|
||||||
{
|
{
|
||||||
struct page *bounce_page = NULL;
|
struct page *bounce_page = NULL;
|
||||||
struct inode *inode = page->mapping->host;
|
struct inode *inode = page->mapping->host;
|
||||||
|
@ -441,14 +440,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
int nr_submitted = 0;
|
int nr_submitted = 0;
|
||||||
int nr_to_submit = 0;
|
int nr_to_submit = 0;
|
||||||
struct writeback_control *wbc = io->io_wbc;
|
struct writeback_control *wbc = io->io_wbc;
|
||||||
|
bool keep_towrite = false;
|
||||||
|
|
||||||
BUG_ON(!PageLocked(page));
|
BUG_ON(!PageLocked(page));
|
||||||
BUG_ON(PageWriteback(page));
|
BUG_ON(PageWriteback(page));
|
||||||
|
|
||||||
if (keep_towrite)
|
|
||||||
set_page_writeback_keepwrite(page);
|
|
||||||
else
|
|
||||||
set_page_writeback(page);
|
|
||||||
ClearPageError(page);
|
ClearPageError(page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -483,12 +479,17 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
if (!buffer_mapped(bh))
|
if (!buffer_mapped(bh))
|
||||||
clear_buffer_dirty(bh);
|
clear_buffer_dirty(bh);
|
||||||
/*
|
/*
|
||||||
* Keeping dirty some buffer we cannot write? Make
|
* Keeping dirty some buffer we cannot write? Make sure
|
||||||
* sure to redirty the page. This happens e.g. when
|
* to redirty the page and keep TOWRITE tag so that
|
||||||
* doing writeout for transaction commit.
|
* 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))
|
if (buffer_dirty(bh)) {
|
||||||
redirty_page_for_writepage(wbc, page);
|
if (!PageDirty(page))
|
||||||
|
redirty_page_for_writepage(wbc, page);
|
||||||
|
keep_towrite = true;
|
||||||
|
}
|
||||||
if (io->io_bio)
|
if (io->io_bio)
|
||||||
ext4_io_submit(io);
|
ext4_io_submit(io);
|
||||||
continue;
|
continue;
|
||||||
|
@ -500,6 +501,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
nr_to_submit++;
|
nr_to_submit++;
|
||||||
} while ((bh = bh->b_this_page) != head);
|
} 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);
|
bh = head = page_buffers(page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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 */
|
/* Now submit buffers to write */
|
||||||
do {
|
do {
|
||||||
if (!buffer_async_write(bh))
|
if (!buffer_async_write(bh))
|
||||||
|
@ -558,11 +568,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
||||||
bounce_page ? bounce_page : page, bh);
|
bounce_page ? bounce_page : page, bh);
|
||||||
nr_submitted++;
|
nr_submitted++;
|
||||||
} while ((bh = bh->b_this_page) != head);
|
} while ((bh = bh->b_this_page) != head);
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
/* Nothing submitted - we have to end page writeback */
|
|
||||||
if (!nr_submitted)
|
|
||||||
end_page_writeback(page);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue