[PATCH v2 6/7] md/raid1,raid10: skip futile retries on P2PDMA mapping failures

From: Mykola Marzhan

Date: Sun Jul 19 2026 - 06:59:56 EST


Since commit 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from
member devices to RAID device") raid1 and raid10 arrays accept P2PDMA
(peer device memory) bios, and a member that cannot DMA-map the
peer's pages fails its leg bio with BLK_STS_TARGET (from
blk_dma_map_iter_start()). That mapping failure is a property of the
peer-device/member pairing, not of the medium: re-submitting the same
peer pages to the same member cannot succeed, and there is nothing on
the disk to repair. Routing it through the stock error machinery
misfires on every path:

- narrow_write_error() re-issues the failed write in badblock-sized
chunks, serializing hundreds of guaranteed-to-fail synchronous bios
through raid1d/raid10d per failed write (256 for a 1 MiB write on
4K logical blocks, 2048 on 512e).

- the write-error handler sets WantReplacement, so a hot spare is
pulled in, rebuilt onto, and the healthy member is then evicted by
spare_active() -- and if the spare is also unreachable from the
peer, the cycle consumes the next spare.

- fix_read_error() probes members with kernel pages, starting with
the failing member itself. That probe succeeds (the member is
healthy for host memory), so the routine rewrites nothing, records
nothing, and logs nothing -- but each invocation costs a full
freeze_array() quiesce and a tick of the read-error budget. The
budget exists to evict members whose medium keeps producing
corrected errors; its hourly decay is sized for sporadic medium
errors, while mapping failures are deterministic and arrive at I/O
rate. On an asymmetric PCIe topology a mixed host/P2P read
workload charges 20 errors to the unreachable leg within a second
and kicks a perfectly healthy member.

- On FailFast members both paths short-circuit into md_error(), so a
single unroutable peer-memory I/O evicts a healthy mirror outright.

Track P2PDMA masters with a new r1bio/r10bio state bit, set at
submission where bio_has_data() is still meaningful; a leg's mapping
failure is then "BLK_STS_TARGET and the master carries P2PDMA pages",
cheap to test on every completion path. Handle it explicitly:

- Writes: probe the whole range with one retry and record one bad
range if it also fails. BLK_STS_TARGET is also produced for
device conditions that narrow_write_error()'s retry loop recovers
from (e.g. NVME_SC_NS_NOT_READY and NVME_SC_CMD_INTERRUPTED via
nvme_error_status()), and md cannot tell the two apart from
bi_status alone, so it must not skip the retry outright. A single
whole-range probe observes the outcome instead of predicting it.
WriteErrorSeen is still set (it gates the write-path badblocks
consult), but WantReplacement is not: replacing a member cannot
fix a topology property, and spending a spare on it destroys
redundancy management for real failures.

- Reads: mark the leg IO_BLOCKED so read_balance() picks another
mirror, and skip the freeze/fix cycle and the budget charge. The
same TARGET ambiguity exists here, but a redirected read leaves
nothing to fence, and fix_read_error()'s host-page probe fails
the same way under e.g. NVME_SC_NS_NOT_READY -- charging the
budget would evict a healthy member for a firmware activation
window. A genuinely failing member is still evicted via host
reads, writes and BLK_STS_MEDIUM errors. If no mirror can serve
the read the master bio fails with EIO as before (md completes
masters by Uptodate state, as for any failed mirror I/O), now
without kicking healthy members on the way.

