From: Boris Burkov <boris@bur.io>
To: Mark Harmstone <mark@harmstone.com>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH v4 09/16] btrfs: handle deletions from remapped block group
Date: Fri, 31 Oct 2025 16:05:25 -0700 [thread overview]
Message-ID: <aQVAtcfRy2rctnaA@devvm12410.ftw0.facebook.com> (raw)
In-Reply-To: <20251024181227.32228-10-mark@harmstone.com>
On Fri, Oct 24, 2025 at 07:12:10PM +0100, Mark Harmstone wrote:
> Handle the case where we free an extent from a block group that has the
> REMAPPED flag set. Because the remap tree is orthogonal to the extent
> tree, for data this may be within any number of identity remaps or
> actual remaps. If we're freeing a metadata node, this will be wholly
> inside one or the other.
>
> btrfs_remove_extent_from_remap_tree() searches the remap tree for the
> remaps that cover the range in question, then calls
> remove_range_from_remap_tree() for each one, to punch a hole in the
> remap and adjust the free-space tree.
>
> For an identity remap, remove_range_from_remap_tree() will adjust the
> block group's `identity_remap_count` if this changes. If it reaches
> zero we call last_identity_remap_gone(), which removes the chunk's
> stripes and device extents - it is now fully remapped.
>
> The changes which involve the block group's ro flag are because the
> REMAPPED flag itself prevents a block group from having any new
> allocations within it, and so we don't need to account for this
> separately.
>
> Signed-off-by: Mark Harmstone <mark@harmstone.com>
> ---
> fs/btrfs/block-group.c | 118 +++++++---
> fs/btrfs/block-group.h | 4 +
> fs/btrfs/disk-io.c | 2 +
> fs/btrfs/extent-tree.c | 77 ++++++-
> fs/btrfs/extent-tree.h | 1 +
> fs/btrfs/fs.h | 4 +-
> fs/btrfs/relocation.c | 509 +++++++++++++++++++++++++++++++++++++++++
> fs/btrfs/relocation.h | 6 +
> fs/btrfs/transaction.c | 4 +
> fs/btrfs/volumes.c | 56 +++--
> fs/btrfs/volumes.h | 6 +
> 11 files changed, 728 insertions(+), 59 deletions(-)
>
> diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
> index 27173aca6fc1..3bf5f20d90ec 100644
> --- a/fs/btrfs/block-group.c
> +++ b/fs/btrfs/block-group.c
> @@ -1068,6 +1068,32 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans,
> return ret;
> }
>
> +void btrfs_remove_bg_from_sinfo(struct btrfs_block_group *block_group)
> +{
> + int factor = btrfs_bg_type_to_factor(block_group->flags);
> +
> + spin_lock(&block_group->space_info->lock);
> +
> + if (btrfs_test_opt(block_group->fs_info, ENOSPC_DEBUG)) {
> + WARN_ON(block_group->space_info->total_bytes
> + < block_group->length);
> + WARN_ON(block_group->space_info->bytes_readonly
> + < block_group->length - block_group->zone_unusable);
> + WARN_ON(block_group->space_info->bytes_zone_unusable
> + < block_group->zone_unusable);
> + WARN_ON(block_group->space_info->disk_total
> + < block_group->length * factor);
> + }
> + block_group->space_info->total_bytes -= block_group->length;
> + block_group->space_info->bytes_readonly -=
> + (block_group->length - block_group->zone_unusable);
> + btrfs_space_info_update_bytes_zone_unusable(block_group->space_info,
> + -block_group->zone_unusable);
> + block_group->space_info->disk_total -= block_group->length * factor;
> +
> + spin_unlock(&block_group->space_info->lock);
> +}
> +
> int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
> struct btrfs_chunk_map *map)
> {
> @@ -1079,7 +1105,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
> struct kobject *kobj = NULL;
> int ret;
> int index;
> - int factor;
> struct btrfs_caching_control *caching_ctl = NULL;
> bool remove_map;
> bool remove_rsv = false;
> @@ -1088,7 +1113,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
> if (!block_group)
> return -ENOENT;
>
> - BUG_ON(!block_group->ro);
> + BUG_ON(!block_group->ro && !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED));
>
> trace_btrfs_remove_block_group(block_group);
> /*
> @@ -1100,7 +1125,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
> block_group->length);
>
> index = btrfs_bg_flags_to_raid_index(block_group->flags);
> - factor = btrfs_bg_type_to_factor(block_group->flags);
>
> /* make sure this block group isn't part of an allocation cluster */
> cluster = &fs_info->data_alloc_cluster;
> @@ -1224,26 +1248,11 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
>
> spin_lock(&block_group->space_info->lock);
> list_del_init(&block_group->ro_list);
> -
> - if (btrfs_test_opt(fs_info, ENOSPC_DEBUG)) {
> - WARN_ON(block_group->space_info->total_bytes
> - < block_group->length);
> - WARN_ON(block_group->space_info->bytes_readonly
> - < block_group->length - block_group->zone_unusable);
> - WARN_ON(block_group->space_info->bytes_zone_unusable
> - < block_group->zone_unusable);
> - WARN_ON(block_group->space_info->disk_total
> - < block_group->length * factor);
> - }
> - block_group->space_info->total_bytes -= block_group->length;
> - block_group->space_info->bytes_readonly -=
> - (block_group->length - block_group->zone_unusable);
> - btrfs_space_info_update_bytes_zone_unusable(block_group->space_info,
> - -block_group->zone_unusable);
> - block_group->space_info->disk_total -= block_group->length * factor;
> -
> spin_unlock(&block_group->space_info->lock);
>
> + if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED))
> + btrfs_remove_bg_from_sinfo(block_group);
> +
> /*
> * Remove the free space for the block group from the free space tree
> * and the block group's item from the extent tree before marking the
> @@ -1538,6 +1547,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> while (!list_empty(&fs_info->unused_bgs)) {
> u64 used;
> int trimming;
> + bool made_ro = false;
>
> block_group = list_first_entry(&fs_info->unused_bgs,
> struct btrfs_block_group,
> @@ -1574,7 +1584,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>
> spin_lock(&space_info->lock);
> spin_lock(&block_group->lock);
> - if (btrfs_is_block_group_used(block_group) || block_group->ro ||
> + if (btrfs_is_block_group_used(block_group) ||
> + (block_group->ro && !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) ||
> list_is_singular(&block_group->list)) {
> /*
> * We want to bail if we made new allocations or have
> @@ -1616,9 +1627,10 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> * needing to allocate extents from the block group.
> */
> used = btrfs_space_info_used(space_info, true);
> - if ((space_info->total_bytes - block_group->length < used &&
> + if (((space_info->total_bytes - block_group->length < used &&
> block_group->zone_unusable < block_group->length) ||
> - has_unwritten_metadata(block_group)) {
> + has_unwritten_metadata(block_group)) &&
> + !(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) {
> /*
> * Add a reference for the list, compensate for the ref
> * drop under the "next" label for the
> @@ -1636,8 +1648,14 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> spin_unlock(&block_group->lock);
> spin_unlock(&space_info->lock);
>
> - /* We don't want to force the issue, only flip if it's ok. */
> - ret = inc_block_group_ro(block_group, 0);
If we are deleting an unused bg, what is the harm in marking it ro even
if it is remapped and it's redundant for new allocations?
> + if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED)) {
> + /* We don't want to force the issue, only flip if it's ok. */
> + ret = inc_block_group_ro(block_group, 0);
> + made_ro = true;
> + } else {
> + ret = 0;
> + }
> +
> up_write(&space_info->groups_sem);
> if (ret < 0) {
> ret = 0;
> @@ -1646,7 +1664,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
>
> ret = btrfs_zone_finish(block_group);
> if (ret < 0) {
> - btrfs_dec_block_group_ro(block_group);
> + if (made_ro)
> + btrfs_dec_block_group_ro(block_group);
> if (ret == -EAGAIN) {
> btrfs_link_bg_list(block_group, &retry_list);
> ret = 0;
> @@ -1661,7 +1680,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> trans = btrfs_start_trans_remove_block_group(fs_info,
> block_group->start);
> if (IS_ERR(trans)) {
> - btrfs_dec_block_group_ro(block_group);
> + if (made_ro)
> + btrfs_dec_block_group_ro(block_group);
> ret = PTR_ERR(trans);
> goto next;
> }
> @@ -1671,7 +1691,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> * just delete them, we don't care about them anymore.
> */
> if (!clean_pinned_extents(trans, block_group)) {
> - btrfs_dec_block_group_ro(block_group);
> + if (made_ro)
> + btrfs_dec_block_group_ro(block_group);
> goto end_trans;
> }
>
> @@ -1685,7 +1706,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
> spin_lock(&fs_info->discard_ctl.lock);
> if (!list_empty(&block_group->discard_list)) {
> spin_unlock(&fs_info->discard_ctl.lock);
> - btrfs_dec_block_group_ro(block_group);
> + if (made_ro)
> + btrfs_dec_block_group_ro(block_group);
> btrfs_discard_queue_work(&fs_info->discard_ctl,
> block_group);
> goto end_trans;
> @@ -1779,6 +1801,15 @@ void btrfs_mark_bg_unused(struct btrfs_block_group *bg)
> struct btrfs_fs_info *fs_info = bg->fs_info;
>
> spin_lock(&fs_info->unused_bgs_lock);
> +
> + /* Leave fully remapped block groups on the fully_remapped_bgs list. */
> + if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED &&
> + bg->identity_remap_count == 0 &&
> + !list_empty(&bg->bg_list)) {
> + spin_unlock(&fs_info->unused_bgs_lock);
> + return;
> + }
> +
> if (list_empty(&bg->bg_list)) {
> btrfs_get_block_group(bg);
> trace_btrfs_add_unused_block_group(bg);
> @@ -4772,3 +4803,30 @@ bool btrfs_block_group_should_use_size_class(const struct btrfs_block_group *bg)
> return false;
> return true;
> }
> +
> +void btrfs_mark_bg_fully_remapped(struct btrfs_block_group *bg,
> + struct btrfs_trans_handle *trans)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + bool already_done;
> +
> + spin_lock(&bg->lock);
> + already_done = bg->fully_remapped;
> + bg->fully_remapped = true;
> + spin_unlock(&bg->lock);
> +
> + if (already_done)
> + return;
> +
> + spin_lock(&fs_info->unused_bgs_lock);
> +
> + if (!list_empty(&bg->bg_list))
> + list_del(&bg->bg_list);
> + else
> + btrfs_get_block_group(bg);
> +
> + list_add_tail(&bg->bg_list, &fs_info->fully_remapped_bgs);
> +
> + spin_unlock(&fs_info->unused_bgs_lock);
> +
> +}
> diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h
> index af23fdb3cf4d..d85f3c2546d0 100644
> --- a/fs/btrfs/block-group.h
> +++ b/fs/btrfs/block-group.h
> @@ -282,6 +282,7 @@ struct btrfs_block_group {
> struct extent_buffer *last_eb;
> enum btrfs_block_group_size_class size_class;
> u64 reclaim_mark;
> + bool fully_remapped;
> };
>
> static inline u64 btrfs_block_group_end(const struct btrfs_block_group *block_group)
> @@ -336,6 +337,7 @@ int btrfs_add_new_free_space(struct btrfs_block_group *block_group,
> struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
> struct btrfs_fs_info *fs_info,
> const u64 chunk_offset);
> +void btrfs_remove_bg_from_sinfo(struct btrfs_block_group *block_group);
> int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
> struct btrfs_chunk_map *map);
> void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info);
> @@ -407,5 +409,7 @@ int btrfs_use_block_group_size_class(struct btrfs_block_group *bg,
> enum btrfs_block_group_size_class size_class,
> bool force_wrong_size_class);
> bool btrfs_block_group_should_use_size_class(const struct btrfs_block_group *bg);
> +void btrfs_mark_bg_fully_remapped(struct btrfs_block_group *bg,
> + struct btrfs_trans_handle *trans);
>
> #endif /* BTRFS_BLOCK_GROUP_H */
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index d3ff148311d8..1a3e525f3d1a 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -2870,6 +2870,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
> INIT_LIST_HEAD(&fs_info->tree_mod_seq_list);
> INIT_LIST_HEAD(&fs_info->unused_bgs);
> INIT_LIST_HEAD(&fs_info->reclaim_bgs);
> + INIT_LIST_HEAD(&fs_info->fully_remapped_bgs);
> INIT_LIST_HEAD(&fs_info->zone_active_bgs);
> #ifdef CONFIG_BTRFS_DEBUG
> INIT_LIST_HEAD(&fs_info->allocated_roots);
> @@ -2925,6 +2926,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
> mutex_init(&fs_info->chunk_mutex);
> mutex_init(&fs_info->transaction_kthread_mutex);
> mutex_init(&fs_info->cleaner_mutex);
> + mutex_init(&fs_info->remap_mutex);
> mutex_init(&fs_info->ro_block_group_mutex);
> init_rwsem(&fs_info->commit_root_sem);
> init_rwsem(&fs_info->cleanup_work_sem);
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index d3ca8105ffc7..1c14e0c82c03 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -40,6 +40,7 @@
> #include "orphan.h"
> #include "tree-checker.h"
> #include "raid-stripe-tree.h"
> +#include "relocation.h"
>
> #undef SCRAMBLE_DELAYED_REFS
>
> @@ -2847,6 +2848,52 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info,
> return ret;
> }
>
> +int btrfs_handle_fully_remapped_bgs(struct btrfs_trans_handle *trans)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct btrfs_block_group *block_group, *tmp;
> + struct list_head *fully_remapped_bgs;
> + int ret;
> +
> + fully_remapped_bgs = &fs_info->fully_remapped_bgs;
> + list_for_each_entry_safe(block_group, tmp, fully_remapped_bgs, bg_list) {
> + struct btrfs_chunk_map *map;
> +
> + map = btrfs_get_chunk_map(fs_info, block_group->start, 1);
> + if (IS_ERR(map))
> + return PTR_ERR(map);
> +
> + ret = btrfs_last_identity_remap_gone(trans, map, block_group);
> + if (ret) {
> + btrfs_free_chunk_map(map);
> + return ret;
> + }
> +
> + /*
> + * Set num_stripes to 0, so that btrfs_remove_dev_extents()
> + * won't run a second time.
> + */
> + map->num_stripes = 0;
> +
> + btrfs_free_chunk_map(map);
> +
> + if (block_group->used == 0) {
> + spin_lock(&fs_info->unused_bgs_lock);
> + list_move_tail(&block_group->bg_list,
> + &fs_info->unused_bgs);
> + spin_unlock(&fs_info->unused_bgs_lock);
> + } else {
> + spin_lock(&fs_info->unused_bgs_lock);
> + list_del_init(&block_group->bg_list);
> + spin_unlock(&fs_info->unused_bgs_lock);
> +
> + btrfs_put_block_group(block_group);
> + }
> + }
> +
> + return 0;
> +}
> +
> int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
> {
> struct btrfs_fs_info *fs_info = trans->fs_info;
> @@ -2999,11 +3046,23 @@ u64 btrfs_get_extent_owner_root(struct btrfs_fs_info *fs_info,
> }
>
> static int do_free_extent_accounting(struct btrfs_trans_handle *trans,
> - u64 bytenr, struct btrfs_squota_delta *delta)
> + u64 bytenr, struct btrfs_squota_delta *delta,
> + struct btrfs_path *path)
> {
> int ret;
> + bool remapped = false;
> u64 num_bytes = delta->num_bytes;
>
> + /* returns 1 on success and 0 on no-op */
> + ret = btrfs_remove_extent_from_remap_tree(trans, path, bytenr,
> + num_bytes);
> + if (ret < 0) {
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + } else if (ret == 1) {
> + remapped = true;
> + }
> +
> if (delta->is_data) {
> struct btrfs_root *csum_root;
>
> @@ -3027,10 +3086,16 @@ static int do_free_extent_accounting(struct btrfs_trans_handle *trans,
> return ret;
> }
>
> - ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes);
> - if (unlikely(ret)) {
> - btrfs_abort_transaction(trans, ret);
> - return ret;
> + /*
> + * If remapped, FST has already been taken care of in
> + * remove_range_from_remap_tree().
> + */
> + if (!remapped) {
> + ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes);
> + if (unlikely(ret)) {
> + btrfs_abort_transaction(trans, ret);
> + return ret;
> + }
> }
>
> ret = btrfs_update_block_group(trans, bytenr, num_bytes, false);
> @@ -3396,7 +3461,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
> }
> btrfs_release_path(path);
>
> - ret = do_free_extent_accounting(trans, bytenr, &delta);
> + ret = do_free_extent_accounting(trans, bytenr, &delta, path);
> }
> btrfs_release_path(path);
>
> diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h
> index e970ac42a871..6b67a4e528da 100644
> --- a/fs/btrfs/extent-tree.h
> +++ b/fs/btrfs/extent-tree.h
> @@ -164,5 +164,6 @@ void btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info, u64 start, u6
> int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
> u64 num_bytes, u64 *actual_bytes);
> int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range);
> +int btrfs_handle_fully_remapped_bgs(struct btrfs_trans_handle *trans);
>
> #endif
> diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
> index 62057e8006a9..c3dacbfe118c 100644
> --- a/fs/btrfs/fs.h
> +++ b/fs/btrfs/fs.h
> @@ -573,6 +573,7 @@ struct btrfs_fs_info {
> struct mutex transaction_kthread_mutex;
> struct mutex cleaner_mutex;
> struct mutex chunk_mutex;
> + struct mutex remap_mutex;
>
> /*
> * This is taken to make sure we don't set block groups ro after the
> @@ -827,10 +828,11 @@ struct btrfs_fs_info {
> struct list_head reclaim_bgs;
> int bg_reclaim_threshold;
>
> - /* Protects the lists unused_bgs and reclaim_bgs. */
> + /* Protects the lists unused_bgs, reclaim_bgs, and fully_remapped_bgs. */
> spinlock_t unused_bgs_lock;
> /* Protected by unused_bgs_lock. */
> struct list_head unused_bgs;
> + struct list_head fully_remapped_bgs;
> struct mutex unused_bg_unpin_mutex;
> /* Protect block groups that are going to be deleted */
> struct mutex reclaim_bgs_lock;
> diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
> index a8abe24de8d7..9f3ce3395d6a 100644
> --- a/fs/btrfs/relocation.c
> +++ b/fs/btrfs/relocation.c
> @@ -37,6 +37,7 @@
> #include "super.h"
> #include "tree-checker.h"
> #include "raid-stripe-tree.h"
> +#include "free-space-tree.h"
>
> /*
> * Relocation overview
> @@ -3870,6 +3871,151 @@ static const char *stage_to_string(enum reloc_stage stage)
> return "unknown";
> }
>
> +static void adjust_block_group_remap_bytes(struct btrfs_trans_handle *trans,
> + struct btrfs_block_group *bg,
> + s64 diff)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + bool bg_already_dirty = true, mark_unused = false;
> +
> + spin_lock(&bg->lock);
> +
> + bg->remap_bytes += diff;
> +
> + if (bg->used == 0 && bg->remap_bytes == 0)
> + mark_unused = true;
> +
> + spin_unlock(&bg->lock);
> +
> + if (mark_unused)
> + btrfs_mark_bg_unused(bg);
> +
> + spin_lock(&trans->transaction->dirty_bgs_lock);
> + if (list_empty(&bg->dirty_list)) {
> + list_add_tail(&bg->dirty_list, &trans->transaction->dirty_bgs);
> + bg_already_dirty = false;
> + btrfs_get_block_group(bg);
> + }
> + spin_unlock(&trans->transaction->dirty_bgs_lock);
> +
> + /* Modified block groups are accounted for in the delayed_refs_rsv. */
> + if (!bg_already_dirty)
> + btrfs_inc_delayed_refs_rsv_bg_updates(fs_info);
> +}
> +
> +static int remove_chunk_stripes(struct btrfs_trans_handle *trans,
> + struct btrfs_chunk_map *chunk,
> + struct btrfs_path *path)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct btrfs_key key;
> + struct extent_buffer *leaf;
> + struct btrfs_chunk *c;
> + int ret;
> +
> + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
> + key.type = BTRFS_CHUNK_ITEM_KEY;
> + key.offset = chunk->start;
> +
> + ret = btrfs_search_slot(trans, fs_info->chunk_root, &key, path,
> + 0, 1);
> + if (ret) {
> + if (ret == 1) {
> + btrfs_release_path(path);
> + ret = -ENOENT;
> + }
> + return ret;
> + }
> +
> + leaf = path->nodes[0];
> +
> + c = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_chunk);
> + btrfs_set_chunk_num_stripes(leaf, c, 0);
> + btrfs_set_chunk_sub_stripes(leaf, c, 0);
> +
> + btrfs_truncate_item(trans, path, offsetof(struct btrfs_chunk, stripe),
> + 1);
> +
> + btrfs_mark_buffer_dirty(trans, leaf);
> +
> + btrfs_release_path(path);
> +
> + return 0;
> +}
> +
> +int btrfs_last_identity_remap_gone(struct btrfs_trans_handle *trans,
> + struct btrfs_chunk_map *chunk,
> + struct btrfs_block_group *bg)
> +{
> + int ret;
> + BTRFS_PATH_AUTO_FREE(path);
> +
> + ret = btrfs_remove_dev_extents(trans, chunk);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&trans->fs_info->chunk_mutex);
> +
> + for (unsigned int i = 0; i < chunk->num_stripes; i++) {
> + ret = btrfs_update_device(trans, chunk->stripes[i].dev);
> + if (ret) {
> + mutex_unlock(&trans->fs_info->chunk_mutex);
> + return ret;
> + }
> + }
> +
> + mutex_unlock(&trans->fs_info->chunk_mutex);
> +
> + write_lock(&trans->fs_info->mapping_tree_lock);
> + btrfs_chunk_map_device_clear_bits(chunk, CHUNK_ALLOCATED);
> + write_unlock(&trans->fs_info->mapping_tree_lock);
> +
> + btrfs_remove_bg_from_sinfo(bg);
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + ret = remove_chunk_stripes(trans, chunk, path);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static void adjust_identity_remap_count(struct btrfs_trans_handle *trans,
> + struct btrfs_block_group *bg, int delta)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + bool bg_already_dirty = true, mark_fully_remapped = false;
> +
> + WARN_ON(delta < 0 && -delta > bg->identity_remap_count);
> +
> + spin_lock(&bg->lock);
> +
> + bg->identity_remap_count += delta;
> +
> + if (!bg->fully_remapped && bg->identity_remap_count == 0)
> + mark_fully_remapped = true;
> +
> + spin_unlock(&bg->lock);
> +
> + spin_lock(&trans->transaction->dirty_bgs_lock);
> + if (list_empty(&bg->dirty_list)) {
> + list_add_tail(&bg->dirty_list, &trans->transaction->dirty_bgs);
> + bg_already_dirty = false;
> + btrfs_get_block_group(bg);
> + }
> + spin_unlock(&trans->transaction->dirty_bgs_lock);
> +
> + /* Modified block groups are accounted for in the delayed_refs_rsv. */
> + if (!bg_already_dirty)
> + btrfs_inc_delayed_refs_rsv_bg_updates(fs_info);
> +
> + if (mark_fully_remapped)
> + btrfs_mark_bg_fully_remapped(bg, trans);
> +}
> +
> int btrfs_translate_remap(struct btrfs_fs_info *fs_info, u64 *logical,
> u64 *length)
> {
> @@ -4478,3 +4624,366 @@ u64 btrfs_get_reloc_bg_bytenr(const struct btrfs_fs_info *fs_info)
> logical = fs_info->reloc_ctl->block_group->start;
> return logical;
> }
> +
> +static int remove_range_from_remap_tree(struct btrfs_trans_handle *trans,
> + struct btrfs_path *path,
> + struct btrfs_block_group *bg,
> + u64 bytenr, u64 num_bytes)
> +{
> + int ret;
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct extent_buffer *leaf = path->nodes[0];
> + struct btrfs_key key, new_key;
> + struct btrfs_remap *remap_ptr = NULL, remap;
> + struct btrfs_block_group *dest_bg = NULL;
> + u64 end, new_addr = 0, remap_start, remap_length, overlap_length;
> + bool is_identity_remap;
> +
> + end = bytenr + num_bytes;
> +
> + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
> +
> + is_identity_remap = key.type == BTRFS_IDENTITY_REMAP_KEY;
> +
> + remap_start = key.objectid;
> + remap_length = key.offset;
> +
> + if (!is_identity_remap) {
> + remap_ptr = btrfs_item_ptr(leaf, path->slots[0],
> + struct btrfs_remap);
> + new_addr = btrfs_remap_address(leaf, remap_ptr);
> +
> + dest_bg = btrfs_lookup_block_group(fs_info, new_addr);
> + }
> +
> + if (bytenr == remap_start && num_bytes >= remap_length) {
> + /* Remove entirely. */
> +
> + ret = btrfs_del_item(trans, fs_info->remap_root, path);
> + if (ret)
> + goto end;
> +
> + btrfs_release_path(path);
> +
> + overlap_length = remap_length;
> +
> + if (!is_identity_remap) {
> + /* Remove backref. */
> +
> + key.objectid = new_addr;
> + key.type = BTRFS_REMAP_BACKREF_KEY;
> + key.offset = remap_length;
> +
> + ret = btrfs_search_slot(trans, fs_info->remap_root,
> + &key, path, -1, 1);
> + if (ret) {
> + if (ret == 1) {
> + btrfs_release_path(path);
> + ret = -ENOENT;
> + }
> + goto end;
> + }
> +
> + ret = btrfs_del_item(trans, fs_info->remap_root, path);
> +
> + btrfs_release_path(path);
> +
> + if (ret)
> + goto end;
> +
> + adjust_block_group_remap_bytes(trans, dest_bg,
> + -remap_length);
> + } else {
> + adjust_identity_remap_count(trans, bg, -1);
> + }
> + } else if (bytenr == remap_start) {
> + /* Remove beginning. */
> +
> + new_key.objectid = end;
> + new_key.type = key.type;
> + new_key.offset = remap_length + remap_start - end;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> + btrfs_mark_buffer_dirty(trans, leaf);
> +
> + overlap_length = num_bytes;
> +
> + if (!is_identity_remap) {
> + btrfs_set_remap_address(leaf, remap_ptr,
> + new_addr + end - remap_start);
> + btrfs_release_path(path);
> +
> + /* Adjust backref. */
> +
> + key.objectid = new_addr;
> + key.type = BTRFS_REMAP_BACKREF_KEY;
> + key.offset = remap_length;
> +
> + ret = btrfs_search_slot(trans, fs_info->remap_root,
> + &key, path, -1, 1);
> + if (ret) {
> + if (ret == 1) {
> + btrfs_release_path(path);
> + ret = -ENOENT;
> + }
> + goto end;
> + }
> +
> + leaf = path->nodes[0];
> +
> + new_key.objectid = new_addr + end - remap_start;
> + new_key.type = BTRFS_REMAP_BACKREF_KEY;
> + new_key.offset = remap_length + remap_start - end;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> +
> + remap_ptr = btrfs_item_ptr(leaf, path->slots[0],
> + struct btrfs_remap);
> + btrfs_set_remap_address(leaf, remap_ptr, end);
> +
> + btrfs_mark_buffer_dirty(trans, path->nodes[0]);
> +
> + btrfs_release_path(path);
> +
> + adjust_block_group_remap_bytes(trans, dest_bg,
> + -num_bytes);
> + }
> + } else if (bytenr + num_bytes < remap_start + remap_length) {
> + /* Remove middle. */
> +
> + new_key.objectid = remap_start;
> + new_key.type = key.type;
> + new_key.offset = bytenr - remap_start;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> + btrfs_mark_buffer_dirty(trans, leaf);
> +
> + new_key.objectid = end;
> + new_key.offset = remap_start + remap_length - end;
> +
> + btrfs_release_path(path);
> +
> + overlap_length = num_bytes;
> +
> + if (!is_identity_remap) {
> + /* Add second remap entry. */
> +
> + ret = btrfs_insert_empty_item(trans, fs_info->remap_root,
> + path, &new_key,
> + sizeof(struct btrfs_remap));
> + if (ret)
> + goto end;
> +
> + btrfs_set_stack_remap_address(&remap,
> + new_addr + end - remap_start);
> +
> + write_extent_buffer(path->nodes[0], &remap,
> + btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
> + sizeof(struct btrfs_remap));
> +
> + btrfs_release_path(path);
> +
> + /* Shorten backref entry. */
> +
> + key.objectid = new_addr;
> + key.type = BTRFS_REMAP_BACKREF_KEY;
> + key.offset = remap_length;
> +
> + ret = btrfs_search_slot(trans, fs_info->remap_root,
> + &key, path, -1, 1);
> + if (ret) {
> + if (ret == 1) {
> + btrfs_release_path(path);
> + ret = -ENOENT;
> + }
> + goto end;
> + }
> +
> + new_key.objectid = new_addr;
> + new_key.type = BTRFS_REMAP_BACKREF_KEY;
> + new_key.offset = bytenr - remap_start;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> + btrfs_mark_buffer_dirty(trans, path->nodes[0]);
> +
> + btrfs_release_path(path);
> +
> + /* Add second backref entry. */
> +
> + new_key.objectid = new_addr + end - remap_start;
> + new_key.type = BTRFS_REMAP_BACKREF_KEY;
> + new_key.offset = remap_start + remap_length - end;
> +
> + ret = btrfs_insert_empty_item(trans, fs_info->remap_root,
> + path, &new_key,
> + sizeof(struct btrfs_remap));
> + if (ret)
> + goto end;
> +
> + btrfs_set_stack_remap_address(&remap, end);
> +
> + write_extent_buffer(path->nodes[0], &remap,
> + btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
> + sizeof(struct btrfs_remap));
> +
> + btrfs_release_path(path);
> +
> + adjust_block_group_remap_bytes(trans, dest_bg,
> + -num_bytes);
> + } else {
> + /* Add second identity remap entry. */
> +
> + ret = btrfs_insert_empty_item(trans, fs_info->remap_root,
> + path, &new_key, 0);
> + if (ret)
> + goto end;
> +
> + btrfs_release_path(path);
> +
> + adjust_identity_remap_count(trans, bg, 1);
> + }
> + } else {
> + /* Remove end. */
> +
> + new_key.objectid = remap_start;
> + new_key.type = key.type;
> + new_key.offset = bytenr - remap_start;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> + btrfs_mark_buffer_dirty(trans, leaf);
> +
> + btrfs_release_path(path);
> +
> + overlap_length = remap_start + remap_length - bytenr;
> +
> + if (!is_identity_remap) {
> + /* Shorten backref entry. */
> +
> + key.objectid = new_addr;
> + key.type = BTRFS_REMAP_BACKREF_KEY;
> + key.offset = remap_length;
> +
> + ret = btrfs_search_slot(trans, fs_info->remap_root,
> + &key, path, -1, 1);
> + if (ret) {
> + if (ret == 1) {
> + btrfs_release_path(path);
> + ret = -ENOENT;
> + }
> + goto end;
> + }
> +
> + new_key.objectid = new_addr;
> + new_key.type = BTRFS_REMAP_BACKREF_KEY;
> + new_key.offset = bytenr - remap_start;
> +
> + btrfs_set_item_key_safe(trans, path, &new_key);
> + btrfs_mark_buffer_dirty(trans, path->nodes[0]);
> +
> + btrfs_release_path(path);
> +
> + adjust_block_group_remap_bytes(trans, dest_bg,
> + bytenr - remap_start - remap_length);
> + }
> + }
> +
> + if (!is_identity_remap) {
> + ret = btrfs_add_to_free_space_tree(trans,
> + bytenr - remap_start + new_addr,
> + overlap_length);
> + if (ret)
> + goto end;
> + }
> +
> + ret = overlap_length;
> +
> +end:
> + if (dest_bg)
> + btrfs_put_block_group(dest_bg);
> +
> + return ret;
> +}
> +
> +/*
> + * Returns 1 if remove_range_from_remap_tree() has been called successfully,
> + * 0 if block group wasn't remapped, and a negative number on error.
> + */
> +int btrfs_remove_extent_from_remap_tree(struct btrfs_trans_handle *trans,
> + struct btrfs_path *path,
> + u64 bytenr, u64 num_bytes)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct btrfs_key key, found_key;
> + struct extent_buffer *leaf;
> + struct btrfs_block_group *bg;
> + int ret, length;
> +
> + if (!(btrfs_super_incompat_flags(fs_info->super_copy) &
> + BTRFS_FEATURE_INCOMPAT_REMAP_TREE))
> + return 0;
> +
> + bg = btrfs_lookup_block_group(fs_info, bytenr);
> + if (!bg)
> + return 0;
> +
> + mutex_lock(&fs_info->remap_mutex);
> +
> + if (!(bg->flags & BTRFS_BLOCK_GROUP_REMAPPED)) {
> + mutex_unlock(&fs_info->remap_mutex);
> + btrfs_put_block_group(bg);
> + return 0;
> + }
> +
> + do {
> + key.objectid = bytenr;
> + key.type = (u8)-1;
> + key.offset = (u64)-1;
> +
> + ret = btrfs_search_slot(trans, fs_info->remap_root, &key, path,
> + -1, 1);
> + if (ret < 0)
> + goto end;
> +
> + leaf = path->nodes[0];
> +
> + if (path->slots[0] == 0) {
> + ret = -ENOENT;
> + goto end;
> + }
> +
> + path->slots[0]--;
> +
> + btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
> +
> + if (found_key.type != BTRFS_IDENTITY_REMAP_KEY &&
> + found_key.type != BTRFS_REMAP_KEY) {
> + ret = -ENOENT;
> + goto end;
> + }
> +
> + if (bytenr < found_key.objectid ||
> + bytenr >= found_key.objectid + found_key.offset) {
> + ret = -ENOENT;
> + goto end;
> + }
> +
> + length = remove_range_from_remap_tree(trans, path, bg, bytenr,
> + num_bytes);
> + if (length < 0) {
> + ret = length;
> + goto end;
> + }
> +
> + bytenr += length;
> + num_bytes -= length;
> + } while (num_bytes > 0);
> +
> + ret = 1;
> +
> +end:
> + mutex_unlock(&fs_info->remap_mutex);
> +
> + btrfs_put_block_group(bg);
> + btrfs_release_path(path);
> + return ret;
> +}
> diff --git a/fs/btrfs/relocation.h b/fs/btrfs/relocation.h
> index b2ba83966650..7cfe91971cab 100644
> --- a/fs/btrfs/relocation.h
> +++ b/fs/btrfs/relocation.h
> @@ -33,5 +33,11 @@ bool btrfs_should_ignore_reloc_root(const struct btrfs_root *root);
> u64 btrfs_get_reloc_bg_bytenr(const struct btrfs_fs_info *fs_info);
> int btrfs_translate_remap(struct btrfs_fs_info *fs_info, u64 *logical,
> u64 *length);
> +int btrfs_remove_extent_from_remap_tree(struct btrfs_trans_handle *trans,
> + struct btrfs_path *path,
> + u64 bytenr, u64 num_bytes);
> +int btrfs_last_identity_remap_gone(struct btrfs_trans_handle *trans,
> + struct btrfs_chunk_map *chunk,
> + struct btrfs_block_group *bg);
>
> #endif
> diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
> index de3eeb37408a..ffee6c285182 100644
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -2437,6 +2437,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
> if (ret)
> goto unlock_reloc;
>
> + ret = btrfs_handle_fully_remapped_bgs(trans);
> + if (ret)
> + goto unlock_reloc;
> +
> /*
> * make sure none of the code above managed to slip in a
> * delayed item
> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> index d117f74e08c1..99ad95e1c300 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -2929,8 +2929,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
> return ret;
> }
>
> -static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
> - struct btrfs_device *device)
> +int btrfs_update_device(struct btrfs_trans_handle *trans,
> + struct btrfs_device *device)
> {
> int ret;
> BTRFS_PATH_AUTO_FREE(path);
> @@ -3228,25 +3228,13 @@ static int remove_chunk_item(struct btrfs_trans_handle *trans,
> return btrfs_free_chunk(trans, chunk_offset);
> }
>
> -int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
> +int btrfs_remove_dev_extents(struct btrfs_trans_handle *trans,
> + struct btrfs_chunk_map *map)
> {
> struct btrfs_fs_info *fs_info = trans->fs_info;
> - struct btrfs_chunk_map *map;
> + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
> u64 dev_extent_len = 0;
> int i, ret = 0;
> - struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
> -
> - map = btrfs_get_chunk_map(fs_info, chunk_offset, 1);
> - if (IS_ERR(map)) {
> - /*
> - * This is a logic error, but we don't want to just rely on the
> - * user having built with ASSERT enabled, so if ASSERT doesn't
> - * do anything we still error out.
> - */
> - DEBUG_WARN("errr %ld reading chunk map at offset %llu",
> - PTR_ERR(map), chunk_offset);
> - return PTR_ERR(map);
> - }
>
> /*
> * First delete the device extent items from the devices btree.
> @@ -3267,7 +3255,7 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
> if (unlikely(ret)) {
> mutex_unlock(&fs_devices->device_list_mutex);
> btrfs_abort_transaction(trans, ret);
> - goto out;
> + return ret;
> }
>
> if (device->bytes_used > 0) {
> @@ -3287,6 +3275,30 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
> }
> mutex_unlock(&fs_devices->device_list_mutex);
>
> + return 0;
> +}
> +
> +int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
> +{
> + struct btrfs_fs_info *fs_info = trans->fs_info;
> + struct btrfs_chunk_map *map;
> + int ret;
> +
> + map = btrfs_get_chunk_map(fs_info, chunk_offset, 1);
> + if (IS_ERR(map)) {
> + /*
> + * This is a logic error, but we don't want to just rely on the
> + * user having built with ASSERT enabled, so if ASSERT doesn't
> + * do anything we still error out.
> + */
> + ASSERT(0);
> + return PTR_ERR(map);
> + }
> +
> + ret = btrfs_remove_dev_extents(trans, map);
> + if (ret)
> + goto out;
> +
> /*
> * We acquire fs_info->chunk_mutex for 2 reasons:
> *
> @@ -5422,7 +5434,7 @@ static void chunk_map_device_set_bits(struct btrfs_chunk_map *map, unsigned int
> }
> }
>
> -static void chunk_map_device_clear_bits(struct btrfs_chunk_map *map, unsigned int bits)
> +void btrfs_chunk_map_device_clear_bits(struct btrfs_chunk_map *map, unsigned int bits)
> {
> for (int i = 0; i < map->num_stripes; i++) {
> struct btrfs_io_stripe *stripe = &map->stripes[i];
> @@ -5439,7 +5451,7 @@ void btrfs_remove_chunk_map(struct btrfs_fs_info *fs_info, struct btrfs_chunk_ma
> write_lock(&fs_info->mapping_tree_lock);
> rb_erase_cached(&map->rb_node, &fs_info->mapping_tree);
> RB_CLEAR_NODE(&map->rb_node);
> - chunk_map_device_clear_bits(map, CHUNK_ALLOCATED);
> + btrfs_chunk_map_device_clear_bits(map, CHUNK_ALLOCATED);
> write_unlock(&fs_info->mapping_tree_lock);
>
> /* Once for the tree reference. */
> @@ -5475,7 +5487,7 @@ int btrfs_add_chunk_map(struct btrfs_fs_info *fs_info, struct btrfs_chunk_map *m
> return -EEXIST;
> }
> chunk_map_device_set_bits(map, CHUNK_ALLOCATED);
> - chunk_map_device_clear_bits(map, CHUNK_TRIMMED);
> + btrfs_chunk_map_device_clear_bits(map, CHUNK_TRIMMED);
> write_unlock(&fs_info->mapping_tree_lock);
>
> return 0;
> @@ -5840,7 +5852,7 @@ void btrfs_mapping_tree_free(struct btrfs_fs_info *fs_info)
> map = rb_entry(node, struct btrfs_chunk_map, rb_node);
> rb_erase_cached(&map->rb_node, &fs_info->mapping_tree);
> RB_CLEAR_NODE(&map->rb_node);
> - chunk_map_device_clear_bits(map, CHUNK_ALLOCATED);
> + btrfs_chunk_map_device_clear_bits(map, CHUNK_ALLOCATED);
> /* Once for the tree ref. */
> btrfs_free_chunk_map(map);
> cond_resched_rwlock_write(&fs_info->mapping_tree_lock);
> diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
> index 7cf76bffcab6..0c64cae59f1c 100644
> --- a/fs/btrfs/volumes.h
> +++ b/fs/btrfs/volumes.h
> @@ -794,6 +794,8 @@ u64 btrfs_calc_stripe_length(const struct btrfs_chunk_map *map);
> int btrfs_nr_parity_stripes(u64 type);
> int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans,
> struct btrfs_block_group *bg);
> +int btrfs_remove_dev_extents(struct btrfs_trans_handle *trans,
> + struct btrfs_chunk_map *map);
> int btrfs_remove_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset);
>
> #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
> @@ -905,6 +907,10 @@ bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical);
>
> bool btrfs_pinned_by_swapfile(struct btrfs_fs_info *fs_info, void *ptr);
> const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb);
> +int btrfs_update_device(struct btrfs_trans_handle *trans,
> + struct btrfs_device *device);
> +void btrfs_chunk_map_device_clear_bits(struct btrfs_chunk_map *map,
> + unsigned int bits);
>
> #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
> struct btrfs_io_context *alloc_btrfs_io_context(struct btrfs_fs_info *fs_info,
> --
> 2.49.1
>
next prev parent reply other threads:[~2025-10-31 23:05 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-24 18:12 [PATCH v4 00/16] Remap tree Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 01/16] btrfs: add definitions and constants for remap-tree Mark Harmstone
2025-10-31 22:50 ` Boris Burkov
2025-11-03 12:18 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 02/16] btrfs: add REMAP chunk type Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 03/16] btrfs: allow remapped chunks to have zero stripes Mark Harmstone
2025-10-31 21:39 ` Boris Burkov
2025-10-24 18:12 ` [PATCH v4 04/16] btrfs: remove remapped block groups from the free-space tree Mark Harmstone
2025-10-31 21:44 ` Boris Burkov
2025-11-03 12:39 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 05/16] btrfs: don't add metadata items for the remap tree to the extent tree Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 06/16] btrfs: add extended version of struct block_group_item Mark Harmstone
2025-10-31 21:47 ` Boris Burkov
2025-10-24 18:12 ` [PATCH v4 07/16] btrfs: allow mounting filesystems with remap-tree incompat flag Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 08/16] btrfs: redirect I/O for remapped block groups Mark Harmstone
2025-10-31 22:03 ` Boris Burkov
2025-10-24 18:12 ` [PATCH v4 09/16] btrfs: handle deletions from remapped block group Mark Harmstone
2025-10-31 23:05 ` Boris Burkov [this message]
2025-11-03 15:51 ` Mark Harmstone
2025-10-31 23:30 ` Boris Burkov
2025-11-04 12:30 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 10/16] btrfs: handle setting up relocation of block group with remap-tree Mark Harmstone
2025-10-31 23:43 ` Boris Burkov
2025-11-03 18:45 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 11/16] btrfs: move existing remaps before relocating block group Mark Harmstone
2025-11-01 0:02 ` Boris Burkov
2025-11-04 13:00 ` Mark Harmstone
2025-11-01 0:10 ` Boris Burkov
2025-10-24 18:12 ` [PATCH v4 12/16] btrfs: replace identity remaps with actual remaps when doing relocations Mark Harmstone
2025-11-01 0:09 ` Boris Burkov
2025-11-04 14:31 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 13/16] btrfs: add do_remap param to btrfs_discard_extent() Mark Harmstone
2025-11-01 0:12 ` Boris Burkov
2025-10-24 18:12 ` [PATCH v4 14/16] btrfs: allow balancing remap tree Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 15/16] btrfs: handle discarding fully-remapped block groups Mark Harmstone
2025-10-27 16:04 ` kernel test robot
2025-10-31 22:12 ` Boris Burkov
2025-11-03 16:49 ` Mark Harmstone
2025-11-09 8:42 ` Philip Li
2025-10-31 22:11 ` Boris Burkov
2025-11-03 17:01 ` Mark Harmstone
2025-10-24 18:12 ` [PATCH v4 16/16] btrfs: add stripe removal pending flag Mark Harmstone
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aQVAtcfRy2rctnaA@devvm12410.ftw0.facebook.com \
--to=boris@bur.io \
--cc=linux-btrfs@vger.kernel.org \
--cc=mark@harmstone.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox