Skip to content

Commit 2fac4ed

Browse files
adam900710kdave
authored andcommitted
btrfs: rework the error handling of run_delalloc_nocow()
Currently the error handling of run_delalloc_nocow() modifies @cur_offset to handle different parts of the delalloc range. However the error handling can always be split into 3 parts: 1) The range with ordered extents allocated (OE cleanup) We have to cleanup the ordered extents and unlock the folios. 2) The range that have been cleaned up (skip) For now it's only for the range of fallback_to_cow(). We should not touch it at all. 3) The range that is not yet touched (untouched) We have to unlock the folios and clear any reserved space. This 3 ranges split has the same principle as cow_file_range(), however the NOCOW/COW handling makes the above 3 range split much more complex: a) Failure immediately after a successful OE allocation Thus no @cow_start nor @cow_end set. start cur_offset end | OE cleanup | untouched | b) Failure after hitting a COW range but before calling fallback_to_cow() start cow_start cur_offset end | OE Cleanup | untouched | c) Failure to call fallback_to_cow() start cow_start cow_end end | OE Cleanup | skip | untouched | Instead of modifying @cur_offset, do proper range calculation for OE-cleanup and untouched ranges using above 3 cases with proper range charts. This avoid updating @cur_offset, as it will an extra output for debug purposes later, and explain the behavior easier. Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent a607ea5 commit 2fac4ed

File tree

1 file changed

+61
-59
lines changed

1 file changed

+61
-59
lines changed

fs/btrfs/inode.c

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,12 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
20472047
bool check_prev = true;
20482048
u64 ino = btrfs_ino(inode);
20492049
struct can_nocow_file_extent_args nocow_args = { 0 };
2050+
/* The range that has ordered extent(s). */
2051+
u64 oe_cleanup_start;
2052+
u64 oe_cleanup_len = 0;
2053+
/* The range that is untouched. */
2054+
u64 untouched_start;
2055+
u64 untouched_len = 0;
20502056

