diff options
author | Christoph Hellwig <hch@lst.de> | 2023-06-28 17:31:41 +0200 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2023-08-21 14:52:16 +0200 |
commit | 6e144bf16ba07dff649e6bd6afd79d2e353f6216 (patch) | |
tree | f1d464dfb857fa57501d00e597f79d26ae8b7cb7 /fs/btrfs/inode.c | |
parent | 44962ca37c8cf882ba72e4cd67f30eaecb920a52 (diff) |
btrfs: refactor the zoned device handling in cow_file_range
Handling of the done_offset to cow_file_range is a bit confusing, as
it is not updated at all when the function succeeds, and the -EAGAIN
status is used bother for the case where we need to wait for a zone
finish and the one where the allocation was partially successful.
Change the calling convention so that done_offset is always updated,
and 0 is returned if some allocation was successful (partial allocation
can still only happen for zoned devices), and waiting for a zone
finish is done internally in cow_file_range instead of the caller.
Also write a comment explaining the logic.
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 58 |
1 files changed, 31 insertions, 27 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7447f3b44cd2..0617e47fb9e3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1363,7 +1363,8 @@ static noinline int cow_file_range(struct btrfs_inode *inode, * compressed extent. */ unlock_page(locked_page); - return 1; + ret = 1; + goto done; } else if (ret < 0) { goto out_unlock; } @@ -1394,6 +1395,31 @@ static noinline int cow_file_range(struct btrfs_inode *inode, ret = btrfs_reserve_extent(root, cur_alloc_size, cur_alloc_size, min_alloc_size, 0, alloc_hint, &ins, 1, 1); + if (ret == -EAGAIN) { + /* + * btrfs_reserve_extent only returns -EAGAIN for zoned + * file systems, which is an indication that there are + * no active zones to allocate from at the moment. + * + * If this is the first loop iteration, wait for at + * least one zone to finish before retrying the + * allocation. Otherwise ask the caller to write out + * the already allocated blocks before coming back to + * us, or return -ENOSPC if it can't handle retries. + */ + ASSERT(btrfs_is_zoned(fs_info)); + if (start == orig_start) { + wait_on_bit_io(&inode->root->fs_info->flags, + BTRFS_FS_NEED_ZONE_FINISH, + TASK_UNINTERRUPTIBLE); + continue; + } + if (done_offset) { + *done_offset = start - 1; + return 0; + } + ret = -ENOSPC; + } if (ret < 0) goto out_unlock; cur_alloc_size = ins.offset; @@ -1477,6 +1503,9 @@ static noinline int cow_file_range(struct btrfs_inode *inode, if (ret) goto out_unlock; } +done: + if (done_offset) + *done_offset = end; return ret; out_drop_extent_cache: @@ -1486,21 +1515,6 @@ out_reserve: btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 1); out_unlock: /* - * If done_offset is non-NULL and ret == -EAGAIN, we expect the - * caller to write out the successfully allocated region and retry. - */ - if (done_offset && ret == -EAGAIN) { - if (orig_start < start) - *done_offset = start - 1; - else - *done_offset = start; - return ret; - } else if (ret == -EAGAIN) { - /* Convert to -ENOSPC since the caller cannot retry. */ - ret = -ENOSPC; - } - - /* * Now, we have three regions to clean up: * * |-------(1)----|---(2)---|-------------(3)----------| @@ -1710,19 +1724,9 @@ static noinline int run_delalloc_zoned(struct btrfs_inode *inode, while (start <= end) { ret = cow_file_range(inode, locked_page, start, end, &done_offset, true, false); - if (ret && ret != -EAGAIN) + if (ret) return ret; - if (ret == 0) - done_offset = end; - - if (done_offset == start) { - wait_on_bit_io(&inode->root->fs_info->flags, - BTRFS_FS_NEED_ZONE_FINISH, - TASK_UNINTERRUPTIBLE); - continue; - } - if (!locked_page_done) { __set_page_dirty_nobuffers(locked_page); account_page_redirty(locked_page); |