Skip to content

Commit 87826df

Browse files
jeffmahoneykdave
authored andcommitted
btrfs: delalloc for page dirtied out-of-band in fixup worker
We encountered an issue that was easily observable on s/390 systems but could really happen anywhere. The timing just seemed to hit reliably on s/390 with limited memory. The gist is that when an unexpected set_page_dirty() happened, we'd run into the BUG() in btrfs_writepage_fixup_worker since it wasn't properly set up for delalloc. This patch does the following: - Performs the missing delalloc in the fixup worker - Allow the start hook to return -EBUSY which informs __extent_writepage that it should mark the page skipped and not to redirty it. This is required since the fixup worker can fail with -ENOSPC and the page will have already been redirtied. That causes an Oops in drop_outstanding_extents later. Retrying the fixup worker could lead to an infinite loop. Deferring the page redirty also saves us some cycles since the page would be stuck in a resubmit-redirty loop until the fixup worker completes. It's not harmful, just wasteful. - If the fixup worker fails, we mark the page and mapping as errored, and end the writeback, similar to what we would do had the page actually been submitted to writeback. Signed-off-by: Jeff Mahoney <[email protected]>
1 parent a7e221e commit 87826df

File tree

3 files changed

+53
-27
lines changed

3 files changed

+53
-27
lines changed

fs/btrfs/extent_io.c

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2161,6 +2161,38 @@ static int bio_readpage_error(struct bio *failed_bio, struct page *page,
21612161

21622162
/* lots and lots of room for performance fixes in the end_bio funcs */
21632163

2164+
int end_extent_writepage(struct page *page, int err, u64 start, u64 end)
2165+
{
2166+
int uptodate = (err == 0);
2167+
struct extent_io_tree *tree;
2168+
int ret;
2169+
2170+
tree = &BTRFS_I(page->mapping->host)->io_tree;
2171+
2172+
if (tree->ops && tree->ops->writepage_end_io_hook) {
2173+
ret = tree->ops->writepage_end_io_hook(page, start,
2174+
end, NULL, uptodate);
2175+
if (ret)
2176+
uptodate = 0;
2177+
}
2178+
2179+
if (!uptodate && tree->ops &&
2180+
tree->ops->writepage_io_failed_hook) {
2181+
ret = tree->ops->writepage_io_failed_hook(NULL, page,
2182+
start, end, NULL);
2183+
/* Writeback already completed */
2184+
if (ret == 0)
2185+
return 1;
2186+
}
2187+
2188+
if (!uptodate) {
2189+
clear_extent_uptodate(tree, start, end, NULL, GFP_NOFS);
2190+
ClearPageUptodate(page);
2191+
SetPageError(page);
2192+
}
2193+
return 0;
2194+
}
2195+
21642196
/*
21652197
* after a writepage IO is done, we need to:
21662198
* clear the uptodate bits on error
@@ -2172,13 +2204,11 @@ static int bio_readpage_error(struct bio *failed_bio, struct page *page,
21722204
*/
21732205
static void end_bio_extent_writepage(struct bio *bio, int err)
21742206
{
2175-
int uptodate = err == 0;
21762207
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
21772208
struct extent_io_tree *tree;
21782209
u64 start;
21792210
u64 end;
21802211
int whole_page;
2181-
int ret;
21822212

21832213
do {
21842214
struct page *page = bvec->bv_page;
@@ -2195,28 +2225,9 @@ static void end_bio_extent_writepage(struct bio *bio, int err)
21952225

21962226
if (--bvec >= bio->bi_io_vec)
21972227
prefetchw(&bvec->bv_page->flags);
2198-
if (tree->ops && tree->ops->writepage_end_io_hook) {
2199-
ret = tree->ops->writepage_end_io_hook(page, start,
2200-
end, NULL, uptodate);
2201-
if (ret)
2202-
uptodate = 0;
2203-
}
2204-
2205-
if (!uptodate && tree->ops &&
2206-
tree->ops->writepage_io_failed_hook) {
2207-
ret = tree->ops->writepage_io_failed_hook(bio, page,
2208-
start, end, NULL);
2209-
if (ret == 0) {
2210-
uptodate = (err == 0);
2211-
continue;
2212-
}
2213-
}
22142228

2215-
if (!uptodate) {
2216-
clear_extent_uptodate(tree, start, end, NULL, GFP_NOFS);
2217-
ClearPageUptodate(page);
2218-
SetPageError(page);
2219-
}
2229+
if (end_extent_writepage(page, err, start, end))
2230+
continue;
22202231

22212232
if (whole_page)
22222233
end_page_writeback(page);
@@ -2818,8 +2829,12 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
28182829
if (tree->ops && tree->ops->writepage_start_hook) {
28192830
ret = tree->ops->writepage_start_hook(page, start,
28202831
page_end);
2821-
if (ret == -EAGAIN) {
2822-
redirty_page_for_writepage(wbc, page);
2832+
if (ret) {
2833+
/* Fixup worker will requeue */
2834+
if (ret == -EBUSY)
2835+
wbc->pages_skipped++;
2836+
else
2837+
redirty_page_for_writepage(wbc, page);
28232838
update_nr_written(page, wbc, nr_written);
28242839
unlock_page(page);
28252840
ret = 0;

fs/btrfs/extent_io.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,5 @@ struct btrfs_mapping_tree;
319319
int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
320320
u64 length, u64 logical, struct page *page,
321321
int mirror_num);
322+
int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
322323
#endif

fs/btrfs/inode.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
15551555
struct inode *inode;
15561556
u64 page_start;
15571557
u64 page_end;
1558+
int ret;
15581559

15591560
fixup = container_of(work, struct btrfs_writepage_fixup, work);
15601561
page = fixup->page;
@@ -1582,12 +1583,21 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
15821583
page_end, &cached_state, GFP_NOFS);
15831584
unlock_page(page);
15841585
btrfs_start_ordered_extent(inode, ordered, 1);
1586+
btrfs_put_ordered_extent(ordered);
15851587
goto again;
15861588
}
15871589

1588-
BUG();
1590+
ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
1591+
if (ret) {
1592+
mapping_set_error(page->mapping, ret);
1593+
end_extent_writepage(page, ret, page_start, page_end);
1594+
ClearPageChecked(page);
1595+
goto out;
1596+
}
1597+
15891598
btrfs_set_extent_delalloc(inode, page_start, page_end, &cached_state);
15901599
ClearPageChecked(page);
1600+
set_page_dirty(page);
15911601
out:
15921602
unlock_extent_cached(&BTRFS_I(inode)->io_tree, page_start, page_end,
15931603
&cached_state, GFP_NOFS);
@@ -1630,7 +1640,7 @@ static int btrfs_writepage_start_hook(struct page *page, u64 start, u64 end)
16301640
fixup->work.func = btrfs_writepage_fixup_worker;
16311641
fixup->page = page;
16321642
btrfs_queue_worker(&root->fs_info->fixup_workers, &fixup->work);
1633-
return -EAGAIN;
1643+
return -EBUSY;
16341644
}
16351645

16361646
static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,

0 commit comments

Comments
 (0)