20512057
/*
20522058
* Normally on a zoned device we're only doing COW writes, but in case
@@ -2232,76 +2238,72 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
22322238
return 0;
22332239

22342240
error:
2235-
/*
2236-
* There are several error cases:
2237-
*
2238-
* 1) Failed without falling back to COW
2239-
* start cur_offset end
2240-
* |/////////////| |
2241-
*
2242-
* In this case, cow_start should be (u64)-1.
2243-
*
2244-
* For range [start, cur_offset) the folios are already unlocked (except
2245-
* @locked_folio), EXTENT_DELALLOC already removed.
2246-
* Need to clear the dirty flags and finish the ordered extents.
2247-
*
2248-
* 2) Failed with error before calling fallback_to_cow()
2249-
*
2250-
* start cow_start end
2251-
* |/////////////| |
2252-
*
2253-
* In this case, only @cow_start is set, @cur_offset is between
2254-
* [cow_start, end)
2255-
*
2256-
* It's mostly the same as case 1), just replace @cur_offset with
2257-
* @cow_start.
2258-
*
2259-
* 3) Failed with error from fallback_to_cow()
2260-
*
2261-
* start cow_start cow_end end
2262-
* |/////////////|-----------| |
2263-
*
2264-
* In this case, both @cow_start and @cow_end is set.
2265-
*
2266-
* For range [start, cow_start) it's the same as case 1).
2267-
* But for range [cow_start, cow_end), all the cleanup is handled by
2268-
* cow_file_range(), we should not touch anything in that range.
2269-
*
2270-
* So for all above cases, if @cow_start is set, cleanup ordered extents
2271-
* for range [start, @cow_start), other wise cleanup range [start, @cur_offset).
2272-
*/
2273-
if (cow_start != (u64)-1)
2274-
cur_offset = cow_start;
2275-
2276-
if (cur_offset > start) {
2277-
btrfs_cleanup_ordered_extents(inode, start, cur_offset - start);
2278-
cleanup_dirty_folios(inode, locked_folio, start, cur_offset - 1, ret);
2241+
if (cow_start == (u64)-1) {
2242+
/*
2243+
* case a)
2244+
* start cur_offset end
2245+
* | OE cleanup | Untouched |
2246+
*
2247+
* We finished a fallback_to_cow() or nocow_one_range() call, but
2248+
* failed to check the next range.
2249+
*/
2250+
oe_cleanup_start = start;
2251+
oe_cleanup_len = cur_offset - start;
2252+
untouched_start = cur_offset;
2253+
untouched_len = end + 1 - untouched_start;
2254+
} else if (cow_start != (u64)-1 && cow_end == 0) {
2255+
/*
2256+
* case b)
2257+
* start cow_start cur_offset end
2258+
* | OE cleanup | Untouched |
2259+
*
2260+
* We got a range that needs COW, but before we hit the next NOCOW range,
2261+
* thus [cow_start, cur_offset) doesn't yet have any OE.
2262+
*/
2263+
oe_cleanup_start = start;
2264+
oe_cleanup_len = cow_start - start;
2265+
untouched_start = cow_start;
2266+
untouched_len = end + 1 - untouched_start;
2267+
} else {
2268+
/*
2269+
* case c)
2270+
* start cow_start cow_end end
2271+
* | OE cleanup | Skip | Untouched |
2272+
*
2273+
* fallback_to_cow() failed, and fallback_to_cow() will do the
2274+
* cleanup for its range, we shouldn't touch the range
2275+
* [cow_start, cow_end].
2276+
*/
2277+
ASSERT(cow_start != (u64)-1 && cow_end != 0);
2278+
oe_cleanup_start = start;
2279+
oe_cleanup_len = cow_start - start;
2280+
untouched_start = cow_end + 1;
2281+
untouched_len = end + 1 - untouched_start;
22792282
}
22802283

2281-
/*
2282-
* If an error happened while a COW region is outstanding, cur_offset
2283-
* needs to be reset to @cow_end + 1 to skip the COW range, as
2284-
* cow_file_range() will do the proper cleanup at error.
2285-
*/
2286-
if (cow_end)
2287-
cur_offset = cow_end + 1;
2284+
if (oe_cleanup_len) {
2285+
btrfs_cleanup_ordered_extents(inode, oe_cleanup_start, oe_cleanup_len);
2286+
cleanup_dirty_folios(inode, locked_folio, oe_cleanup_start,
2287+
oe_cleanup_start + oe_cleanup_len - 1, ret);
2288+
}
22882289

2289-
/*
2290-
* We need to lock the extent here because we're clearing DELALLOC and
2291-
* we're not locked at this point.
2292-
*/
2293-
if (cur_offset < end) {
2290+
if (untouched_len) {
22942291
struct extent_state *cached = NULL;
2292+
const u64 untouched_end = untouched_start + untouched_len - 1;
22952293

2296-
btrfs_lock_extent(&inode->io_tree, cur_offset, end, &cached);
2297-
extent_clear_unlock_delalloc(inode, cur_offset, end,
2294+
/*
2295+
* We need to lock the extent here because we're clearing DELALLOC and
2296+
* we're not locked at this point.
2297+
*/
2298+
btrfs_lock_extent(&inode->io_tree, untouched_start, untouched_end, &cached);
2299+
extent_clear_unlock_delalloc(inode, untouched_start, untouched_end,
22982300
locked_folio, &cached,
22992301
EXTENT_LOCKED | EXTENT_DELALLOC |
23002302
EXTENT_DEFRAG |
23012303
EXTENT_DO_ACCOUNTING, PAGE_UNLOCK |
23022304
PAGE_START_WRITEBACK |
23032305
PAGE_END_WRITEBACK);
2304-
btrfs_qgroup_free_data(inode, NULL, cur_offset, end - cur_offset + 1, NULL);
2306+
btrfs_qgroup_free_data(inode, NULL, untouched_start, untouched_len, NULL);
23052307
}
23062308
btrfs_free_path(path);
23072309
btrfs_err_rl(fs_info,

0 commit comments

Comments
 (0)