Skip to content

Commit e6f5426

Browse files
adam900710kdave
authored andcommitted
btrfs: make nocow_one_range() to do cleanup on error
Currently if we hit an error inside nocow_one_range(), we do not clear the page dirty, and let the caller to handle it. This is very different compared to fallback_to_cow(), when that function failed, everything will be cleaned up by cow_file_range(). Enhance the situation by: - Use a common error handling for nocow_one_range() If we failed anything, use the same btrfs_cleanup_ordered_extents() and extent_clear_unlock_delalloc(). btrfs_cleanup_ordered_extents() is safe even if we haven't created new ordered extent, in that case there should be no OE and that function will do nothing. The same applies to extent_clear_unlock_delalloc(), and since we're passing PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK, it will also clear folio dirty flag during error handling. - Avoid touching the failed range of nocow_one_range() As the failed range will be cleaned up and unlocked by that function. Here we introduce a new variable @nocow_end to record the failed range, so that we can skip it during the error handling of run_delalloc_nocow(). Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 9a0d619 commit e6f5426

File tree

1 file changed

+38
-21
lines changed

1 file changed

+38
-21
lines changed

fs/btrfs/inode.c

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,8 +1982,8 @@ static int nocow_one_range(struct btrfs_inode *inode, struct folio *locked_folio
19821982
em = btrfs_create_io_em(inode, file_pos, &nocow_args->file_extent,
19831983
BTRFS_ORDERED_PREALLOC);
19841984
if (IS_ERR(em)) {
1985-
btrfs_unlock_extent(&inode->io_tree, file_pos, end, cached);
1986-
return PTR_ERR(em);
1985+
ret = PTR_ERR(em);
1986+
goto error;
19871987
}
19881988
btrfs_free_extent_map(em);
19891989
}
@@ -1995,8 +1995,8 @@ static int nocow_one_range(struct btrfs_inode *inode, struct folio *locked_folio
19951995
if (IS_ERR(ordered)) {
19961996
if (is_prealloc)
19971997
btrfs_drop_extent_map_range(inode, file_pos, end, false);
1998-
btrfs_unlock_extent(&inode->io_tree, file_pos, end, cached);
1999-
return PTR_ERR(ordered);
1998+
ret = PTR_ERR(ordered);
1999+
goto error;
20002000
}
20012001

20022002
if (btrfs_is_data_reloc_root(inode->root))
@@ -2008,23 +2008,24 @@ static int nocow_one_range(struct btrfs_inode *inode, struct folio *locked_folio
20082008
ret = btrfs_reloc_clone_csums(ordered);
20092009
btrfs_put_ordered_extent(ordered);
20102010

2011+
if (ret < 0)
2012+
goto error;
20112013
extent_clear_unlock_delalloc(inode, file_pos, end, locked_folio, cached,
20122014
EXTENT_LOCKED | EXTENT_DELALLOC |
20132015
EXTENT_CLEAR_DATA_RESV,
20142016
PAGE_UNLOCK | PAGE_SET_ORDERED);
2015-
/*
2016-
* On error, we need to cleanup the ordered extents we created.
2017-
*
2018-
* We do not clear the folio Dirty flags because they are set and
2019-
* cleaered by the caller.
2020-
*/
2021-
if (ret < 0) {
2022-
btrfs_cleanup_ordered_extents(inode, file_pos, len);
2023-
btrfs_err(inode->root->fs_info,
2024-
"%s failed, root=%lld inode=%llu start=%llu len=%llu: %d",
2025-
__func__, btrfs_root_id(inode->root), btrfs_ino(inode),
2026-
file_pos, len, ret);
2027-
}
2017+
return ret;
2018+
error:
2019+
btrfs_cleanup_ordered_extents(inode, file_pos, len);
2020+
extent_clear_unlock_delalloc(inode, file_pos, end, locked_folio, cached,
2021+
EXTENT_LOCKED | EXTENT_DELALLOC |
2022+
EXTENT_CLEAR_DATA_RESV,
2023+
PAGE_UNLOCK | PAGE_START_WRITEBACK |
2024+
PAGE_END_WRITEBACK);
2025+
btrfs_err(inode->root->fs_info,
2026+
"%s failed, root=%lld inode=%llu start=%llu len=%llu: %d",
2027+
__func__, btrfs_root_id(inode->root), btrfs_ino(inode),
2028+
file_pos, len, ret);
20282029
return ret;
20292030
}
20302031

@@ -2046,8 +2047,12 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
20462047
/*
20472048
* If not 0, represents the inclusive end of the last fallback_to_cow()
20482049
* range. Only for error handling.
2050+
*
2051+
* The same for nocow_end, it's to avoid double cleaning up the range
2052+
* already cleaned by nocow_one_range().
20492053
*/
20502054
u64 cow_end = 0;
2055+
u64 nocow_end = 0;
20512056
u64 cur_offset = start;
20522057
int ret;
20532058
bool check_prev = true;
@@ -2222,8 +2227,10 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
22222227
&nocow_args, cur_offset,
22232228
extent_type == BTRFS_FILE_EXTENT_PREALLOC);
22242229
btrfs_dec_nocow_writers(nocow_bg);
2225-
if (ret < 0)
2230+
if (ret < 0) {
2231+
nocow_end = cur_offset + nocow_args.file_extent.num_bytes - 1;
22262232
goto error;
2233+
}
22272234
cur_offset = extent_end;
22282235
}
22292236
btrfs_release_path(path);
@@ -2250,12 +2257,22 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
22502257
* start cur_offset end
22512258
* | OE cleanup | Untouched |
22522259
*
2253-
* We finished a fallback_to_cow() or nocow_one_range() call, but
2254-
* failed to check the next range.
2260+
* We finished a fallback_to_cow() or nocow_one_range() call,
2261+
* but failed to check the next range.
2262+
*
2263+
* or
2264+
* start cur_offset nocow_end end
2265+
* | OE cleanup | Skip | Untouched |
2266+
*
2267+
* nocow_one_range() failed, the range [cur_offset, nocow_end] is
2268+
* alread cleaned up.
22552269
*/
22562270
oe_cleanup_start = start;
22572271
oe_cleanup_len = cur_offset - start;
2258-
untouched_start = cur_offset;
2272+
if (nocow_end)
2273+
untouched_start = nocow_end + 1;
2274+
else
2275+
untouched_start = cur_offset;
22592276
untouched_len = end + 1 - untouched_start;
22602277
} else if (cow_start != (u64)-1 && cow_end == 0) {
22612278
/*

0 commit comments

Comments
 (0)