- FailFast: a mapping failure is rejected at map time and never
reaches the wire, so it is no evidence of device unreliability;
don't let it trigger the FailFast md_error() -- the same reasoning
commit f7b24c7b41f2 ("md/raid1,raid10: don't fail devices for
invalid IO errors") applied to BLK_STS_INVAL.

raid10 replacement legs keep their stock policy: badblocks are never
recorded on a replacement, so failing it is the only outcome that
cannot leave a silent hole in a rebuilding replacement.

This belongs in the same release as the BLK_STS_TARGET restoration:
with that fix alone, an unroutable leg costs the retry storms and
the healthy-member evictions above.

Measured on QEMU q35 rigs (one CMB provider, two NVMe members),
8KiB peer-memory I/O, reads x90 under concurrent host reads:

scenario stock error machinery with this patch
asym reads 90/90 ok, healthy far 90/90 ok, no eviction,
leg kicked after ~20 no budget charge
unreach reads all EIO, one healthy all EIO, no members
member kicked kicked
p2p TARGET 16 chunk retries from 1 whole-range probe,
write raid1d, then badblocks same badblocks, master ok

A transient TARGET recovers through the single probe with no
badblocks recorded, and a mapping failure no longer trips FailFast
eviction while a genuine I/O error still does -- both verified on
the same rig.

Fixes: 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Mykola Marzhan <mykola@xxxxxxxxxxx>
---
drivers/md/raid1.c | 56 +++++++++++++++++++++++++++++++-----
drivers/md/raid1.h | 2 ++
drivers/md/raid10.c | 69 ++++++++++++++++++++++++++++++++++++++-------
drivers/md/raid10.h | 2 ++
4 files changed, 112 insertions(+), 17 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index f562b6bd438b..61f635463475 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -483,12 +483,22 @@ static void raid1_end_write_request(struct bio *bio)
* 'one mirror IO has finished' event handler:
*/
if (bio->bi_status && !ignore_error) {
+ /*
+ * A P2PDMA mapping failure reflects the peer/member
+ * pairing, not member health: don't pull in a spare
+ * or trip FailFast for it.
+ */
+ bool p2pdma_unmappable = bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
+
set_bit(WriteErrorSeen, &rdev->flags);
- if (!test_and_set_bit(WantReplacement, &rdev->flags))
+ if (!p2pdma_unmappable &&
+ !test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &
conf->mddev->recovery);

- if (test_bit(FailFast, &rdev->flags) &&
+ if (!p2pdma_unmappable &&
+ test_bit(FailFast, &rdev->flags) &&
(bio->bi_opf & MD_FAILFAST) &&
/* We never try FailFast to WriteMostly devices */
!test_bit(WriteMostly, &rdev->flags)) {
@@ -1378,6 +1388,8 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
else
init_r1bio(r1_bio, mddev, bio);
r1_bio->sectors = max_read_sectors;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R1BIO_P2PDMA, &r1_bio->state);

/*
* make_request() can abort the operation when read-ahead is being
@@ -1557,6 +1569,8 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,

r1_bio = alloc_r1bio(mddev, bio);
r1_bio->sectors = max_sectors;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R1BIO_P2PDMA, &r1_bio->state);

/* first select target devices under rcu_lock and
* inc refcount on their rdev. Record them by setting
@@ -2525,7 +2539,7 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio)
}
}

-static void narrow_write_error(struct r1bio *r1_bio, int i)
+static void narrow_write_error(struct r1bio *r1_bio, int i, bool coarse)
{
struct mddev *mddev = r1_bio->mddev;
struct r1conf *conf = mddev->private;
@@ -2539,6 +2553,11 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
* It is conceivable that the bio doesn't exactly align with
* blocks. We must handle this somehow.
*
+ * With 'coarse', retry the whole range as one bio and record
+ * one bad range if it fails: for P2PDMA mapping failures,
+ * which fail every block identically, while the single retry
+ * still lets a cleared transient error recover.
+ *
* We currently own a reference on the rdev.
*/

@@ -2553,9 +2572,12 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
block_sectors = roundup(1 << rdev->badblocks.shift, lbs);

sector = r1_bio->sector;
- sectors = ((sector + block_sectors)
- & ~(sector_t)(block_sectors - 1))
- - sector;
+ if (coarse)
+ sectors = sect_to_write;
+ else
+ sectors = ((sector + block_sectors)
+ & ~(sector_t)(block_sectors - 1))
+ - sector;

while (sect_to_write) {
struct bio *wbio;
@@ -2636,8 +2658,18 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
* narrow down and record precise write
* errors.
*/
+ bool coarse = r1_bio->bios[m]->bi_status ==
+ BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
+
fail = true;
- narrow_write_error(r1_bio, m);
+ /*
+ * A P2PDMA mapping failure fails the whole range
+ * identically: probe it once (coarse) instead of
+ * narrowing block by block. A transient TARGET
+ * recovers via the probe with nothing recorded.
+ */
+ narrow_write_error(r1_bio, m, coarse);
rdev_dec_pending(conf->mirrors[m].rdev,
conf->mddev);
}
@@ -2664,6 +2696,9 @@ static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
{
struct md_rdev *rdev = conf->mirrors[r1_bio->read_disk].rdev;
struct bio *bio = r1_bio->bios[r1_bio->read_disk];
+ /* evaluate before the bio_put() below */
+ bool p2pdma_error = bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
struct mddev *mddev = conf->mddev;
sector_t sector;

@@ -2683,6 +2718,13 @@ static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
*/
if (mddev->ro) {
r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED;
+ } else if (p2pdma_error) {
+ /*
+ * The peer cannot reach this member; nothing on the
+ * medium to fix. Skip the read-error budget and
+ * FailFast, just keep this leg out of the retry.
+ */
+ r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED;
} else if (test_bit(FailFast, &rdev->flags)) {
md_error(mddev, rdev);
} else {
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index c98d43a7ae99..61b788a99d14 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -184,6 +184,8 @@ enum r1bio_state {
R1BIO_MadeGood,
R1BIO_WriteError,
R1BIO_FailFast,
+/* the master bio carries PCI P2PDMA (peer device memory) pages */
+ R1BIO_P2PDMA,
};

static inline int sector_to_idx(sector_t sector)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index f7ef903a3d4e..144d07b6029c 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -482,13 +482,24 @@ static void raid10_end_write_request(struct bio *bio)
*/
md_error(rdev->mddev, rdev);
else {
+ /*
+ * A P2PDMA mapping failure reflects the
+ * peer/member pairing, not member health: don't
+ * pull in a spare or trip FailFast for it.
+ */
+ bool p2pdma_unmappable =
+ bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R10BIO_P2PDMA, &r10_bio->state);
+
set_bit(WriteErrorSeen, &rdev->flags);
- if (!test_and_set_bit(WantReplacement, &rdev->flags))
+ if (!p2pdma_unmappable &&
+ !test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);

dec_rdev = 0;
- if (test_bit(FailFast, &rdev->flags) &&
+ if (!p2pdma_unmappable &&
+ test_bit(FailFast, &rdev->flags) &&
(bio->bi_opf & MD_FAILFAST)) {
md_error(rdev->mddev, rdev);
}
@@ -1170,6 +1181,9 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
*/
gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;

+ if (md_bio_is_p2pdma(bio))
+ set_bit(R10BIO_P2PDMA, &r10_bio->state);
+
if (slot >= 0 && r10_bio->devs[slot].rdev) {
/*
* This is an error retry, but we cannot
@@ -1357,6 +1371,9 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
sector_t sectors;
int max_sectors;

+ if (md_bio_is_p2pdma(bio))
+ set_bit(R10BIO_P2PDMA, &r10_bio->state);
+
if ((mddev_is_clustered(mddev) &&
mddev->cluster_ops->area_resyncing(mddev, WRITE,
bio->bi_iter.bi_sector,
@@ -2786,7 +2803,7 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
}
}

-static void narrow_write_error(struct r10bio *r10_bio, int i)
+static void narrow_write_error(struct r10bio *r10_bio, int i, bool coarse)
{
struct bio *bio = r10_bio->master_bio;
struct mddev *mddev = r10_bio->mddev;
@@ -2800,6 +2817,11 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
* It is conceivable that the bio doesn't exactly align with
* blocks. We must handle this.
*
+ * With 'coarse', retry the whole range as one bio and record
+ * one bad range if it fails: for P2PDMA mapping failures,
+ * which fail every block identically, while the single retry
+ * still lets a cleared transient error recover.
+ *
* We currently own a reference to the rdev.
*/

@@ -2814,9 +2836,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;
@@ -2856,6 +2881,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 +2894,28 @@ 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) {
+ /*
+ * The peer cannot reach this member; nothing on the
+ * medium to fix. Skip the read-error budget and
+ * FailFast, just keep this leg out of the retry.
+ */
+ 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;
@@ -2947,8 +2984,20 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
r10_bio->sectors, 0);
rdev_dec_pending(rdev, conf->mddev);
} else if (bio != NULL && bio->bi_status) {
+ bool coarse = bio->bi_status ==
+ BLK_STS_TARGET &&
+ test_bit(R10BIO_P2PDMA,
+ &r10_bio->state);
+
fail = true;
- narrow_write_error(r10_bio, m);
+ /*
+ * A P2PDMA mapping failure fails the whole
+ * range identically: probe it once (coarse)
+ * instead of narrowing block by block. A
+ * transient TARGET recovers via the probe
+ * with nothing recorded.
+ */
+ narrow_write_error(r10_bio, m, coarse);
rdev_dec_pending(rdev, conf->mddev);
}
bio = r10_bio->devs[m].repl_bio;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index ec79d87fb92f..a2e1554f77db 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -174,6 +174,8 @@ enum r10bio_state {
R10BIO_Previous,
/* failfast devices did receive failfast requests. */
R10BIO_FailFast,
+/* the master bio carries PCI P2PDMA (peer device memory) pages */
+ R10BIO_P2PDMA,
R10BIO_Discard,
};
#endif
--
2.43.0