Re: [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA

[email protected] Thu, 23 Jul 2026 21:13:07 +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 3 potential issue(s) to consider:

New issues:
- [High] Bypassing `freeze_array()` for `BLK_STS_P2PDMA` in `raid1.c` introduces a hard deadlock against concurrent array freezes.

Pre-existing issues:
- [High] Race condition leading to a NULL pointer dereference in `raid1_read_request()` when logging an unrecoverable read error.
- [High] Double barrier drop (`nr_pending` underflow) when a `REQ_NOWAIT` read fails while the array is frozen.
--

--- Patch [8]: [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA ---
commit 39fd90f26512b119562fa95bb3bbc654fa9ed8a3
Author: Mykola Marzhan <[email protected]>

md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA

A read that fails with BLK_STS_P2PDMA cannot succeed against that
member, so mark the leg IO_BLOCKED and let the retry redirect elsewhere.
Skip the read-error machinery: there is nothing on the medium to fix,
fix_read_error()'s probe reads into host pages and would "succeed", and
charging the read-error budget would evict a healthy member under a P2P
read workload.  FailFast eviction is skipped for the same reason -- the
request never reached the wire.

Fixes: 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
Assisted-by: Claude:claude-fable-5
Co-developed-by: Logan Gunthorpe <[email protected]>
Signed-off-by: Logan Gunthorpe <[email protected]>
Signed-off-by: Mykola Marzhan <[email protected]>

> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 83ebf40237d57..16a47568679f0 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c

[ ... ]

> @@ -2674,7 +2676,7 @@ static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
>  	 * read error.  This is all done synchronously while the array is
>  	 * frozen.
>  	 */
> -	if (mddev->ro) {
> +	if (mddev->ro || status == BLK_STS_P2PDMA) {
>  		r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED;

[Severity: High]
Does skipping freeze_array() for P2PDMA errors here risk a hard deadlock?

If a concurrent thread calls freeze_array(conf, 0) during a management command,
it sets array_frozen = 1 and waits for get_unqueued_pending() == 0.

Meanwhile, this handle_read_error() thread bypasses freeze_array() and
proceeds to call raid1_read_request() while holding the original nr_pending
reference.

When raid1_read_request() calls wait_read_barrier():

drivers/md/raid1.c:raid1_read_request() {
    ...
    if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) {
    ...
}

And wait_read_barrier() sees the array is frozen:

drivers/md/raid1.c:wait_read_barrier() {
    ...
    if (unlikely(conf->array_frozen)) {
        ...
        wait_event_lock_irq_cmd(...)
    ...
}

The raid1d thread will block on array_frozen, but because it still holds its
nr_pending reference, get_unqueued_pending() will remain >= 1. The freezer
thread will wait forever for the reference to drop, and raid1d will wait
forever for the array to unfreeze.

>  	} else if (test_bit(FailFast, &rdev->flags)) {
>  		md_error(mddev, rdev);

[Severity: High]
This is a pre-existing issue, but does dropping the active reference to the
failing device later in handle_read_error() introduce a race condition with
concurrent disk removal?

Looking further down in handle_read_error():

    rdev_dec_pending(rdev, conf->mddev);
    sector = r1_bio->sector;
    ...
    raid1_read_request(mddev, bio, r1_bio->sectors, r1_bio);

Since raid1d doesn't hold reconfig_mutex, a concurrent raid1_remove_disk() can
observe nr_pending == 0 and clear the device pointer. If the subsequent call
to raid1_read_request() fails to find an alternative disk in read_balance(),
it attempts to log a critical error:

drivers/md/raid1.c:raid1_read_request() {
    ...
    if (r1_bio->read_disk < 0) {
        ...
        pr_crit_ratelimited("md/raid1:%s: %pg: unrecoverable I/O read...\n",
                            mdname(mddev),
                            conf->mirrors[r1_bio->read_disk].rdev->bdev,
                            ...);
    ...
}

Dereferencing conf->mirrors[r1_bio->read_disk].rdev->bdev when rdev has been
set to NULL would cause a fatal page fault.


[Severity: High]
This is also a pre-existing issue, but could the unconditional allow_barrier()
at the end of handle_read_error() lead to a double barrier drop if a
REQ_NOWAIT read fails while the array is frozen?

Looking at the end of the function:

    raid1_read_request(mddev, bio, r1_bio->sectors, r1_bio);
    allow_barrier(conf, sector);

If wait_read_barrier() inside raid1_read_request() encounters a frozen array
and returns false due to REQ_NOWAIT, it does not increment nr_pending:

drivers/md/raid1.c:raid1_read_request() {
    ...
    if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) {
        bio_wouldblock_error(bio);
        raid_end_bio_io(r1_bio);
        return;
    ...
}

raid_end_bio_io() internally calls allow_barrier() to drop the original
reference. After raid1_read_request() returns, handle_read_error() will
unconditionally execute this second allow_barrier(conf, sector).

This would cause nr_pending to underflow, leading to a permanent deadlock on
any subsequent array freeze since raise_barrier() will block forever waiting
for nr_pending == 0.

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