btrfs: split discard handling out of btrfs_map_block
authorChristoph Hellwig <hch@lst.de>
Fri, 3 Jun 2022 06:57:25 +0000 (08:57 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 25 Jul 2022 15:45:34 +0000 (17:45 +0200)
Mapping block for discard doesn't really share any code with the regular
block mapping case.  Split it out into an entirely separate helper
that just returns an array of btrfs_discard_stripe structures and the
number of stripes.

This removes the need for the length field in the btrfs_io_context
structure, so remove tht.

Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent-tree.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index a3afc15..91d2c20 100644 (file)
@@ -1269,7 +1269,7 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
        return ret;
 }
 
-static int do_discard_extent(struct btrfs_io_stripe *stripe, u64 *bytes)
+static int do_discard_extent(struct btrfs_discard_stripe *stripe, u64 *bytes)
 {
        struct btrfs_device *dev = stripe->dev;
        struct btrfs_fs_info *fs_info = dev->fs_info;
@@ -1316,76 +1316,60 @@ int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
        u64 discarded_bytes = 0;
        u64 end = bytenr + num_bytes;
        u64 cur = bytenr;
-       struct btrfs_io_context *bioc = NULL;
 
        /*
-        * Avoid races with device replace and make sure our bioc has devices
-        * associated to its stripes that don't go away while we are discarding.
+        * Avoid races with device replace and make sure the devices in the
+        * stripes don't go away while we are discarding.
         */
        btrfs_bio_counter_inc_blocked(fs_info);
        while (cur < end) {
-               struct btrfs_io_stripe *stripe;
+               struct btrfs_discard_stripe *stripes;
+               unsigned int num_stripes;
                int i;
 
                num_bytes = end - cur;
-               /* Tell the block device(s) that the sectors can be discarded */
-               ret = btrfs_map_block(fs_info, BTRFS_MAP_DISCARD, cur,
-                                     &num_bytes, &bioc, 0);
-               /*
-                * Error can be -ENOMEM, -ENOENT (no such chunk mapping) or
-                * -EOPNOTSUPP. For any such error, @num_bytes is not updated,
-                * thus we can't continue anyway.
-                */
-               if (ret < 0)
-                       goto out;
+               stripes = btrfs_map_discard(fs_info, cur, &num_bytes, &num_stripes);
+               if (IS_ERR(stripes)) {
+                       ret = PTR_ERR(stripes);
+                       if (ret == -EOPNOTSUPP)
+                               ret = 0;
+                       break;
+               }
 
-               stripe = bioc->stripes;
-               for (i = 0; i < bioc->num_stripes; i++, stripe++) {
+               for (i = 0; i < num_stripes; i++) {
+                       struct btrfs_discard_stripe *stripe = stripes + i;
                        u64 bytes;
-                       struct btrfs_device *device = stripe->dev;
 
-                       if (!device->bdev) {
+                       if (!stripe->dev->bdev) {
                                ASSERT(btrfs_test_opt(fs_info, DEGRADED));
                                continue;
                        }
 
-                       if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
+                       if (!test_bit(BTRFS_DEV_STATE_WRITEABLE,
+                                       &stripe->dev->dev_state))
                                continue;
 
                        ret = do_discard_extent(stripe, &bytes);
-                       if (!ret) {
-                               discarded_bytes += bytes;
-                       } else if (ret != -EOPNOTSUPP) {
+                       if (ret) {
                                /*
-                                * Logic errors or -ENOMEM, or -EIO, but
-                                * unlikely to happen.
-                                *
-                                * And since there are two loops, explicitly
-                                * go to out to avoid confusion.
+                                * Keep going if discard is not supported by the
+                                * device.
                                 */
-                               btrfs_put_bioc(bioc);
-                               goto out;
+                               if (ret != -EOPNOTSUPP)
+                                       break;
+                               ret = 0;
+                       } else {
+                               discarded_bytes += bytes;
                        }
-
-                       /*
-                        * Just in case we get back EOPNOTSUPP for some reason,
-                        * just ignore the return value so we don't screw up
-                        * people calling discard_extent.
-                        */
-                       ret = 0;
                }
-               btrfs_put_bioc(bioc);
+               kfree(stripes);
+               if (ret)
+                       break;
                cur += num_bytes;
        }
-out:
        btrfs_bio_counter_dec(fs_info);
-
        if (actual_bytes)
                *actual_bytes = discarded_bytes;
-
-
-       if (ret == -EOPNOTSUPP)
-               ret = 0;
        return ret;
 }
 
index 7513e45..12a6150 100644 (file)
@@ -5913,18 +5913,17 @@ void btrfs_put_bioc(struct btrfs_io_context *bioc)
                kfree(bioc);
 }
 
-/* can REQ_OP_DISCARD be sent with other REQ like REQ_OP_WRITE? */
 /*
  * Please note that, discard won't be sent to target device of device
  * replace.
  */
-static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
-                                        u64 logical, u64 *length_ret,
-                                        struct btrfs_io_context **bioc_ret)
+struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
+                                              u64 logical, u64 *length_ret,
+                                              u32 *num_stripes)
 {
        struct extent_map *em;
        struct map_lookup *map;
-       struct btrfs_io_context *bioc;
+       struct btrfs_discard_stripe *stripes;
        u64 length = *length_ret;
        u64 offset;
        u64 stripe_nr;
@@ -5933,29 +5932,26 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
        u64 stripe_cnt;
        u64 stripe_len;
        u64 stripe_offset;
-       u64 num_stripes;
        u32 stripe_index;
        u32 factor = 0;
        u32 sub_stripes = 0;
        u64 stripes_per_dev = 0;
        u32 remaining_stripes = 0;
        u32 last_stripe = 0;
-       int ret = 0;
+       int ret;
        int i;
 
-       /* Discard always returns a bioc. */
-       ASSERT(bioc_ret);
-
        em = btrfs_get_chunk_map(fs_info, logical, length);
        if (IS_ERR(em))
-               return PTR_ERR(em);
+               return ERR_CAST(em);
 
        map = em->map_lookup;
+
        /* we don't discard raid56 yet */
        if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
                ret = -EOPNOTSUPP;
-               goto out;
-       }
+               goto out_free_map;
+}
 
        offset = logical - em->start;
        length = min_t(u64, em->start + em->len - logical, length);
