Re: [PATCH v3 7/8] md/raid10: skip futile retries on P2PDMA mapping failures

[email protected] Tue, 21 Jul 2026 18:01:55 +0000
Newsgroups org.kernel.vger.linux-raid,org.kernel.vger.linux-pci
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] narrow_write_error() drops critical bio flags (e.g., REQ_FUA, REQ_PREFLUSH) during write error retries.
- [High] fix_read_error() incorrectly attributes read errors on a replacement device to the primary device.
--

--- Patch [7]: [PATCH v3 7/8] md/raid10: skip futile retries on P2PDMA mapping failures ---
commit 850a2ebdf179522973aa4c7332fa7233fdc47fc1
Author: Mykola Marzhan <[email protected]>
Subject: md/raid10: skip futile retries on P2PDMA mapping failures

This commit handles P2PDMA mapping failures by skipping futile retries
and retrying the write once as a whole range. It also leaves replacement
legs with the default fail-on-write-error policy to avoid silent holes
during rebuilds.

Fixes: 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")

> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index f7ef903a3d4ec..4c3da50250a5a 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
[ ... ]
> @@ -2814,9 +2831,12 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
>  		block_sectors = roundup(1 << rdev->badblocks.shift, lbs);
>  
>  	sector = r10_bio->sector;
> -	sectors = ((r10_bio->sector + block_sectors)
> -		   & ~(sector_t)(block_sectors - 1))
> -		- sector;
> +	if (coarse)
> +		sectors = sect_to_write;
> +	else
> +		sectors = ((r10_bio->sector + block_sectors)
> +			   & ~(sector_t)(block_sectors - 1))
> +			- sector;
>  
>  	while (sect_to_write) {
>  		struct bio *wbio;

[Severity: High]
This is a pre-existing issue, but does narrow_write_error() drop critical
bio flags like REQ_FUA and REQ_PREFLUSH here?

Further down in narrow_write_error(), the clone's operation flags are
overwritten with a bare write:

drivers/md/raid10.c:narrow_write_error() {
    ...
    wbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO, &mddev->bio_set);
    bio_trim(wbio, sector - bio->bi_iter.bi_sector, sectors);
    ...
    wbio->bi_opf = REQ_OP_WRITE;
    ...
}

If the master bio was submitted with REQ_FUA to guarantee data persistence,
these flags are stripped and the retry is issued as a normal asynchronous
write.

If the retry succeeds by placing data in the drive's volatile cache, the
master bio completes successfully. Could a power failure before the cache
is flushed to stable media result in silent data loss, violating the
original REQ_FUA guarantees?

(Note: The identical logic appears to be present in drivers/md/raid1.c in
narrow_write_error() as well).

[ ... ]
> @@ -2856,6 +2876,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
>  {
>  	int slot = r10_bio->read_slot;
>  	struct bio *bio;
> +	bool p2pdma_error;
>  	struct r10conf *conf = mddev->private;
>  	struct md_rdev *rdev = r10_bio->devs[slot].rdev;
>  
> @@ -2868,17 +2889,24 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
>  	 * frozen.
>  	 */
>  	bio = r10_bio->devs[slot].bio;
> +	/* evaluate before the bio_put() below */
> +	p2pdma_error = bio->bi_status == BLK_STS_TARGET &&
> +		test_bit(R10BIO_P2PDMA, &r10_bio->state);
>  	bio_put(bio);
>  	r10_bio->devs[slot].bio = NULL;
>  
>  	if (mddev->ro)
>  		r10_bio->devs[slot].bio = IO_BLOCKED;
> -	else if (!test_bit(FailFast, &rdev->flags)) {
> +	else if (p2pdma_error) {
> +		/* Peer can't reach this member: just redirect the read. */
> +		r10_bio->devs[slot].bio = IO_BLOCKED;
> +	} else if (test_bit(FailFast, &rdev->flags)) {
> +		md_error(mddev, rdev);
> +	} else {
>  		freeze_array(conf, 1);
>  		fix_read_error(conf, mddev, r10_bio);
>  		unfreeze_array(conf);
> -	} else
> -		md_error(mddev, rdev);
> +	}
>  
>  	rdev_dec_pending(rdev, mddev);
>  	r10_bio->state = 0;

[Severity: High]
This is a pre-existing issue, but does fix_read_error() incorrectly attribute
read errors on a replacement device to the primary device?

When read_balance() selects the replacement device for a read and it fails,
handle_read_error() passes the context to fix_read_error().

However, fix_read_error() ignores the actual device that failed
(r10_bio->devs[slot].rdev) and blindly selects the primary device:

drivers/md/raid10.c:fix_read_error() {
    ...
    /* still own a reference to this rdev, so it cannot
     * have been cleared recently.
     */
    rdev = conf->mirrors[d].rdev;

    if (test_bit(Faulty, &rdev->flags))
    ...
}

Later, bad blocks are also recorded on the primary device:

drivers/md/raid10.c:fix_read_error() {
    ...
    int dn = r10_bio->devs[slot].devnum;
    rdev = conf->mirrors[dn].rdev;

    if (!rdev_set_badblocks(
                rdev,
                r10_bio->devs[slot].addr
                + sect,
                s, 0)) {
    ...
}

Could this result in the healthy primary device being penalized via
exceed_read_errors() and incorrectly targeted for bad block recording,
while the faulty replacement device continues to operate without
correction?

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=7