diff options
| author | Johannes Thumshirn <johannes.thumshirn@wdc.com> | 2026-06-23 13:38:51 +0200 |
|---|---|---|
| committer | David Sterba <dsterba@suse.com> | 2026-08-07 19:16:28 +0200 |
| commit | e549093c11a2fff8430df3dfbdb45eb9811a69a5 (patch) | |
| tree | 55fd466fa158ca5fceb3c68749506f8a98f88dfe | |
| parent | 097cdc84620e7dbc8653bddb4a3009e08980ea1f (diff) | |
btrfs: zoned: don't force read-only on transient -EAGAIN from reloc merge
On a zoned FS, btrfs_delayed_refs_rsv_refill() returns -EAGAIN whenever
the over-committed metadata plus the zone_unusable bytes exceeds the
usable size in a metadata block-group to avoid heavy over-commit of
metadata and early ENOSPC in one transaction.
If this happens while doing reclaim, the transaction is getting aborted.
Treat -EAGAIN as a soft, retryable condition in case of block-group
reclaim.
Reported-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: 7bcb04de982f ("btrfs: zoned: cap delayed refs metadata reservation to avoid overcommit")
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
| -rw-r--r-- | fs/btrfs/block-group.c | 8 | ||||
| -rw-r--r-- | fs/btrfs/relocation.c | 49 |
2 files changed, 51 insertions, 6 deletions
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 8def7abb728f..c5bcd3c03d24 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2047,6 +2047,11 @@ static int btrfs_reclaim_block_group(struct btrfs_block_group *bg, int *reclaime trace_btrfs_reclaim_block_group(bg); ret = btrfs_relocate_chunk(fs_info, bg->start, false); + if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { + btrfs_dec_block_group_ro(bg); + btrfs_debug(fs_info, "deferring reclaim of chunk %llu", bg->start); + return ret; + } if (ret) { btrfs_dec_block_group_ro(bg); btrfs_err(fs_info, "error relocating chunk %llu", @@ -2113,7 +2118,8 @@ void btrfs_reclaim_block_groups(struct btrfs_fs_info *fs_info, unsigned int limi spin_unlock(&fs_info->unused_bgs_lock); ret = btrfs_reclaim_block_group(bg, &reclaimed); - if (ret && !READ_ONCE(space_info->periodic_reclaim)) + if ((btrfs_is_zoned(fs_info) && ret == -EAGAIN) || + (ret && !READ_ONCE(space_info->periodic_reclaim))) btrfs_link_bg_list(bg, &retry_list); btrfs_put_block_group(bg); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 6a1817613036..f14bb4158d8d 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1538,6 +1538,33 @@ static void clear_reloc_root(struct btrfs_root *root) clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); } +/* Drop the reloc trees of a relocation that is being deferred and retried. */ +static void abort_reloc_roots(struct reloc_control *rc, struct list_head *list) +{ + struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; + struct btrfs_root *reloc_root, *tmp; + + list_for_each_entry_safe(reloc_root, tmp, list, root_list) { + struct btrfs_root *root; + + root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset, false); + if (!IS_ERR(root)) { + if (root->reloc_root == reloc_root) { + clear_reloc_root(root); + btrfs_put_root(reloc_root); + } + btrfs_put_root(root); + } + + btrfs_set_root_refs(&reloc_root->root_item, 0); + memset(&reloc_root->root_item.drop_progress, 0, sizeof(struct btrfs_disk_key)); + btrfs_set_root_drop_level(&reloc_root->root_item, 0); + + list_del_init(&reloc_root->root_list); + list_add_tail(&reloc_root->reloc_dirty_list, &rc->dirty_subvol_roots); + } +} + static int clean_dirty_subvols(struct reloc_control *rc) { struct btrfs_root *root; @@ -1877,8 +1904,7 @@ again: return err; } -static noinline_for_stack -void merge_reloc_roots(struct reloc_control *rc) +static noinline_for_stack int merge_reloc_roots(struct reloc_control *rc) { struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; struct btrfs_root *root; @@ -1976,7 +2002,15 @@ again: goto again; } out: - if (ret) { + if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { + abort_reloc_roots(rc, &reloc_roots); + + /* New reloc root may be added. */ + mutex_lock(&fs_info->reloc_mutex); + list_splice_init(&rc->reloc_roots, &reloc_roots); + mutex_unlock(&fs_info->reloc_mutex); + abort_reloc_roots(rc, &reloc_roots); + } else if (ret) { btrfs_handle_fs_error(fs_info, ret, NULL); free_reloc_roots(&reloc_roots); @@ -2002,6 +2036,7 @@ out: * * The remaining nodes will be cleaned up by put_reloc_control(). */ + return ret; } static void free_block_list(struct rb_root *blocks) @@ -3731,7 +3766,9 @@ restart: */ err = prepare_to_merge(rc, err); - merge_reloc_roots(rc); + ret = merge_reloc_roots(rc); + if (ret && !err) + err = ret; rc->merge_reloc_tree = false; unset_reloc_control(rc); @@ -5700,7 +5737,9 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) if (ret) goto out_unset; - merge_reloc_roots(rc); + ret = merge_reloc_roots(rc); + if (ret) + goto out_unset; unset_reloc_control(rc); |
