btrfs: update stripe extents for existing logical addresses
authorJohannes Thumshirn <johannes.thumshirn@wdc.com>
Mon, 8 Jul 2024 11:24:08 +0000 (13:24 +0200)
committerDavid Sterba <dsterba@suse.com>
Tue, 10 Sep 2024 14:51:12 +0000 (16:51 +0200)
Update a stripe extent in case of an already existing logical address,
but with different physical addresses and/or device id instead of
bailing out with EEXIST.

This can happen i.e. in case of a device replace operation, where data
extents get rewritten to a new disk.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/raid-stripe-tree.c

index e6f7a23..0c7b928 100644 (file)
@@ -73,6 +73,36 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le
        return ret;
 }
 
+static int update_raid_extent_item(struct btrfs_trans_handle *trans,
+                                  struct btrfs_key *key,
+                                  struct btrfs_stripe_extent *stripe_extent,
+                                  const size_t item_size)
+{
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       int ret;
+       int slot;
+
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+
+       ret = btrfs_search_slot(trans, trans->fs_info->stripe_root, key, path,
+                               0, 1);
+       if (ret)
+               return (ret == 1 ? ret : -EINVAL);
+
+       leaf = path->nodes[0];
+       slot = path->slots[0];
+
+       write_extent_buffer(leaf, stripe_extent, btrfs_item_ptr_offset(leaf, slot),
+                           item_size);
+       btrfs_mark_buffer_dirty(trans, leaf);
+       btrfs_free_path(path);
+
+       return ret;
+}
+
 static int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,
                                        struct btrfs_io_context *bioc)
 {
@@ -112,6 +142,9 @@ static int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,
 
        ret = btrfs_insert_item(trans, stripe_root, &stripe_key, stripe_extent,
                                item_size);
+       if (ret == -EEXIST)
+               ret = update_raid_extent_item(trans, &stripe_key, stripe_extent,
+                                             item_size);
        if (ret)
                btrfs_abort_transaction(trans, ret);