]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md/raid5: validate discard support at request time
authorYu Kuai <yukuai@fygo.io>
Fri, 5 Jun 2026 07:26:38 +0000 (15:26 +0800)
committerYu Kuai <yukuai@fygo.io>
Sat, 20 Jun 2026 19:16:24 +0000 (03:16 +0800)
Raid5 used to disable discard limits when devices_handle_discard_safely
was not set or when stacked member limits could not support a full-stripe
discard. That hides discard from userspace before raid5 can decide whether
a request can be handled safely.

Follow other virtual drivers and advertise a UINT_MAX discard limit for the
md device. Cache lower discard support in r5conf when setting queue limits,
and reject unsupported discard bios before queuing stripe work.

Link: https://patch.msgid.link/20260605072639.2434847-3-yukuai@kernel.org
Signed-off-by: Yu Kuai <yukuai@fygo.io>
drivers/md/raid5.c
drivers/md/raid5.h

index debf35342ae0a9ca4c88f7cc12d8ad18b0004663..76e736ee48d386b92f45a7eeb9bb302302fd49f4 100644 (file)
@@ -1134,6 +1134,18 @@ static void defer_issue_bios(struct r5conf *conf, sector_t sector,
        dispatch_bio_list(&tmp);
 }
 
+static bool raid5_discard_limits(struct mddev *mddev, struct bio *bi)
+{
+       struct r5conf *conf = mddev->private;
+
+       if (!conf->raid5_discard_unsupported)
+               return true;
+
+       bi->bi_status = BLK_STS_NOTSUPP;
+       bio_endio(bi);
+       return false;
+}
+
 static void
 raid5_end_read_request(struct bio *bi);
 static void
@@ -5704,6 +5716,9 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
                /* Skip discard while reshape is happening */
                return;
 
+       if (!raid5_discard_limits(mddev, bi))
+               return;
+
        stripe_sectors = conf->chunk_sectors *
                (conf->raid_disks - conf->max_degraded);
        first_stripe = DIV_ROUND_UP_SECTOR_T(bi->bi_iter.bi_sector,
@@ -7817,24 +7832,12 @@ static int raid5_set_limits(struct mddev *mddev)
                queue_limits_stack_bdev(&lim, rdev->bdev, rdev->new_data_offset,
                                mddev->gendisk->disk_name);
 
-       /*
-        * Zeroing is required for discard, otherwise data could be lost.
-        *
-        * Consider a scenario: discard a stripe (the stripe could be
-        * inconsistent if discard_zeroes_data is 0); write one disk of the
-        * stripe (the stripe could be inconsistent again depending on which
-        * disks are used to calculate parity); the disk is broken; The stripe
-        * data of this disk is lost.
-        *
-        * We only allow DISCARD if the sysadmin has confirmed that only safe
-        * devices are in use by setting a module parameter.  A better idea
-        * might be to turn DISCARD into WRITE_ZEROES requests, as that is
-        * required to be safe.
-        */
        if (!devices_handle_discard_safely ||
            lim.max_discard_sectors < (stripe >> 9) ||
            lim.discard_granularity < stripe)
-               lim.max_hw_discard_sectors = 0;
+               conf->raid5_discard_unsupported = true;
+       else
+               conf->raid5_discard_unsupported = false;
 
        /*
         * Requests require having a bitmap for each stripe.
@@ -7843,6 +7846,7 @@ static int raid5_set_limits(struct mddev *mddev)
        lim.max_hw_sectors = RAID5_MAX_REQ_STRIPES << RAID5_STRIPE_SHIFT(conf);
        if ((lim.max_hw_sectors << 9) < lim.io_opt)
                lim.max_hw_sectors = lim.io_opt >> 9;
+       lim.max_hw_discard_sectors = UINT_MAX;
 
        /* No restrictions on the number of segments in the request */
        lim.max_segments = USHRT_MAX;
index 1dfa60a41d91f8a6af00c21839464967f74ad287..cb5feae04db27b4fa23853a915459d706f5a07da 100644 (file)
@@ -689,6 +689,7 @@ struct r5conf {
        struct list_head        pending_list;
        int                     pending_data_cnt;
        struct r5pending_data   *next_pending_data;
+       bool                    raid5_discard_unsupported;
 
        mempool_t               *ctx_pool;
        int                     ctx_size;