@@ -5981,7 +5977,7 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
         * device we have to walk to find the data, and stripe_index is
         * the number of our device in the stripe array
         */
-       num_stripes = 1;
+       *num_stripes = 1;
        stripe_index = 0;
        if (map->type & (BTRFS_BLOCK_GROUP_RAID0 |
                         BTRFS_BLOCK_GROUP_RAID10)) {
@@ -5991,7 +5987,7 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
                        sub_stripes = map->sub_stripes;
 
                factor = map->num_stripes / sub_stripes;
-               num_stripes = min_t(u64, map->num_stripes,
+               *num_stripes = min_t(u64, map->num_stripes,
                                    sub_stripes * stripe_cnt);
                stripe_nr = div_u64_rem(stripe_nr, factor, &stripe_index);
                stripe_index *= sub_stripes;
@@ -6001,31 +5997,30 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
                last_stripe *= sub_stripes;
        } else if (map->type & (BTRFS_BLOCK_GROUP_RAID1_MASK |
                                BTRFS_BLOCK_GROUP_DUP)) {
-               num_stripes = map->num_stripes;
+               *num_stripes = map->num_stripes;
        } else {
                stripe_nr = div_u64_rem(stripe_nr, map->num_stripes,
                                        &stripe_index);
        }
 
-       bioc = alloc_btrfs_io_context(fs_info, num_stripes, 0);
-       if (!bioc) {
+       stripes = kcalloc(*num_stripes, sizeof(*stripes), GFP_NOFS);
+       if (!stripes) {
                ret = -ENOMEM;
-               goto out;
+               goto out_free_map;
        }
 
-       for (i = 0; i < num_stripes; i++) {
-               bioc->stripes[i].physical =
+       for (i = 0; i < *num_stripes; i++) {
+               stripes[i].physical =
                        map->stripes[stripe_index].physical +
                        stripe_offset + stripe_nr * map->stripe_len;
-               bioc->stripes[i].dev = map->stripes[stripe_index].dev;
+               stripes[i].dev = map->stripes[stripe_index].dev;
 
                if (map->type & (BTRFS_BLOCK_GROUP_RAID0 |
                                 BTRFS_BLOCK_GROUP_RAID10)) {
-                       bioc->stripes[i].length = stripes_per_dev *
-                               map->stripe_len;
+                       stripes[i].length = stripes_per_dev * map->stripe_len;
 
                        if (i / sub_stripes < remaining_stripes)
-                               bioc->stripes[i].length += map->stripe_len;
+                               stripes[i].length += map->stripe_len;
 
                        /*
                         * Special for the first stripe and
@@ -6036,17 +6031,17 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
                         *    off     end_off
                         */
                        if (i < sub_stripes)
-                               bioc->stripes[i].length -= stripe_offset;
+                               stripes[i].length -= stripe_offset;
 
                        if (stripe_index >= last_stripe &&
                            stripe_index <= (last_stripe +
                                             sub_stripes - 1))
-                               bioc->stripes[i].length -= stripe_end_offset;
+                               stripes[i].length -= stripe_end_offset;
 
                        if (i == sub_stripes - 1)
                                stripe_offset = 0;
                } else {
-                       bioc->stripes[i].length = length;
+                       stripes[i].length = length;
                }
 
                stripe_index++;
@@ -6056,12 +6051,11 @@ static int __btrfs_map_block_for_discard(struct btrfs_fs_info *fs_info,
                }
        }
 
-       *bioc_ret = bioc;
-       bioc->map_type = map->type;
-       bioc->num_stripes = num_stripes;
-out:
        free_extent_map(em);
-       return ret;
+       return stripes;
+out_free_map:
+       free_extent_map(em);
+       return ERR_PTR(ret);
 }
 
 /*
@@ -6204,7 +6198,6 @@ static void handle_ops_on_dev_replace(enum btrfs_map_op op,
                                        bioc->stripes + i;
 
                                new->physical = old->physical;
-                               new->length = old->length;
                                new->dev = dev_replace->tgtdev;
                                bioc->tgtdev_map[i] = index_where_to_add;
                                index_where_to_add++;
@@ -6245,8 +6238,6 @@ static void handle_ops_on_dev_replace(enum btrfs_map_op op,
                                bioc->stripes + num_stripes;
 
                        tgtdev_stripe->physical = physical_of_found;
-                       tgtdev_stripe->length =
-                               bioc->stripes[index_srcdev].length;
                        tgtdev_stripe->dev = dev_replace->tgtdev;
                        bioc->tgtdev_map[index_srcdev] = num_stripes;
 
@@ -6600,10 +6591,6 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
                      u64 logical, u64 *length,
                      struct btrfs_io_context **bioc_ret, int mirror_num)
 {
-       if (op == BTRFS_MAP_DISCARD)
-               return __btrfs_map_block_for_discard(fs_info, logical,
-                                                    length, bioc_ret);
-
        return __btrfs_map_block(fs_info, op, logical, length, bioc_ret,
                                 mirror_num, 0);
 }
index a3c3a0d..588367c 100644 (file)
@@ -418,7 +418,12 @@ struct btrfs_io_stripe {
                /* For the endio handler */
                struct btrfs_io_context *bioc;
        };
-       u64 length; /* only used for discard mappings */
+};
+
+struct btrfs_discard_stripe {
+       struct btrfs_device *dev;
+       u64 physical;
+       u64 length;
 };
 
 /*
@@ -557,6 +562,9 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
 int btrfs_map_sblock(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
                     u64 logical, u64 *length,
                     struct btrfs_io_context **bioc_ret);
+struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
+                                              u64 logical, u64 *length_ret,
+                                              u32 *num_stripes);
 int btrfs_get_io_geometry(struct btrfs_fs_info *fs_info, struct extent_map *map,
                          enum btrfs_map_op op, u64 logical,
                          struct btrfs_io_geometry *io_geom);