* [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers
@ 2026-07-23 20:41 Mykola Marzhan
2026-07-23 20:41 ` [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
` (9 more replies)
0 siblings, 10 replies; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:41 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
md treats an unsupported P2PDMA transfer to a member as success, and
nvme-rdma retries one forever. Whether peer memory can be DMA-mapped
depends on the PCIe topology between the two devices, and since v6.17
the failure completes as BLK_STS_INVAL: retryable for multipath and
deliberately ignored by md for member failures, so mirrors silently
diverge.
v1-v3 restored BLK_STS_TARGET for these failures and tagged P2PDMA
bios in md. Logan Gunthorpe proposed a dedicated BLK_STS_P2PDMA
instead, so no consumer needs to know whether an I/O was P2P, and
wrote the core patches; v5 is built on them. Patches 1 and 5 are
his and keep his authorship.
1 block: add BLK_STS_P2PDMA (stable, v6.17)
2 md: ensure REQ_NOMERGE is set on P2PDMA bios
3 md/raid1: serialize non-write-behind writes on CollisionCheck
rdevs (pre-existing bug patch 4 would widen; stable)
4 md/raid1: no write-behind for P2PDMA bios
5 md/raid1,raid10: factor out raid1_write_error() helper
6 md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error()
retry clones
7 md/raid1,raid10: skip futile retries on P2PDMA mapping
failures
8 md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
9 nvme-rdma: use ib_dma_map_sgtable_attrs() (stable, v7.1;
dependency of patch 10)
10 nvme-rdma: return BLK_STS_P2PDMA (stable, v7.1; needs patches
1 and 9)
Routing: patch 1 block, 2-8 md, 9-10 nvme; 7, 8 and 10 use the new
status. Patch 1 alone stops the divergence and the dm-multipath
requeue loop; the nvme-rdma loop also needs 9-10; everything else
refines the fallout, so the series bisects safely. The md patches
fix a v7.2-rc1 regression (02666132403a), except 3 (older bug,
stable) and 5 (refactor).
Tested with the QEMU raid1/raid10 error matrix and a ConnectX-4 Lx.
After patch 1, block-layer error injection accepts status=P2PDMA, so
the md completion paths can be exercised on any member with no P2P
hardware. The patches were developed with AI assistance (see the
Assisted-by trailers); all code was human-reviewed and tested.
Against v7.2-rc4 (Logan's base).
Changes in v5:
- patches 7, 8: From: is now Mykola (Logan's request)
Logan keeps Signed-off-by on both, Co-developed-by on 8
- patch 8: raid10 declares bio up front and bi_status sits in a
blk_status_t local in both files.
- old patch 9 split: 9 is the mechanical sgtable conversion,
10 the -EREMOTEIO -> BLK_STS_P2PDMA translation; both stable
since 10 needs 9
Changes in v4:
- new BLK_STS_P2PDMA (Logan) replaces restoring BLK_STS_TARGET; the
R1BIO_P2PDMA/R10BIO_P2PDMA state bits are gone and the md changes
shrink to completion-time status tests
- blk_dma_map_iter_start() gets the explicit
PCI_P2PDMA_MAP_NOT_SUPPORTED case (Logan's patch; v3 kept
default: only)
- write completions record the whole failed range as bad blocks
instead of narrow_write_error()'s per-block retries; v3's coarse
retry is gone -- the new status is deterministic, nothing to
re-attempt
- read path: bi_status is evaluated before bio_put()
Link: https://lore.kernel.org/linux-raid/20260718162547.448892-1-mykola@meshstor.io/ [v1]
Link: https://lore.kernel.org/linux-raid/20260719105327.864949-1-mykola@meshstor.io/ [v2]
Link: https://lore.kernel.org/linux-raid/20260721174502.111503-1-mykola@meshstor.io/ [v3]
Link: https://lore.kernel.org/linux-raid/20260722185841.449934-1-mykola@meshstor.io/ [v4]
Link: https://lore.kernel.org/linux-raid/9f846c85-f427-433a-b87d-8e9e3a4406e7@deltatee.com/ [Logan's proposal]
Logan Gunthorpe (2):
block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
md/raid1,raid10: factor out raid1_write_error() helper
Mykola Marzhan (8):
md: ensure REQ_NOMERGE is set on P2PDMA bios
md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
md/raid1: don't use write-behind for P2PDMA bios
md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
md/raid1,raid10: skip futile retries on P2PDMA mapping failures
md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
nvme-rdma: use ib_dma_map_sgtable_attrs()
nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
block/blk-core.c | 3 +++
block/blk-mq-dma.c | 3 ++-
drivers/md/md.c | 10 +++++++--
drivers/md/md.h | 15 +++++++++++++
drivers/md/raid1-10.c | 16 +++++++++++++
drivers/md/raid1.c | 47 +++++++++++++++++++++++----------------
drivers/md/raid10.c | 27 +++++++++++-----------
drivers/nvme/host/rdma.c | 38 ++++++++++++++++++-------------
include/linux/blk_types.h | 10 +++++++++
9 files changed, 118 insertions(+), 51 deletions(-)
base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f
--
2.52.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
@ 2026-07-23 20:41 ` Mykola Marzhan
2026-07-23 20:51 ` sashiko-bot
2026-07-23 20:41 ` [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
` (8 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:41 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
From: Logan Gunthorpe <logang@deltatee.com>
blk_dma_map_iter_start() reports BLK_STS_INVAL when a P2PDMA transfer
is attempted between two devices whose PCIe topology cannot route it.
blk_path_error() treats INVAL as retryable, so multipath requeues the
I/O forever, and callers cannot tell it apart from an invalid request.
md also ignores it for member failures, so a mirror silently diverges.
Restoring the old BLK_STS_TARGET would stay ambiguous -- nvme maps
NS_NOT_READY, LBA_RANGE and CMD_INTERRUPTED to it too. Add a dedicated
BLK_STS_P2PDMA, non-retryable in blk_path_error(), and return it from
blk_dma_map_iter_start().
Fixes: 858299dc6160 ("block: add scatterlist-less DMA mapping helpers")
Fixes: 7ce3c1dd78fc ("nvme-pci: convert the data mapping to blk_rq_dma_map")
Cc: stable@vger.kernel.org # v6.17
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
block/blk-core.c | 3 +++
block/blk-mq-dma.c | 3 ++-
include/linux/blk_types.h | 10 ++++++++++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 365641266c9e..83cb5464265f 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -177,6 +177,9 @@ static const struct {
/* Command duration limit device-side timeout */
ENT(DURATION_LIMIT, -ETIME, "duration limit exceeded"),
+
+ /* no PCIe P2PDMA route between initiator and target */
+ ENT(P2PDMA, -EREMOTEIO, "peer-to-peer transfer unroutable"),
ENT(INVAL, -EINVAL, "invalid"),
/* everything else not covered above: */
diff --git a/block/blk-mq-dma.c b/block/blk-mq-dma.c
index bfdb9ed70741..e88d6b3524e6 100644
--- a/block/blk-mq-dma.c
+++ b/block/blk-mq-dma.c
@@ -189,8 +189,9 @@ static bool blk_dma_map_iter_start(struct request *req, struct device *dma_dev,
*/
case PCI_P2PDMA_MAP_NONE:
break;
+ case PCI_P2PDMA_MAP_NOT_SUPPORTED:
default:
- iter->status = BLK_STS_INVAL;
+ iter->status = BLK_STS_P2PDMA;
return false;
}
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 8808ee76e73c..c8de7809320a 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -165,6 +165,15 @@ typedef u16 blk_short_t;
*/
#define BLK_STS_DURATION_LIMIT ((__force blk_status_t)17)
+/*
+ * BLK_STS_P2PDMA is returned when a peer-to-peer DMA transfer cannot be
+ * mapped (typically because there is no route between the initiator and the
+ * target that the platform's PCIe topology and ACS settings allow). This
+ * is a property of the initiator/target pair, not of the target device's
+ * health. This error should never be retried.
+ */
+#define BLK_STS_P2PDMA ((__force blk_status_t)18)
+
/*
* Invalid size or alignment.
*/
@@ -188,6 +197,7 @@ static inline bool blk_path_error(blk_status_t error)
case BLK_STS_NOTSUPP:
case BLK_STS_NOSPC:
case BLK_STS_TARGET:
+ case BLK_STS_P2PDMA:
case BLK_STS_RESV_CONFLICT:
case BLK_STS_MEDIUM:
case BLK_STS_PROTECTION:
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
2026-07-23 20:41 ` [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
@ 2026-07-23 20:41 ` Mykola Marzhan
2026-07-23 20:51 ` sashiko-bot
2026-07-23 20:41 ` [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
` (7 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:41 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
md_submit_bio() strips REQ_NOMERGE from every bio. For P2PDMA bios
the flag is what keeps requests single-provider (see
__bio_add_page()): merging a P2PDMA bio with one over a different
pgmap, or over host memory, maps the merged request with the wrong
bus address. Set the flag on P2PDMA bios -- bios built via
bio_iov_bvec_set() arrive without it -- and keep stripping it
otherwise.
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@meshstor.io>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
drivers/md/md.c | 10 ++++++++--
drivers/md/md.h | 15 +++++++++++++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d1465bcd86c8..3770bdb4d4b2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -451,8 +451,14 @@ static void md_submit_bio(struct bio *bio)
return;
}
- /* bio could be mergeable after passing to underlayer */
- bio->bi_opf &= ~REQ_NOMERGE;
+ /*
+ * A bio md split may merge again below md -- except P2PDMA bios,
+ * which must stay single-provider (see __bio_add_page()).
+ */
+ if (md_bio_is_p2pdma(bio))
+ bio->bi_opf |= REQ_NOMERGE;
+ else
+ bio->bi_opf &= ~REQ_NOMERGE;
md_handle_request(mddev, bio);
}
diff --git a/drivers/md/md.h b/drivers/md/md.h
index d8daf0f75cbb..73df1a6eccd8 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -11,8 +11,10 @@
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/badblocks.h>
+#include <linux/bio.h>
#include <linux/kobject.h>
#include <linux/list.h>
+#include <linux/memremap.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/timer.h>
@@ -22,6 +24,19 @@
#include <trace/events/block.h>
#define MaxSector (~(sector_t)0)
+
+/*
+ * P2P and host pages never mix within a bio, so the first bvec is
+ * representative. Read bi_io_vec directly: bio_first_bvec_all()
+ * WARNs on the split clones md handles, and data-less bios have no
+ * bi_io_vec. Not valid after the bio's iterator is consumed.
+ */
+static inline bool md_bio_is_p2pdma(struct bio *bio)
+{
+ return bio_has_data(bio) && bio->bi_io_vec &&
+ is_pci_p2pdma_page(bio->bi_io_vec->bv_page);
+}
+
/*
* Number of guaranteed raid bios in case of extreme VM load:
*/
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
2026-07-23 20:41 ` [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
2026-07-23 20:41 ` [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-23 20:41 ` Mykola Marzhan
2026-07-23 21:04 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
` (6 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:41 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
A write that skips write-behind (full behind queue, a waiting
reader, failed behind-bio allocation) races the in-flight behind
write it overlaps: nothing orders the two on the write-mostly
member, and if the older behind data lands last the member keeps
stale data for sectors already acknowledged as rewritten.
Serialize on CollisionCheck, which marks exactly the rdevs owning a
serial tree: serialize_policy rdevs plus write-mostly members when
write-behind arms serialization -- the case the MD_SERIALIZE_POLICY
test misses. remove_serial() under the same condition. This also
avoids the old gate's latent NULL deref for rdevs hot-added under
serialize_policy (they never get a serial tree).
Fixes: 69df9cfc7042 ("raid1: serialize the overlap write")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/md/raid1.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index afe2ca96ad8c..997f79c05db7 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -564,7 +564,7 @@ static void raid1_end_write_request(struct bio *bio)
call_bio_endio(r1_bio);
}
}
- } else if (test_bit(MD_SERIALIZE_POLICY, &rdev->mddev->flags))
+ } else if (test_bit(CollisionCheck, &rdev->flags))
remove_serial(rdev, lo, hi);
if (r1_bio->bios[mirror] == NULL)
rdev_dec_pending(rdev, conf->mddev);
@@ -1677,7 +1677,11 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO,
&mddev->bio_set);
- if (test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
+ /*
+ * CollisionCheck marks every rdev with a serial
+ * tree; order against in-flight write-behind I/O.
+ */
+ if (test_bit(CollisionCheck, &rdev->flags))
wait_for_serialization(rdev, r1_bio);
}
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (2 preceding siblings ...)
2026-07-23 20:41 ` [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:01 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
` (5 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
alloc_behind_master_bio() copies the bio's data with bio_copy_data(),
a CPU copy. P2PDMA pages are peer device (BAR) memory; generic code
must not assume CPU load/store access to them is safe or fast on
every architecture, and bouncing peer memory through the CPU defeats
the point of a peer-to-peer transfer.
Skip write-behind for P2PDMA bios: they are written directly to all
members, including write-mostly ones. Ordering against write-behind
I/O in flight to overlapping sectors is preserved: the non-behind
clone path serializes on CollisionCheck rdevs (see the preceding
fix), which covers these bios like any other write that bypasses
write-behind.
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@meshstor.io>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
drivers/md/raid1.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 997f79c05db7..7d769b6460ca 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1523,6 +1523,7 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
bool write_behind = false;
bool nowait = bio->bi_opf & REQ_NOWAIT;
bool is_discard = op_is_discard(bio->bi_opf);
+ bool is_p2pdma = md_bio_is_p2pdma(bio);
sector_t sector = bio->bi_iter.bi_sector;
if (mddev_is_clustered(mddev) &&
@@ -1575,9 +1576,12 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
/*
* The write-behind io is only attempted on drives marked as
* write-mostly, which means we could allocate write behind
- * bio later.
+ * bio later. P2PDMA bios are excluded: write-behind copies
+ * the data with bio_copy_data(), a CPU copy that cannot be
+ * assumed safe or fast on P2PDMA (device BAR) pages.
*/
- if (!is_discard && rdev && test_bit(WriteMostly, &rdev->flags))
+ if (!is_discard && !is_p2pdma && rdev &&
+ test_bit(WriteMostly, &rdev->flags))
write_behind = true;
r1_bio->bios[i] = NULL;
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (3 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 20:54 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
` (4 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
From: Logan Gunthorpe <logang@deltatee.com>
Factor out a common raid1_write_error() helper from raid1.c and
raid10.c.
This will help reduce code duplication in subsequent patches.
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/md/raid1-10.c | 13 +++++++++++++
drivers/md/raid1.c | 13 ++-----------
drivers/md/raid10.c | 10 +---------
3 files changed, 16 insertions(+), 20 deletions(-)
diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index 56a56a4da4f8..cee21452e066 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -303,3 +303,16 @@ static inline bool raid1_should_handle_error(struct bio *bio)
return !(bio->bi_opf & (REQ_RAHEAD | REQ_NOWAIT)) &&
bio->bi_status != BLK_STS_INVAL;
}
+
+static inline void raid1_write_error(struct mddev *mddev, struct md_rdev *rdev,
+ struct bio *bio, bool allow_failfast)
+{
+ set_bit(WriteErrorSeen, &rdev->flags);
+
+ if (!test_and_set_bit(WantReplacement, &rdev->flags))
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+
+ if (allow_failfast && test_bit(FailFast, &rdev->flags) &&
+ (bio->bi_opf & MD_FAILFAST))
+ md_error(mddev, rdev);
+}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 7d769b6460ca..f07edeac6e5a 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -483,17 +483,8 @@ static void raid1_end_write_request(struct bio *bio)
* 'one mirror IO has finished' event handler:
*/
if (bio->bi_status && !ignore_error) {
- set_bit(WriteErrorSeen, &rdev->flags);
- if (!test_and_set_bit(WantReplacement, &rdev->flags))
- set_bit(MD_RECOVERY_NEEDED, &
- conf->mddev->recovery);
-
- if (test_bit(FailFast, &rdev->flags) &&
- (bio->bi_opf & MD_FAILFAST) &&
- /* We never try FailFast to WriteMostly devices */
- !test_bit(WriteMostly, &rdev->flags)) {
- md_error(r1_bio->mddev, rdev);
- }
+ raid1_write_error(conf->mddev, rdev, bio,
+ !test_bit(WriteMostly, &rdev->flags));
/*
* When the device is faulty, it is not necessary to
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 0a3cfdd3f5df..5b63175fb382 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -482,16 +482,8 @@ static void raid10_end_write_request(struct bio *bio)
*/
md_error(rdev->mddev, rdev);
else {
- set_bit(WriteErrorSeen, &rdev->flags);
- if (!test_and_set_bit(WantReplacement, &rdev->flags))
- set_bit(MD_RECOVERY_NEEDED,
- &rdev->mddev->recovery);
-
dec_rdev = 0;
- if (test_bit(FailFast, &rdev->flags) &&
- (bio->bi_opf & MD_FAILFAST)) {
- md_error(rdev->mddev, rdev);
- }
+ raid1_write_error(rdev->mddev, rdev, bio, true);
/*
* When the device is faulty, it is not necessary to
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (4 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:05 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
` (3 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
narrow_write_error() re-issues a failed write in badblock-granularity
chunks, cloning from the master bio and resetting bi_opf to a bare
REQ_OP_WRITE. For a P2PDMA bio that reset drops REQ_NOMERGE, which is
the only request-level protection against the member queue merging
P2PDMA segments across pgmaps or with host memory (see the preceding
md_submit_bio() fix): the retry path would quietly reopen the hole
the submission path closes. Restore the flag on P2PDMA retry clones.
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@meshstor.io>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
drivers/md/raid1.c | 3 +++
drivers/md/raid10.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index f07edeac6e5a..19c4dec450de 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2561,6 +2561,9 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
}
wbio->bi_opf = REQ_OP_WRITE;
+ /* Keep P2PDMA retry bios unmergeable, like the original */
+ if (md_bio_is_p2pdma(wbio))
+ wbio->bi_opf |= REQ_NOMERGE;
wbio->bi_iter.bi_sector = r1_bio->sector;
wbio->bi_iter.bi_size = r1_bio->sectors << 9;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 5b63175fb382..428bddddfe71 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2823,6 +2823,9 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
wbio->bi_iter.bi_sector = wsector +
choose_data_offset(r10_bio, rdev);
wbio->bi_opf = REQ_OP_WRITE;
+ /* Keep P2PDMA retry bios unmergeable, like the original */
+ if (md_bio_is_p2pdma(wbio))
+ wbio->bi_opf |= REQ_NOMERGE;
if (submit_bio_wait(wbio) &&
!rdev_set_badblocks(rdev, wsector, sectors, 0)) {
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (5 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:09 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
` (2 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
BLK_STS_P2PDMA reflects the peer/member pairing, not member health:
retrying the same pages on the same member cannot succeed. Keep
WriteErrorSeen so the range stays fenced, but skip WantReplacement and
FailFast eviction, as commit f7b24c7b41f2 ("md/raid1,raid10: don't fail
devices for invalid IO errors") did for BLK_STS_INVAL.
The failure is whole-range and deterministic, so narrow_write_error()'s
block-by-block submit_bio_wait() retries are pointless. Record the
whole range as a bad block in the completion handler instead.
Fixes: 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/md/raid1-10.c | 3 +++
drivers/md/raid1.c | 11 ++++++++---
drivers/md/raid10.c | 7 ++++++-
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c
index cee21452e066..89daf8558251 100644
--- a/drivers/md/raid1-10.c
+++ b/drivers/md/raid1-10.c
@@ -309,6 +309,9 @@ static inline void raid1_write_error(struct mddev *mddev, struct md_rdev *rdev,
{
set_bit(WriteErrorSeen, &rdev->flags);
+ if (bio->bi_status == BLK_STS_P2PDMA)
+ return;
+
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 19c4dec450de..83ebf40237d5 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2624,10 +2624,15 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
* narrow down and record precise write
* errors.
*/
+ struct md_rdev *rdev = conf->mirrors[m].rdev;
+
fail = true;
- narrow_write_error(r1_bio, m);
- rdev_dec_pending(conf->mirrors[m].rdev,
- conf->mddev);
+ if (r1_bio->bios[m]->bi_status == BLK_STS_P2PDMA)
+ rdev_set_badblocks(rdev, r1_bio->sector,
+ r1_bio->sectors, 0);
+ else
+ narrow_write_error(r1_bio, m);
+ rdev_dec_pending(rdev, conf->mddev);
}
if (fail) {
spin_lock_irq(&conf->device_lock);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 428bddddfe71..77c7d8a59916 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2940,7 +2940,12 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
rdev_dec_pending(rdev, conf->mddev);
} else if (bio != NULL && bio->bi_status) {
fail = true;
- narrow_write_error(r10_bio, m);
+ if (bio->bi_status == BLK_STS_P2PDMA)
+ rdev_set_badblocks(rdev,
+ r10_bio->devs[m].addr,
+ r10_bio->sectors, 0);
+ else
+ narrow_write_error(r10_bio, m);
rdev_dec_pending(rdev, conf->mddev);
}
bio = r10_bio->devs[m].repl_bio;
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (6 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:13 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
2026-07-23 20:42 ` [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
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 <logang@deltatee.com>
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/md/raid1.c | 4 +++-
drivers/md/raid10.c | 7 ++++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 83ebf40237d5..16a47568679f 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2657,6 +2657,8 @@ 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 */
+ blk_status_t status = bio->bi_status;
struct mddev *mddev = conf->mddev;
sector_t sector;
@@ -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;
} else if (test_bit(FailFast, &rdev->flags)) {
md_error(mddev, rdev);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 77c7d8a59916..462f622556ed 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2847,7 +2847,9 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
{
int slot = r10_bio->read_slot;
- struct bio *bio;
+ struct bio *bio = r10_bio->devs[slot].bio;
+ /* evaluate before the bio_put() below */
+ blk_status_t status = bio->bi_status;
struct r10conf *conf = mddev->private;
struct md_rdev *rdev = r10_bio->devs[slot].rdev;
@@ -2859,11 +2861,10 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
* This is all done synchronously while the array is
* frozen.
*/
- bio = r10_bio->devs[slot].bio;
bio_put(bio);
r10_bio->devs[slot].bio = NULL;
- if (mddev->ro)
+ if (mddev->ro || status == BLK_STS_P2PDMA)
r10_bio->devs[slot].bio = IO_BLOCKED;
else if (!test_bit(FailFast, &rdev->flags)) {
freeze_array(conf, 1);
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs()
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (7 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:09 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
ib_dma_map_sg() folds every mapping error into a zero return, losing
the DMA layer's error code. Switch both scatterlist mappings to
ib_dma_map_sgtable_attrs(), which preserves it for the next patch to
act on. No behavior change: failures are still reported as -EIO.
A local sg_table is needed: dma_map_sgtable() maps orig_nents
entries, and the embedded table's orig_nents is the allocated count,
not the count blk_rq_map_sg() produced. rdma_rw_ctx_init() does the
same.
Assisted-by: Claude:claude-fable-5
Cc: stable@vger.kernel.org # v7.1
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/nvme/host/rdma.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 6909e3542794..63830334c73e 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1469,6 +1469,7 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq,
int *count, int *pi_count)
{
struct nvme_rdma_request *req = blk_mq_rq_to_pdu(rq);
+ struct sg_table sgt;
int ret;
req->data_sgl.sg_table.sgl = (struct scatterlist *)(req + 1);
@@ -1480,12 +1481,16 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq,
req->data_sgl.nents = blk_rq_map_sg(rq, req->data_sgl.sg_table.sgl);
- *count = ib_dma_map_sg(ibdev, req->data_sgl.sg_table.sgl,
- req->data_sgl.nents, rq_dma_dir(rq));
- if (unlikely(*count <= 0)) {
+ sgt = (struct sg_table) {
+ .sgl = req->data_sgl.sg_table.sgl,
+ .orig_nents = req->data_sgl.nents,
+ };
+ ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, rq_dma_dir(rq), 0);
+ if (unlikely(ret)) {
ret = -EIO;
goto out_free_table;
}
+ *count = sgt.nents;
if (blk_integrity_rq(rq)) {
req->metadata_sgl->sg_table.sgl =
@@ -1501,14 +1506,16 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq,
req->metadata_sgl->nents = blk_rq_map_integrity_sg(rq,
req->metadata_sgl->sg_table.sgl);
- *pi_count = ib_dma_map_sg(ibdev,
- req->metadata_sgl->sg_table.sgl,
- req->metadata_sgl->nents,
- rq_dma_dir(rq));
- if (unlikely(*pi_count <= 0)) {
+ sgt = (struct sg_table) {
+ .sgl = req->metadata_sgl->sg_table.sgl,
+ .orig_nents = req->metadata_sgl->nents,
+ };
+ ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, rq_dma_dir(rq), 0);
+ if (unlikely(ret)) {
ret = -EIO;
goto out_free_pi_table;
}
+ *pi_count = sgt.nents;
}
return 0;
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
` (8 preceding siblings ...)
2026-07-23 20:42 ` [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
@ 2026-07-23 20:42 ` Mykola Marzhan
2026-07-23 21:19 ` sashiko-bot
9 siblings, 1 reply; 21+ messages in thread
From: Mykola Marzhan @ 2026-07-23 20:42 UTC (permalink / raw)
To: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme
Cc: Li Nan, Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
Henrique Carvalho, stable, linux-kernel, linux-rdma, linux-pci
A P2P transfer the PCIe topology cannot route fails DMA mapping
with -EREMOTEIO. nvme-rdma folds every mapping error into -EIO, a
retryable host-path error: multipath requeues the I/O forever, a
single path burns its whole retry budget.
Propagate the real error code and return the unroutable case as the
non-retryable BLK_STS_P2PDMA. As in nvme-pci, -ENOMEM now requeues
(BLK_STS_RESOURCE) and -EINVAL fails (BLK_STS_IOERR); -EIO stays a
retryable host-path error. While at it, start the request only
after mapping succeeds -- nvme-pci's order -- and ratelimit the
map-failure message.
Fixes: 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA transport")
Cc: stable@vger.kernel.org # v7.1: requires "nvme-rdma: use ib_dma_map_sgtable_attrs()"
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
drivers/nvme/host/rdma.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 63830334c73e..2f50509a7a61 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1486,10 +1486,8 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq,
.orig_nents = req->data_sgl.nents,
};
ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, rq_dma_dir(rq), 0);
- if (unlikely(ret)) {
- ret = -EIO;
+ if (unlikely(ret))
goto out_free_table;
- }
*count = sgt.nents;
if (blk_integrity_rq(rq)) {
@@ -1511,10 +1509,8 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq,
.orig_nents = req->metadata_sgl->nents,
};
ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, rq_dma_dir(rq), 0);
- if (unlikely(ret)) {
- ret = -EIO;
+ if (unlikely(ret))
goto out_free_pi_table;
- }
*pi_count = sgt.nents;
}
@@ -2033,8 +2029,6 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
if (ret)
goto unmap_qe;
- nvme_start_request(rq);
-
if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) &&
queue->pi_support &&
(c->common.opcode == nvme_cmd_write ||
@@ -2046,11 +2040,13 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
err = nvme_rdma_map_data(queue, rq, c);
if (unlikely(err < 0)) {
- dev_err(queue->ctrl->ctrl.device,
- "Failed to map data (%d)\n", err);
+ dev_err_ratelimited(queue->ctrl->ctrl.device,
+ "Failed to map data (%d)\n", err);
goto err;
}
+ nvme_start_request(rq);
+
sqe->cqe.done = nvme_rdma_send_done;
ib_dma_sync_single_for_device(dev, sqe->dma,
@@ -2070,6 +2066,9 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
ret = nvme_host_path_error(rq);
else if (err == -ENOMEM || err == -EAGAIN)
ret = BLK_STS_RESOURCE;
+ /* Peer memory unreachable from this device: don't retry. */
+ else if (err == -EREMOTEIO)
+ ret = BLK_STS_P2PDMA;
else
ret = BLK_STS_IOERR;
nvme_cleanup_cmd(rq);
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
2026-07-23 20:41 ` [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
@ 2026-07-23 20:51 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 20:51 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: yukuai, linux-pci, linux-raid
> From: Logan Gunthorpe <logang@deltatee.com>
>
> blk_dma_map_iter_start() reports BLK_STS_INVAL when a P2PDMA transfer
> is attempted between two devices whose PCIe topology cannot route it.
> blk_path_error() treats INVAL as retryable, so multipath requeues the
> I/O forever, and callers cannot tell it apart from an invalid request.
> md also ignores it for member failures, so a mirror silently diverges.
>
> Restoring the old BLK_STS_TARGET would stay ambiguous -- nvme maps
> NS_NOT_READY, LBA_RANGE and CMD_INTERRUPTED to it too. Add a dedicated
> BLK_STS_P2PDMA, non-retryable in blk_path_error(), and return it from
> blk_dma_map_iter_start().
>
> Fixes: 858299dc6160 ("block: add scatterlist-less DMA mapping helpers")
> Fixes: 7ce3c1dd78fc ("nvme-pci: convert the data mapping to blk_rq_dma_map")
> Cc: stable@vger.kernel.org # v6.17
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios
2026-07-23 20:41 ` [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-23 20:51 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 20:51 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, linux-pci, yukuai
> md_submit_bio() strips REQ_NOMERGE from every bio. For P2PDMA bios
> the flag is what keeps requests single-provider (see
> __bio_add_page()): merging a P2PDMA bio with one over a different
> pgmap, or over host memory, maps the merged request with the wrong
> bus address. Set the flag on P2PDMA bios -- bios built via
> bio_iov_bvec_set() arrive without it -- and keep stripping it
> otherwise.
>
> 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@meshstor.io>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper
2026-07-23 20:42 ` [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
@ 2026-07-23 20:54 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 20:54 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-pci, linux-raid, yukuai
> From: Logan Gunthorpe <logang@deltatee.com>
>
> Factor out a common raid1_write_error() helper from raid1.c and
> raid10.c.
>
> This will help reduce code duplication in subsequent patches.
>
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=5
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios
2026-07-23 20:42 ` [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
@ 2026-07-23 21:01 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:01 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-pci, linux-raid, yukuai
> alloc_behind_master_bio() copies the bio's data with bio_copy_data(),
> a CPU copy. P2PDMA pages are peer device (BAR) memory; generic code
> must not assume CPU load/store access to them is safe or fast on
> every architecture, and bouncing peer memory through the CPU defeats
> the point of a peer-to-peer transfer.
>
> Skip write-behind for P2PDMA bios: they are written directly to all
> members, including write-mostly ones. Ordering against write-behind
> I/O in flight to overlapping sectors is preserved: the non-behind
> clone path serializes on CollisionCheck rdevs (see the preceding
> fix), which covers these bios like any other write that bypasses
> write-behind.
>
> 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@meshstor.io>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
2026-07-23 20:41 ` [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
@ 2026-07-23 21:04 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:04 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: yukuai, linux-pci, linux-raid
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Hot-added devices silently bypass the array's `MD_SERIALIZE_POLICY`, leaving them vulnerable to overlap write data corruption.
Pre-existing issues:
- [High] Dynamic evaluation of `CollisionCheck` during write-behind I/O completion races with sysfs state changes, leading to use-after-free and memory leaks.
--
--- Patch [3]: [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs ---
commit b12be35453573f37c0313dbe2cfc0eb2808cb133
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
This patch changes the serialization gate in raid1 to use CollisionCheck
instead of MD_SERIALIZE_POLICY. It aims to correctly serialize on
write-mostly members when write-behind is armed, and remove a latent NULL
dereference.
Cc: stable@vger.kernel.org
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index afe2ca96ad8c2..997f79c05db7b 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -564,7 +564,7 @@ static void raid1_end_write_request(struct bio *bio)
> call_bio_endio(r1_bio);
> }
> }
> - } else if (test_bit(MD_SERIALIZE_POLICY, &rdev->mddev->flags))
> + } else if (test_bit(CollisionCheck, &rdev->flags))
[Severity: High]
This is a pre-existing issue, but does this unlocked dynamic check of
CollisionCheck create a time-of-check to time-of-use race against sysfs
state changes?
When -writemostly is echoed to sysfs, mddev_suspend() is called. For
write-behind I/O, mddev->active_io is decremented early when non-writemostly
clones finish, which allows mddev_suspend() to return while the write-mostly
clone is still in flight.
Then state_store() in drivers/md/md.c calls mddev_destroy_serial_pool()
which frees rdev->serial and clears CollisionCheck:
drivers/md/md.c:rdev_uninit_serial() {
if (!test_and_clear_bit(CollisionCheck, &rdev->flags))
return;
kvfree(rdev->serial);
rdev->serial = NULL;
}
When the in-flight write-mostly clone subsequently completes here in
raid1_end_write_request(), could it check CollisionCheck and either call
remove_serial() on the freed pointer, or skip it and leak the serial_info
elements?
> remove_serial(rdev, lo, hi);
> if (r1_bio->bios[mirror] == NULL)
> rdev_dec_pending(rdev, conf->mddev);
[ ... ]
> @@ -1677,7 +1677,11 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
> mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO,
> &mddev->bio_set);
>
> - if (test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
> + /*
> + * CollisionCheck marks every rdev with a serial
> + * tree; order against in-flight write-behind I/O.
> + */
> + if (test_bit(CollisionCheck, &rdev->flags))
[Severity: High]
By gating serialization exclusively on CollisionCheck, do hot-added devices
now silently bypass serialization?
When an array has MD_SERIALIZE_POLICY enabled and a new device is hot-added,
mddev_create_serial_pool() skips initializing the device's serial tree
because it doesn't check the global policy flag.
Since CollisionCheck remains unset for the new device, this change masks
the previous NULL pointer dereference crash by skipping serialization for
the hot-added device entirely. Will this leave the array vulnerable to
overlap write data corruption when stale data persists?
> wait_for_serialization(rdev, r1_bio);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
2026-07-23 20:42 ` [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-23 21:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:05 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] narrow_write_error() dangerously converts failed dataless operations into normal writes (REQ_OP_WRITE), causing crashes during trimming or block layer submission.
- [High] narrow_write_error() resets bi_opf to a bare REQ_OP_WRITE, silently dropping critical data integrity flags like REQ_FUA.
--
--- Patch [6]: [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones ---
commit 63dc9314df6c2490722e42dfb137e9e52bd9544f
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
narrow_write_error() re-issues a failed write in badblock-granularity chunks,
cloning from the master bio and resetting bi_opf to a bare REQ_OP_WRITE. This
drops REQ_NOMERGE for P2PDMA bios, so the patch restores the flag on P2PDMA
retry clones.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index f07edeac6e5a3..19c4dec450de0 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2561,6 +2561,9 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
> }
>
> wbio->bi_opf = REQ_OP_WRITE;
[Severity: High]
This is a pre-existing issue, but does forcing wbio->bi_opf to REQ_OP_WRITE
cause crashes for dataless operations?
If an underlying device fails an operation like REQ_OP_SECURE_ERASE or
REQ_OP_WRITE_ZEROES, forcing it to REQ_OP_WRITE means it is no longer
recognized as dataless.
Would bio_trim() then attempt to advance the bio iterator and dereference
a NULL bi_io_vec array? Even if it doesn't span multiple badblocks and
bypasses the trim crash, would it be submitted to the lower-level driver as
a malformed payloadless write?
> + /* Keep P2PDMA retry bios unmergeable, like the original */
> + if (md_bio_is_p2pdma(wbio))
> + wbio->bi_opf |= REQ_NOMERGE;
> wbio->bi_iter.bi_sector = r1_bio->sector;
> wbio->bi_iter.bi_size = r1_bio->sectors << 9;
>
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 5b63175fb3826..428bddddfe71d 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -2823,6 +2823,9 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
> wbio->bi_iter.bi_sector = wsector +
> choose_data_offset(r10_bio, rdev);
> wbio->bi_opf = REQ_OP_WRITE;
[Severity: High]
This isn't a bug introduced by this patch, but does resetting bi_opf to a
bare REQ_OP_WRITE silently drop critical data integrity flags?
By overwriting the opcode rather than modifying it, it appears flags like
REQ_FUA and REQ_SYNC that were present on the master bio would be lost on the
retry clone, potentially leading to data loss if a power failure occurs.
> + /* Keep P2PDMA retry bios unmergeable, like the original */
> + if (md_bio_is_p2pdma(wbio))
> + wbio->bi_opf |= REQ_NOMERGE;
>
> if (submit_bio_wait(wbio) &&
> !rdev_set_badblocks(rdev, wsector, sectors, 0)) {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=6
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
2026-07-23 20:42 ` [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
@ 2026-07-23 21:09 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:09 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-pci, yukuai, linux-raid
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The `rounddown` and `roundup` macros evaluate to a value but do not modify their arguments in place, causing bad block alignment to fail.
--
--- Patch [7]: [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures ---
commit 7daa9c9f8cade5fa7f5dd4ab20ed50fa6c654eff
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1,raid10: skip futile retries on P2PDMA mapping failures
This patch skips replacement and FailFast eviction for BLK_STS_P2PDMA errors
in raid1 and raid10, keeping the WriteErrorSeen flag so the range stays
fenced. It also bypasses block-by-block submit_bio_wait retries for
P2PDMA errors, instead recording the whole range as a bad block.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 19c4dec450de0..83ebf40237d57 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
[ ... ]
> @@ -2624,10 +2624,15 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
> * narrow down and record precise write
> * errors.
> */
> + struct md_rdev *rdev = conf->mirrors[m].rdev;
>
> fail = true;
> - narrow_write_error(r1_bio, m);
> - rdev_dec_pending(conf->mirrors[m].rdev,
> - conf->mddev);
> + if (r1_bio->bios[m]->bi_status == BLK_STS_P2PDMA)
> + rdev_set_badblocks(rdev, r1_bio->sector,
> + r1_bio->sectors, 0);
[Severity: High]
This is a pre-existing issue, but will these rdev_set_badblocks calls
fail to align correctly due to how rounddown and roundup are used
in the badblocks subsystem?
Looking at _badblocks_set in block/badblocks.c, the alignment macros
are evaluated but their results are discarded rather than assigned back to
s and next:
block/badblocks.c:_badblocks_set() {
...
if (bb->shift > 0) {
rounddown(s, 1 << bb->shift);
roundup(next, 1 << bb->shift);
}
...
}
Does this cause MD RAID configurations with bb->shift > 0 to track
unaligned, smaller bad blocks instead of the full chunk?
If so, this could allow later I/O requests to bypass the check and access
bad hardware sectors. The same discarded macro results appear to be present
in _badblocks_clear and _badblocks_check as well:
block/badblocks.c:_badblocks_clear() {
...
roundup(s, 1 << bb->shift);
rounddown(target, 1 << bb->shift);
...
}
block/badblocks.c:_badblocks_check() {
...
rounddown(s, 1 << bb->shift);
roundup(target, 1 << bb->shift);
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=7
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs()
2026-07-23 20:42 ` [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
@ 2026-07-23 21:09 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:09 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
> ib_dma_map_sg() folds every mapping error into a zero return, losing
> the DMA layer's error code. Switch both scatterlist mappings to
> ib_dma_map_sgtable_attrs(), which preserves it for the next patch to
> act on. No behavior change: failures are still reported as -EIO.
>
> A local sg_table is needed: dma_map_sgtable() maps orig_nents
> entries, and the embedded table's orig_nents is the allocated count,
> not the count blk_rq_map_sg() produced. rdma_rw_ctx_init() does the
> same.
>
> Assisted-by: Claude:claude-fable-5
> Cc: stable@vger.kernel.org # v7.1
> Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
2026-07-23 20:42 ` [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
@ 2026-07-23 21:13 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:13 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
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 <mykola@meshstor.io>
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 <logang@deltatee.com>
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
> 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/20260723204206.76930-1-mykola@meshstor.io?part=8
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
2026-07-23 20:42 ` [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
@ 2026-07-23 21:19 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-23 21:19 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Modifying the PI metadata buffer via nvme_start_request() after it has been mapped for DMA causes cache coherency issues on non-coherent architectures.
Pre-existing issues:
- [High] Accessing the block request and its associated PDU after being freed on the error handling path when mapping or post_send fails with -EIO.
- [High] Returning BLK_STS_RESOURCE after the request has been started triggers kernel warnings and false timeouts.
- [Medium] Returning a hard error from queue_rq after calling nvme_start_request() bypasses nvme_mpath_end_request(), leaking the nr_active counter.
--
--- Patch [10]: [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers ---
commit 2111f8ba9b5738dc4d1ef4e08339058c75231ebd
Author: Mykola Marzhan <mykola@meshstor.io>
nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
> A P2P transfer the PCIe topology cannot route fails DMA mapping
> with -EREMOTEIO. nvme-rdma folds every mapping error into -EIO, a
> retryable host-path error: multipath requeues the I/O forever, a
> single path burns its whole retry budget.
>
> Propagate the real error code and return the unroutable case as the
> non-retryable BLK_STS_P2PDMA. As in nvme-pci, -ENOMEM now requeues
> (BLK_STS_RESOURCE) and -EINVAL fails (BLK_STS_IOERR); -EIO stays a
> retryable host-path error. While at it, start the request only
> after mapping succeeds -- nvme-pci's order -- and ratelimit the
> map-failure message.
> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
> index 63830334c73e2..2f50509a7a616 100644
> --- a/drivers/nvme/host/rdma.c
> +++ b/drivers/nvme/host/rdma.c
> [ ... ]
> @@ -2033,8 +2029,6 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
> if (ret)
> goto unmap_qe;
>
> - nvme_start_request(rq);
> -
> if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) &&
> queue->pi_support &&
> (c->common.opcode == nvme_cmd_write ||
> @@ -2046,11 +2040,13 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
>
> err = nvme_rdma_map_data(queue, rq, c);
> if (unlikely(err < 0)) {
> - dev_err(queue->ctrl->ctrl.device,
> - "Failed to map data (%d)\n", err);
> + dev_err_ratelimited(queue->ctrl->ctrl.device,
> + "Failed to map data (%d)\n", err);
> goto err;
> }
>
> + nvme_start_request(rq);
[Severity: High]
Since the patch moves nvme_start_request() to execute after
nvme_rdma_map_data() has mapped the metadata buffer for streaming DMA, can this
cause cache coherency issues on non-coherent architectures?
nvme_start_request() invokes blk_integrity_prepare(), which writes to the PI
tags directly in the CPU cache.
Because this now happens after the memory is mapped, those modifications might
not be synced to main memory, causing the RDMA NIC to read stale PI tags and
leading to silent data corruption and I/O failures.
> +
> sqe->cqe.done = nvme_rdma_send_done;
>
> ib_dma_sync_single_for_device(dev, sqe->dma,
> [ ... ]
> @@ -2070,6 +2066,9 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
>
> err_unmap:
> nvme_rdma_unmap_data(queue, rq);
> err:
> if (err == -EIO)
> ret = nvme_host_path_error(rq);
[Severity: High]
This is a pre-existing issue, but does this lead to a use-after-free when
nvme_rdma_map_data() or nvme_rdma_post_send() fails with -EIO?
nvme_host_path_error() synchronously completes the request via
blk_mq_set_request_complete() and __nvme_complete_rq(). This frees the request
and its PDU back to the tag allocator, where it can be immediately reallocated.
The code then falls through and accesses the freed request by calling
nvme_cleanup_cmd(rq) and reading req->sqe.dma during ib_dma_unmap_single().
> else if (err == -ENOMEM || err == -EAGAIN)
> ret = BLK_STS_RESOURCE;
[Severity: High]
This is a pre-existing issue, but does returning BLK_STS_RESOURCE here cause
false I/O timeouts?
At this point, nvme_start_request() has already been called, placing the
request in the MQ_RQ_IN_FLIGHT state and starting its timeout timer.
When returning BLK_STS_RESOURCE, the block layer requeues the request without
reverting its state to MQ_RQ_IDLE. This leaves the timer ticking while the
request is on the dispatch queue, eventually triggering a false timeout and
controller reset.
> + /* Peer memory unreachable from this device: don't retry. */
> + else if (err == -EREMOTEIO)
> + ret = BLK_STS_P2PDMA;
> else
> ret = BLK_STS_IOERR;
> nvme_cleanup_cmd(rq);
[Severity: Medium]
This is a pre-existing issue, but does returning a hard error here leak the
multipath nr_active counter?
When a hard error is returned to the block layer from nvme_rdma_queue_rq(),
it directly completes the request via blk_mq_end_request(), bypassing the
driver's .complete callback.
Since nvme_start_request() was already called and incremented the multipath
nr_active counter, bypassing the completion callback means
nvme_mpath_end_request() is never invoked to balance the counter.
> unmap_qe:
> ib_dma_unmap_single(dev, req->sqe.dma, sizeof(struct nvme_command),
> DMA_TO_DEVICE);
> return ret;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723204206.76930-1-mykola@meshstor.io?part=10
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-07-23 21:19 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 20:41 [PATCH v5 00/10] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
2026-07-23 20:41 ` [PATCH v5 01/10] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
2026-07-23 20:51 ` sashiko-bot
2026-07-23 20:41 ` [PATCH v5 02/10] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
2026-07-23 20:51 ` sashiko-bot
2026-07-23 20:41 ` [PATCH v5 03/10] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
2026-07-23 21:04 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 04/10] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
2026-07-23 21:01 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 05/10] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
2026-07-23 20:54 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 06/10] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
2026-07-23 21:05 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 07/10] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
2026-07-23 21:09 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 08/10] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
2026-07-23 21:13 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 09/10] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
2026-07-23 21:09 ` sashiko-bot
2026-07-23 20:42 ` [PATCH v5 10/10] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
2026-07-23 21:19 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox