Commit 11c8349b authored by Chris Mason's avatar Chris Mason

Btrfs: fix oops on page->mapping->host during writepage

The extent_io writepage call updates the writepage index in the inode
as it makes progress.  But, it was doing the update after unlocking the page,
which isn't legal because page->mapping can't be trusted once the page
is unlocked.

This lead to an oops, especially common with compression turned on.  The
fix here is to update the writeback index before unlocking the page.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent d313d7a3
...@@ -2104,6 +2104,16 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page, ...@@ -2104,6 +2104,16 @@ int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
return ret; return ret;
} }
static noinline void update_nr_written(struct page *page,
struct writeback_control *wbc,
unsigned long nr_written)
{
wbc->nr_to_write -= nr_written;
if (wbc->range_cyclic || (wbc->nr_to_write > 0 &&
wbc->range_start == 0 && wbc->range_end == LLONG_MAX))
page->mapping->writeback_index = page->index + nr_written;
}
/* /*
* the writepage semantics are similar to regular writepage. extent * the writepage semantics are similar to regular writepage. extent
* records are inserted to lock ranges in the tree, and as dirty areas * records are inserted to lock ranges in the tree, and as dirty areas
...@@ -2173,6 +2183,12 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2173,6 +2183,12 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
delalloc_end = 0; delalloc_end = 0;
page_started = 0; page_started = 0;
if (!epd->extent_locked) { if (!epd->extent_locked) {
/*
* make sure the wbc mapping index is at least updated
* to this page.
*/
update_nr_written(page, wbc, 0);
while (delalloc_end < page_end) { while (delalloc_end < page_end) {
nr_delalloc = find_lock_delalloc_range(inode, tree, nr_delalloc = find_lock_delalloc_range(inode, tree,
page, page,
...@@ -2194,7 +2210,13 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2194,7 +2210,13 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
*/ */
if (page_started) { if (page_started) {
ret = 0; ret = 0;
goto update_nr_written; /*
* we've unlocked the page, so we can't update
* the mapping's writeback index, just update
* nr_to_write.
*/
wbc->nr_to_write -= nr_written;
goto done_unlocked;
} }
} }
lock_extent(tree, start, page_end, GFP_NOFS); lock_extent(tree, start, page_end, GFP_NOFS);
...@@ -2207,13 +2229,18 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2207,13 +2229,18 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
unlock_extent(tree, start, page_end, GFP_NOFS); unlock_extent(tree, start, page_end, GFP_NOFS);
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
update_nr_written(page, wbc, nr_written);
unlock_page(page); unlock_page(page);
ret = 0; ret = 0;
goto update_nr_written; goto done_unlocked;
} }
} }
nr_written++; /*
* we don't want to touch the inode after unlocking the page,
* so we update the mapping writeback index now
*/
update_nr_written(page, wbc, nr_written + 1);
end = page_end; end = page_end;
if (test_range_bit(tree, start, page_end, EXTENT_DELALLOC, 0)) if (test_range_bit(tree, start, page_end, EXTENT_DELALLOC, 0))
...@@ -2345,11 +2372,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2345,11 +2372,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
unlock_extent(tree, unlock_start, page_end, GFP_NOFS); unlock_extent(tree, unlock_start, page_end, GFP_NOFS);
unlock_page(page); unlock_page(page);
update_nr_written: done_unlocked:
wbc->nr_to_write -= nr_written;
if (wbc->range_cyclic || (wbc->nr_to_write > 0 &&
wbc->range_start == 0 && wbc->range_end == LLONG_MAX))
page->mapping->writeback_index = page->index + nr_written;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment