[PATCH v2 2/3] md/raid5: validate discard support at request time

From: Yu Kuai

Date: Fri Jun 05 2026 - 03:37:25 EST


From: Yu Kuai <yukuai@xxxxxxx>

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.

Signed-off-by: Yu Kuai <yukuai@xxxxxxx>
---
drivers/md/raid5.c | 34 +++++++++++++++++++---------------
drivers/md/raid5.h | 1 +
2 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index debf35342ae0..76e736ee48d3 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -1132,10 +1132,22 @@ static void defer_issue_bios(struct r5conf *conf, sector_t sector,
spin_unlock(&conf->pending_bios_lock);

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
raid5_end_write_request(struct bio *bi);

@@ -5702,10 +5714,13 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)

if (mddev->reshape_position != MaxSector)
/* 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,
stripe_sectors);
last_stripe = bio_end_sector(bi);
@@ -7815,36 +7830,25 @@ static int raid5_set_limits(struct mddev *mddev)
mddev_stack_rdev_limits(mddev, &lim, 0);
rdev_for_each(rdev, 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.
* Limit the max sectors based on this.
*/
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;

return queue_limits_set(mddev->gendisk->queue, &lim);
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 1c7b710fc9c1..ba06cf88aa24 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -687,10 +687,11 @@ struct r5conf {
struct r5pending_data *pending_data;
struct list_head free_list;
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;
};

--
2.51.0