btrfs: zoned: reserve data_reloc block group on mount
authorJohannes Thumshirn <johannes.thumshirn@wdc.com>
Tue, 3 Jun 2025 06:14:01 +0000 (08:14 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 21 Jul 2025 21:56:31 +0000 (23:56 +0200)
Create a block group dedicated for data relocation on mount of a zoned
filesystem.

If there is already more than one empty DATA block group on mount, this
one is picked for the data relocation block group, instead of a newly
created one.

This is done to ensure, there is always space for performing garbage
collection and the filesystem is not hitting ENOSPC under heavy overwrite
workloads.

CC: stable@vger.kernel.org # 6.6+
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/disk-io.c
fs/btrfs/zoned.c
fs/btrfs/zoned.h

index 0d6ad7512f217c0b0766ce49b413b7d72f7bdb32..4cfcd879dc5eb72a534d5c36028263d3fb00b2df 100644 (file)
@@ -3561,6 +3561,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
                goto fail_sysfs;
        }
 
+       btrfs_zoned_reserve_data_reloc_bg(fs_info);
        btrfs_free_zone_cache(fs_info);
 
        btrfs_check_active_zone_reservation(fs_info);
index 805f2eca20e935640242cf0101025edb36015de4..4ab7808bca6219d59d0c9f8ce585301dfde917eb 100644 (file)
@@ -17,6 +17,7 @@
 #include "fs.h"
 #include "accessors.h"
 #include "bio.h"
+#include "transaction.h"
 
 /* Maximum number of zones to report per blkdev_report_zones() call */
 #define BTRFS_REPORT_NR_ZONES   4096
@@ -2501,6 +2502,66 @@ void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg)
        spin_unlock(&fs_info->relocation_bg_lock);
 }
 
+void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_space_info *data_sinfo = fs_info->data_sinfo;
+       struct btrfs_space_info *space_info = data_sinfo->sub_group[0];
+       struct btrfs_trans_handle *trans;
+       struct btrfs_block_group *bg;
+       struct list_head *bg_list;
+       u64 alloc_flags;
+       bool initial = false;
+       bool did_chunk_alloc = false;
+       int index;
+       int ret;
+
+       if (!btrfs_is_zoned(fs_info))
+               return;
+
+       if (fs_info->data_reloc_bg)
+               return;
+
+       if (sb_rdonly(fs_info->sb))
+               return;
+
+       ASSERT(space_info->subgroup_id == BTRFS_SUB_GROUP_DATA_RELOC);
+       alloc_flags = btrfs_get_alloc_profile(fs_info, space_info->flags);
+       index = btrfs_bg_flags_to_raid_index(alloc_flags);
+
+       bg_list = &data_sinfo->block_groups[index];
+again:
+       list_for_each_entry(bg, bg_list, list) {
+               if (bg->used > 0)
+                       continue;
+
+               if (!initial) {
+                       initial = true;
+                       continue;
+               }
+
+               fs_info->data_reloc_bg = bg->start;
+               set_bit(BLOCK_GROUP_FLAG_ZONED_DATA_RELOC, &bg->runtime_flags);
+               btrfs_zone_activate(bg);
+
+               return;
+       }
+
+       if (did_chunk_alloc)
+               return;
+
+       trans = btrfs_join_transaction(fs_info->tree_root);
+       if (IS_ERR(trans))
+               return;
+
+       ret = btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE);
+       btrfs_end_transaction(trans);
+       if (ret == 1) {
+               did_chunk_alloc = true;
+               bg_list = &space_info->block_groups[index];
+               goto again;
+       }
+}
+
 void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
index 9672bf4c333552deee3028c5b41df92a77160d4b..6e11533b8e14c27e5b8bebbf24a9b632b36c23ba 100644 (file)
@@ -88,6 +88,7 @@ void btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical,
 void btrfs_schedule_zone_finish_bg(struct btrfs_block_group *bg,
                                   struct extent_buffer *eb);
 void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg);
+void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info);
 void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info);
 bool btrfs_zoned_should_reclaim(const struct btrfs_fs_info *fs_info);
 void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical,
@@ -241,6 +242,8 @@ static inline void btrfs_schedule_zone_finish_bg(struct btrfs_block_group *bg,
 
 static inline void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg) { }
 
+static inline void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info) { }
+
 static inline void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info) { }
 
 static inline bool btrfs_zoned_should_reclaim(const struct btrfs_fs_info *fs_info)