Message ID | 73a3c0f290dc84b8b802c8705503370d8cb62bc8.1733989299.git.jth@kernel.org (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | btrfs: more RST delete fixes | expand |
On Thu, Dec 12, 2024 at 7:56 AM Johannes Thumshirn <jth@kernel.org> wrote: > > From: Johannes Thumshirn <johannes.thumshirn@wdc.com> > > When a user requests the deletion of a range that spans multiple stripe > extents and btrfs_search_slot() returns us the second RAID stripe extent, > we need to pick the previous item and truncate it, if there's still a > range to delete left, move on to the next item. > > The following diagram illustrates the operation: > > |--- RAID Stripe Extent ---||--- RAID Stripe Extent ---| > |--- keep ---|--- drop ---| > > While at it, comment the trivial case of a whole item delete as well. > > Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> > --- > fs/btrfs/raid-stripe-tree.c | 30 ++++++++++++++++++++++++++++++ > 1 file changed, 30 insertions(+) > > diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c > index 6246ab4c1a21..ccf455b30314 100644 > --- a/fs/btrfs/raid-stripe-tree.c > +++ b/fs/btrfs/raid-stripe-tree.c > @@ -101,6 +101,33 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le > found_end = found_start + key.offset; > ret = 0; > > + /* > + * The stripe extent starts before the range we want to delete, > + * but the range spans more than one stripe extent: > + * > + * |--- RAID Stripe Extent ---||--- RAID Stripe Extent ---| > + * |--- keep ---|--- drop ---| > + * > + * This means we have to get the previous item, truncate its > + * length and then restart the search. > + */ > + if (found_start > start) { > + if (slot == 0) > + break; Why? The last item on the previous leaf may be the first stripe extent in the diagram, it may partially cover the range we want to delete. Or there may be no previous item at all with a range that partially overlaps but the current item partially overlaps, so we shouldn't break and stop - we should process the current item. Remember that btrfs_previous_items() goes to the previous leaf in case the current slot is 0. Thanks. > + > + ret = btrfs_previous_item(stripe_root, path, start, > + BTRFS_RAID_STRIPE_KEY); > + if (ret < 0) > + break; > + ret = 0; > + > + leaf = path->nodes[0]; > + slot = path->slots[0]; > + btrfs_item_key_to_cpu(leaf, &key, slot); > + found_start = key.objectid; > + found_end = found_start + key.offset; > + } > + > if (key.type != BTRFS_RAID_STRIPE_KEY) > break; > > @@ -155,6 +182,9 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le > break; > } > > + /* > + * Finally we can delete the whole item, no more special cases. > + */ > ret = btrfs_del_item(trans, stripe_root, path); > if (ret) > break; > -- > 2.43.0 > >
diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index 6246ab4c1a21..ccf455b30314 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -101,6 +101,33 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le found_end = found_start + key.offset; ret = 0; + /* + * The stripe extent starts before the range we want to delete, + * but the range spans more than one stripe extent: + * + * |--- RAID Stripe Extent ---||--- RAID Stripe Extent ---| + * |--- keep ---|--- drop ---| + * + * This means we have to get the previous item, truncate its + * length and then restart the search. + */ + if (found_start > start) { + if (slot == 0) + break; + + ret = btrfs_previous_item(stripe_root, path, start, + BTRFS_RAID_STRIPE_KEY); + if (ret < 0) + break; + ret = 0; + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + found_start = key.objectid; + found_end = found_start + key.offset; + } + if (key.type != BTRFS_RAID_STRIPE_KEY) break; @@ -155,6 +182,9 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le break; } + /* + * Finally we can delete the whole item, no more special cases. + */ ret = btrfs_del_item(trans, stripe_root, path); if (ret) break;