All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers
@ 2026-07-18 16:25 Mykola Marzhan
  2026-07-18 16:25 ` [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, linux-kernel, linux-rdma, linux-pci

Driving peer-to-peer I/O (NVMe CMB source) through md arrays and
nvme-rdma legs on asymmetric PCIe topologies -- where the peer device
reaches some members/paths but not others -- turns up two failures on
v7.2-rc, both rooted in how an unsupported-P2PDMA mapping failure is
reported. On md: silent data loss -- an unreachable-leg P2PDMA write
counts as written, mirrors silently diverge, and where no member is
reachable the write reports success with zero copies on stable
storage; reads picked to an unreachable leg fail with EIO and are
never retried on the mirror that holds the data. On nvme-rdma: the
transport swallows the DMA layer's mapping errno into a path error,
which under the default multipath configuration livelocks the I/O (a
stacked md mirror hangs) and with nvme_core.multipath=N surfaces as
retryable BLK_STS_TRANSPORT.

The root cause is a block-layer status-code regression, not an md or
nvme bug. Commit 91fb2b6052f7 ("nvme-pci: convert to using
dma_map_sgtable()") deliberately mapped unsupported P2PDMA transfers
to BLK_STS_TARGET ("... return BLK_STS_TARGET so the request isn't
retried"). Commit 858299dc6160 ("block: add scatterlist-less DMA
mapping helpers") silently changed that to BLK_STS_INVAL, and since
commit 7ce3c1dd78fc ("nvme-pci: convert the data mapping to
blk_rq_dma_map") in v6.17 the failure surfaces to direct NVMe
consumers as EINVAL instead of the documented -EREMOTEIO, with
blk_path_error() now classifying it as retryable. md/raid1,raid10
ignore BLK_STS_INVAL leg failures per commit f7b24c7b41f2
("md/raid1,raid10: don't fail devices for invalid IO errors"), where
it means a request-shaped error that fails identically on every
member -- hence the silent divergence. The md leg of the regression
became reachable when md started advertising BLK_FEAT_PCI_P2PDMA in
v7.2-rc1 (commit 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA
from member devices to RAID device")).

Patch 1 restores BLK_STS_TARGET at the source. md then handles an
unreachable leg like any other per-device error: badblocks on the
affected member, the master bio succeeds while an In_sync leg holds
the data, and reads are redirected to the other mirror.

Patches 2-4 fix independent md-side P2PDMA bugs of the same vintage:
md_submit_bio() strips REQ_NOMERGE (the only request-level protection
against merging P2PDMA segments across pgmaps); raid1 write-behind
CPU-copies device BAR memory via bio_copy_data(); and
narrow_write_error()'s retry clones reset bi_opf, dropping the
REQ_NOMERGE protection exactly on the error-retry path.

Patch 5 stops the restored device-error machinery from misfiring
where its medium-error assumptions don't hold. A mapping failure is a
property of the peer/member pairing: retrying the same peer pages
against the same member cannot succeed, and there is nothing on the
medium to repair. Without it, narrow_write_error() serializes dozens
of guaranteed-to-fail chunk retries per failed write; on reads each
unreachable-leg pick costs a full freeze_array() quiesce plus a tick
of the read-error budget -- measured below, a mixed host/P2P read
workload on an asymmetric topology kicks the perfectly healthy far
leg after ~20 failed picks, in under a second; and on FailFast
members a single unroutable I/O evicts a healthy mirror outright.
Because BLK_STS_TARGET is also produced for transient device
conditions md cannot distinguish from bi_status alone, writes probe
the whole range once instead of predicting futility: a real mapping
failure is recorded in one call, a cleared transient recovers with
nothing recorded. Patches 1 and 5 belong in the same release: with
patch 1 alone, restoring the error surfaces the retry storms and
read-path evictions above on 7.2's newly reachable md path.

Patch 6 closes the same hole in the rdma transport. nvme-rdma maps
the data scatterlist with ib_dma_map_sg(), which returns 0 on a
peer-unreachable failure, discarding the -EREMOTEIO that
dma_map_sgtable() documents for exactly this case; the driver reports
it as a path error. Because the multipath head node advertises
BLK_FEAT_PCI_P2PDMA (commit fb0eeeed91f3 ("nvme-multipath: enable PCI
P2PDMA for multipath devices"), v7.2-rc1) and the mapping failure is
deterministic, nvme_failover_req() requeues the bios with a fresh
retry budget every cycle and the I/O never completes -- a hot requeue
livelock that hangs a stacked md mirror instead of failing over; with
nvme_core.multipath=N it burns nvme_max_retries requeues and
completes as retryable BLK_STS_TRANSPORT. Patch 6 maps with
ib_dma_map_sgtable_attrs() to preserve the errno and returns
BLK_STS_TARGET, matching nvme-pci so patch 5's handling covers rdma
legs too, and starts the request only after the map succeeds so
nvme_mpath_start_request() accounting cannot leak on the direct
blk-mq completion. nvme-rdma stays on the scatterlist DMA API: its
fast-reg MR path (ib_map_mr_sg()) consumes scatterlists, so a
blk_rq_dma_map conversion is separate modernization, out of scope for
a fix.

Behavior on v7.2-rc2, QEMU rig (one CMB provider, two NVMe members,
q35 PCIe topologies), 90 8KiB peer-memory reads under concurrent
host reads for the read rows:

  topology     op          v7.2-rc2                patched (1-5)
  symmetric    write+read  ok                      ok
  asymmetric   write       "ok", silent diverge    ok + badblocks on
                                                   far leg (1 probe)
  asymmetric   read        41/90 EIO, no mirror    90/90 ok, no
               under load  retry, no eviction      eviction
  unreachable  write       "ok", NO data copied    EIO + badblocks
  unreachable  read        EIO                     EIO, no members
                                                   kicked

Also verified on the same rig: a transient injected TARGET recovers
through the single probe with nothing recorded; P2P MEDIUM keeps the
chunked path; host-page TARGET/INVAL handling is unchanged; FailFast
survives mapping failures but still evicts on genuine errors;
write-mostly legs are written directly (no write-behind CPU copy); a
degraded array whose only leg is unreachable gets plain EIO -- no
budget charge, no eviction; an exhausted badblocks table fails the
member as described below. The write/read/injection rows repeat
identically on raid10.

Validation of patch 6: the mechanism -- ib_dma_map_sg() returning 0
where ib_dma_map_sgtable_attrs() preserves -EREMOTEIO -- was
confirmed on a real mlx5 HCA with a map-only probe. The
start-after-map reorder ran over an rxe nvmet-rdma loopback under fio
with crc32c verification; multipath inflight and nr_active accounting
drain to zero, matching the pre-reorder kernel. rxe cannot produce
-EREMOTEIO, so the failure paths were driven by injecting that exact
mechanism at the map call site: unpatched, one 4KiB write livelocked
(132 failover requeues in 8s, a stacked raid1 hung; multipath=N:
retryable BLK_STS_TRANSPORT); patched, it failed immediately as
BLK_STS_TARGET with zero requeues, and the stacked raid1 badblocked
the leg without evicting it.

Known trade-offs of routing through the stock md write machinery:
an unroutable P2P write records badblocks and sets WriteErrorSeen
and WantReplacement, so later writes overlapping those ranges skip
the member -- host writes do not clear the entries and repair skips
known-bad ranges -- leaving them single-copy until re-add or
replacement recovery (host memory, so it succeeds) rewrites and
clears them; a member whose badblocks table is exhausted or disabled
is failed on the first unroutable P2P write, as with any write error
md cannot record. FailFast members are exempted from md_error() for
mapping failures only (nothing reached the wire, so the error says
nothing about device health); genuine I/O errors keep their
immediate-eviction semantics. raid10 additionally fails a
replacement device outright on a P2P write mapping failure (its
stock replacement-write policy), and an IO_BLOCKED slot excludes
that slot's replacement from the read retry -- both pre-existing
raid10 behaviors with a new trigger. On reads, read_balance() keeps
no memory of unreachability, so each far-leg pick still costs one
failed submission before the redirect; a sticky per-rdev
reachability hint would be md-next follow-up material, as would a
distinct block status naming the mapping failure exactly.

Routing: patch 1 is block-tree material and fixes a v6.17-rc1 status
regression (hence its Cc: stable); patches 2-5 are md and address
exposure that only became reachable with v7.2-rc1; patch 6 is
nvme-rdma and also carries Cc: stable -- the transport misreport
predates the head-node change (Fixes: 23528aa3320a, v7.1) and is
reachable on v7.1 with multipath=N. Patch 6 sources the errno from
dma_map_sgtable() in rdma.c and does not depend on patch 1, which
fixes the separate blk_rq_dma_map path. Patches 1 (block) and 6 (nvme) are
independently applicable bug fixes: each fixes a user-visible defect
on its own and neither depends on the md patches, while 2-5 are
md-side hardening built on patch 1 -- a maintainer can take 1 and 6
even if the md discussion runs longer. Single-cycle routing through
the relevant trees with acks works for us: patch 1 block, 2-5 md,
6 nvme.

Per Documentation/process/coding-assistants.rst: the patches were
developed with AI assistance (see the Assisted-by trailers); all code
was human-reviewed and tested as described above.

Series is against v7.2-rc2.

Mykola Marzhan (6):
  blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers
  md: ensure REQ_NOMERGE is set on P2PDMA bios
  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
  nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers

 block/blk-mq-dma.c       | 10 ++++++-
 drivers/md/md.c          | 14 ++++++++--
 drivers/md/md.h          | 31 +++++++++++++++++++++
 drivers/md/raid1.c       | 56 +++++++++++++++++++++++++++++++------
 drivers/md/raid10.c      | 60 ++++++++++++++++++++++++++++++++++------
 drivers/nvme/host/rdma.c | 26 +++++++++++------
 6 files changed, 168 insertions(+), 29 deletions(-)


base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
--
2.43.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 16:53   ` sashiko-bot
  2026-07-18 16:25 ` [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, linux-kernel, linux-rdma, linux-pci

Commit 91fb2b6052f7 ("nvme-pci: convert to using dma_map_sgtable()")
deliberately mapped unsupported P2PDMA transfers to BLK_STS_TARGET,
matching dma_map_sgtable()'s -EREMOTEIO: "When this happens, return
BLK_STS_TARGET so the request isn't retried." The conversion to
blk_rq_dma_map silently changed the status to BLK_STS_INVAL, which
regresses two consumers:

 - md/raid1 and raid10 ignore BLK_STS_INVAL leg failures
   (commit f7b24c7b41f2 ("md/raid1,raid10: don't fail devices for
   invalid IO errors"), where it means a request-shaped error that
   fails identically on every member).  Since commit 02666132403a
   ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
   P2PDMA bios reach md arrays: a peer-memory write to a member the
   peer cannot reach is counted as written, the master bio reports
   success, mirrors silently diverge, and on a topology where no
   member is reachable the write reports success with zero copies on
   stable storage.

 - the failure's classification flips: for direct NVMe consumers
   (nvme advertises BLK_FEAT_PCI_P2PDMA) the mapping failure has
   surfaced as EINVAL instead of the documented -EREMOTEIO since
   v6.17, and blk_path_error(BLK_STS_INVAL) is true, so a stacking
   consumer would treat it as a retryable path error (dm-mpath is
   the only in-tree blk_path_error() caller; P2P bios cannot
   currently reach it, but the classification is wrong on its face).

Restore BLK_STS_TARGET.  md then routes an unreachable leg through
its per-device error handling (badblocks, WantReplacement, mirror
retry for reads); a follow-up patch teaches raid1/raid10 to handle
mapping failures without the retry storms that machinery was built
around.

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
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
 block/blk-mq-dma.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/block/blk-mq-dma.c b/block/blk-mq-dma.c
index bfdb9ed70741..2eed06bfe791 100644
--- a/block/blk-mq-dma.c
+++ b/block/blk-mq-dma.c
@@ -190,7 +190,15 @@ static bool blk_dma_map_iter_start(struct request *req, struct device *dma_dev,
 	case PCI_P2PDMA_MAP_NONE:
 		break;
 	default:
-		iter->status = BLK_STS_INVAL;
+		/*
+		 * P2P transfers that the mapping layer cannot support
+		 * report BLK_STS_TARGET, matching dma_map_sgtable()'s
+		 * -EREMOTEIO and the pre-blk_rq_dma_map nvme behavior:
+		 * the failure is a property of this device pairing, so
+		 * it must not be retried on another path (blk_path_error)
+		 * nor be mistaken for an invalid request.
+		 */
+		iter->status = BLK_STS_TARGET;
 		return false;
 	}
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
  2026-07-18 16:25 ` [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 16:50   ` sashiko-bot
  2026-07-18 16:25 ` [PATCH 3/6] md/raid1: don't use write-behind for " Mykola Marzhan
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, linux-kernel, linux-rdma, linux-pci

md_submit_bio() unconditionally strips REQ_NOMERGE before passing the
bio to the personality, an optimization from commit 9c573de3283a ("MD:
make bio mergeable"): a bio that md has split may become mergeable
again below md.

For PCI P2PDMA bios the flag is load-bearing, not a hint. The block
layer sets REQ_NOMERGE on P2PDMA bios (__bio_add_page(), and the
extraction path of bio_iov_iter_get_pages()) because the DMA mapping
type of a request is resolved once, from its first segment
(blk_dma_map_iter_start()), and request-level merging is prevented
only by REQ_NOMERGE. Stripping it allows the member queue to merge a
P2PDMA bio with a bio carrying pages of a different pgmap, or host
memory, mapping the merged segments with the wrong bus address:
silent data corruption on the member.

Set the flag for P2PDMA bios instead of merely preserving it. No
in-tree path currently submits P2PDMA pages through the bvec-iter
path (bio_iov_bvec_set()), which skips the flagging -- but nothing
structural prevents one, so setting rather than preserving hardens
md against that gap at the cost of one branch. Everything else keeps
the original optimization of clearing the flag. This covers every
personality that advertises BLK_FEAT_PCI_P2PDMA (raid0, raid1,
raid10), which is why the fix lives in the shared md_submit_bio()
path.

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>
---
 drivers/md/md.c | 14 ++++++++++++--
 drivers/md/md.h | 18 ++++++++++++++++++
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index d1465bcd86c8..3ae4fd4ef381 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -451,8 +451,18 @@ 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 could be mergeable again below md, but for P2PDMA
+	 * bios REQ_NOMERGE is load-bearing: the DMA mapping type of a
+	 * request is resolved once, from its first segment, so requests
+	 * must stay single-provider (see __bio_add_page()). Set the flag
+	 * rather than merely preserve it -- bios built through the
+	 * bvec-iter path arrive without it.
+	 */
+	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..140e2b3670d8 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,22 @@
 #include <trace/events/block.h>
 
 #define MaxSector (~(sector_t)0)
+
+/*
+ * Check if the bio carries PCI P2PDMA (peer device memory) pages. Read
+ * bi_io_vec directly rather than using bio_first_bvec_all(), which WARNs
+ * on cloned bios: md routinely handles split clones, which have
+ * bi_vcnt == 0 but a valid bi_io_vec shared with the parent. P2PDMA and
+ * host pages must not be mixed within one bio, so the first bvec is
+ * representative. Only valid before the bio's iterator is consumed:
+ * bio_has_data() is false at completion time.
+ */
+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.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/6] md/raid1: don't use write-behind for P2PDMA bios
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
  2026-07-18 16:25 ` [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
  2026-07-18 16:25 ` [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 16:58   ` sashiko-bot
  2026-07-18 16:25 ` [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, 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.

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>
---
 drivers/md/raid1.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index afe2ca96ad8c..57f64e890102 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1575,9 +1575,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 && rdev && test_bit(WriteMostly, &rdev->flags) &&
+		    !md_bio_is_p2pdma(bio))
 			write_behind = true;
 
 		r1_bio->bios[i] = NULL;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (2 preceding siblings ...)
  2026-07-18 16:25 ` [PATCH 3/6] md/raid1: don't use write-behind for " Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 16:58   ` sashiko-bot
  2026-07-18 16:25 ` [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
  2026-07-18 16:25 ` [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, 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>
---
 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 57f64e890102..a30032321191 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2565,6 +2565,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 0a3cfdd3f5df..f7ef903a3d4e 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2831,6 +2831,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.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (3 preceding siblings ...)
  2026-07-18 16:25 ` [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 17:07   ` sashiko-bot
  2026-07-18 16:25 ` [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, linux-kernel, linux-rdma, linux-pci

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 all three paths:

 - 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).

 - 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 (peer reaches one member but
   not the other) a mixed host/P2P read workload charges 20 errors to
   the unreachable leg within a second and kicks a perfectly healthy
   member -- destroying redundancy for all I/O because some I/O
   cannot route, while every P2P read is meanwhile served correctly
   by the reachable mirror.

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

Handle the mapping failure -- BLK_STS_TARGET on a bio carrying PCI
P2PDMA pages -- 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 retry observes the outcome instead of predicting it: a
   mapping failure fails deterministically and is recorded in one
   call, collapsing the chunked storm to a single bio, while a
   transient error that has since cleared succeeds and records
   nothing, exactly as the chunked loop behaves today.

 - Reads: mark the leg IO_BLOCKED so read_balance() picks another
   mirror, and skip the freeze/fix cycle and the budget charge.  If
   no mirror can serve the read the master bio fails with EIO as
   before, now without kicking healthy members on the way.  The P2P
   page check is what keeps this narrow: a member that keeps failing
   with a device-produced BLK_STS_TARGET (e.g. stuck not-ready) must
   still be evictable, so the status alone cannot gate the skip.

 - 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.

The first bvec is representative of the whole bio: the block layer
never mixes P2P pages from different pgmaps with each other or with
host pages in one bio (zone_device_pages_compatible()), and reads
bi_io_vec[0] the same way in bio_iov_iter_get_pages().  md's P2PDMA
support is otherwise declared at array setup time through queue
limits, but whether one leg's completion was a mapping refusal is a
property of that bio alone, so the completion path has to look at
the bio.  A distinct block status naming the mapping failure exactly
would remove the remaining ambiguity against device-produced
BLK_STS_TARGET and is left as a follow-up.

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       before (patches 1-4)      after (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@meshstor.io>
---
 drivers/md/md.h     | 13 +++++++++++
 drivers/md/raid1.c  | 46 +++++++++++++++++++++++++++++++-----
 drivers/md/raid10.c | 57 ++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/drivers/md/md.h b/drivers/md/md.h
index 140e2b3670d8..b1abfc89cfc0 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -40,6 +40,19 @@ static inline bool md_bio_is_p2pdma(struct bio *bio)
 	       is_pci_p2pdma_page(bio->bi_io_vec->bv_page);
 }
 
+/*
+ * True when a leg bio failed because its P2PDMA pages cannot be DMA-mapped
+ * to this member (BLK_STS_TARGET from blk_dma_map_iter_start()). Usable at
+ * completion time, unlike md_bio_is_p2pdma(): leg bios are clones sharing
+ * the master bio's bvec table, which outlives the leg's end_io, but their
+ * iterator is consumed by then, so bio_has_data() cannot gate the access.
+ */
+static inline bool md_bio_p2pdma_mapping_error(struct bio *bio)
+{
+	return bio->bi_status == BLK_STS_TARGET && 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:
  */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index a30032321191..241c2994b6a3 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -491,7 +491,9 @@ static void raid1_end_write_request(struct bio *bio)
 		if (test_bit(FailFast, &rdev->flags) &&
 		    (bio->bi_opf & MD_FAILFAST) &&
 		    /* We never try FailFast to WriteMostly devices */
-		    !test_bit(WriteMostly, &rdev->flags)) {
+		    !test_bit(WriteMostly, &rdev->flags) &&
+		    /* A mapping failure says nothing about device health */
+		    !md_bio_p2pdma_mapping_error(bio)) {
 			md_error(r1_bio->mddev, rdev);
 		}
 
@@ -2517,7 +2519,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;
@@ -2531,6 +2533,12 @@ 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: used when per-block narrowing
+	 * cannot find a good block (P2PDMA mapping failures fail the
+	 * whole range identically), while a single retry still tells
+	 * a since-cleared transient error apart.
+	 *
 	 * We currently own a reference on the rdev.
 	 */
 
@@ -2545,9 +2553,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;
@@ -2629,7 +2640,20 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
 			 * errors.
 			 */
 			fail = true;
-			narrow_write_error(r1_bio, m);
+			if (md_bio_p2pdma_mapping_error(r1_bio->bios[m]))
+				/*
+				 * A P2PDMA mapping failure fails the whole
+				 * range identically, so narrowing block by
+				 * block cannot find a good block -- but a
+				 * transient device error also surfaces as
+				 * BLK_STS_TARGET, so don't assume.  Retry
+				 * the range once: if it fails, record it in
+				 * one go; if it succeeds, there was nothing
+				 * wrong with the medium.
+				 */
+				narrow_write_error(r1_bio, m, true);
+			else
+				narrow_write_error(r1_bio, m, false);
 			rdev_dec_pending(conf->mirrors[m].rdev,
 					 conf->mddev);
 		}
@@ -2656,6 +2680,7 @@ 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];
+	bool p2pdma_error = md_bio_p2pdma_mapping_error(bio);
 	struct mddev *mddev = conf->mddev;
 	sector_t sector;
 
@@ -2675,6 +2700,15 @@ 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 pages cannot be DMA-mapped to this member;
+		 * there is nothing to fix on the medium and the member
+		 * is healthy for host I/O: don't charge the read-error
+		 * budget or fail a FailFast member, 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/raid10.c b/drivers/md/raid10.c
index f7ef903a3d4e..b24429a19254 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -489,7 +489,9 @@ static void raid10_end_write_request(struct bio *bio)
 
 			dec_rdev = 0;
 			if (test_bit(FailFast, &rdev->flags) &&
-			    (bio->bi_opf & MD_FAILFAST)) {
+			    (bio->bi_opf & MD_FAILFAST) &&
+			    /* A mapping failure says nothing about device health */
+			    !md_bio_p2pdma_mapping_error(bio)) {
 				md_error(rdev->mddev, rdev);
 			}
 
@@ -2786,7 +2788,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 +2802,12 @@ 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: used when per-block narrowing
+	 * cannot find a good block (P2PDMA mapping failures fail the
+	 * whole range identically), while a single retry still tells
+	 * a since-cleared transient error apart.
+	 *
 	 * We currently own a reference to the rdev.
 	 */
 
@@ -2814,9 +2822,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 +2867,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 +2880,28 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
 	 * frozen.
 	 */
 	bio = r10_bio->devs[slot].bio;
+	p2pdma_error = md_bio_p2pdma_mapping_error(bio);
 	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 pages cannot be DMA-mapped to this member;
+		 * there is nothing to fix on the medium and the member
+		 * is healthy for host I/O: don't charge the read-error
+		 * budget or fail a FailFast member, 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;
@@ -2948,7 +2971,23 @@ 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 (md_bio_p2pdma_mapping_error(bio))
+					/*
+					 * A P2PDMA mapping failure fails
+					 * the whole range identically, so
+					 * narrowing block by block cannot
+					 * find a good block -- but a
+					 * transient device error also
+					 * surfaces as BLK_STS_TARGET, so
+					 * don't assume.  Retry the range
+					 * once: if it fails, record it in
+					 * one go; if it succeeds, there
+					 * was nothing wrong with the
+					 * medium.
+					 */
+					narrow_write_error(r10_bio, m, true);
+				else
+					narrow_write_error(r10_bio, m, false);
 				rdev_dec_pending(rdev, conf->mddev);
 			}
 			bio = r10_bio->devs[m].repl_bio;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers
  2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (4 preceding siblings ...)
  2026-07-18 16:25 ` [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
@ 2026-07-18 16:25 ` Mykola Marzhan
  2026-07-18 17:12   ` sashiko-bot
  5 siblings, 1 reply; 13+ messages in thread
From: Mykola Marzhan @ 2026-07-18 16:25 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, Leon Romanovsky, Jason Gunthorpe,
	Kiran Kumar Modukuri, Chaitanya Kulkarni, Logan Gunthorpe,
	Bjorn Helgaas, Shivaji Kant, Pranjal Shrivastava,
	Henrique Carvalho, linux-kernel, linux-rdma, linux-pci

Since commit 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA
transport") nvme-rdma accepts P2PDMA bios, but a mapping failure for
peer memory the HCA cannot reach is misreported as a path error:
ib_dma_map_sg() returns 0, discarding the -EREMOTEIO that
dma_map_sgtable() documents for exactly this case, and the driver
converts it to -EIO -> nvme_host_path_error().

Under the default multipath configuration (the multipath head node
advertises BLK_FEAT_PCI_P2PDMA since commit fb0eeeed91f3
("nvme-multipath: enable PCI P2PDMA for multipath devices")) the
path-error status makes nvme_failover_req() requeue the bios with a
fresh retry budget each cycle, and since the mapping failure is a
deterministic property of the peer/device pairing the I/O simply
never completes: a hot requeue livelock, and a stacked md mirror
hangs instead of failing over to its other leg.  With
nvme_core.multipath=N the request burns nvme_max_retries requeues --
nothing ever reaching the wire -- and completes as
BLK_STS_TRANSPORT, which blk_path_error() classifies as retryable
and md/raid1,raid10 treat as a genuine device error: retry storms on
writes, read-error-budget eviction of a healthy member on reads (see
the preceding md patches, whose mapping-failure handling keys on
BLK_STS_TARGET).

Map the data scatterlist with ib_dma_map_sgtable_attrs() so the DMA
layer's error code is preserved, and translate -EREMOTEIO to
BLK_STS_TARGET: the classification nvme-pci established in commit
91fb2b6052f7 ("nvme-pci: convert to using dma_map_sgtable()") and
the preceding blk-mq-dma patch restores (the blk_rq_dma_map
conversion had changed it to BLK_STS_INVAL).  The failure is a
property of the peer/device pairing, so it must not be retried on
this or another path.  Start the request only after mapping
succeeds, as nvme-pci does: a mapping failure now errors out of
queue_rq on a not-yet-started request, so nvme_mpath_start_request()
accounting is never taken and cannot leak on the direct blk-mq
completion (this also closes the same latent leak for the existing
BLK_STS_IOERR returns).  A mapping -ENOMEM now takes the existing
BLK_STS_RESOURCE branch instead of masquerading as a path error, and
a DMA-layer -EINVAL completes as BLK_STS_IOERR instead of a path
error; generic -EIO keeps today's host-path-error behavior, and
virt-DMA devices are unaffected.  The metadata scatterlist keeps
ib_dma_map_sg(): integrity buffers are host memory and cannot
produce -EREMOTEIO.  Ratelimit the map-failure message -- with an md
mirror steering peer-memory I/O around an unreachable leg it fires
per redirected I/O, not per rare event.

Fixes: 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA transport")
Cc: stable@vger.kernel.org # v7.1
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
 drivers/nvme/host/rdma.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 6909e3542794..b8642cd2fb79 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,12 @@ 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)) {
-		ret = -EIO;
+	sgt.sgl = req->data_sgl.sg_table.sgl;
+	sgt.orig_nents = req->data_sgl.nents;
+	ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, rq_dma_dir(rq), 0);
+	if (unlikely(ret))
 		goto out_free_table;
-	}
+	*count = sgt.nents;
 
 	if (blk_integrity_rq(rq)) {
 		req->metadata_sgl->sg_table.sgl =
@@ -2026,8 +2027,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 ||
@@ -2039,11 +2038,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,
@@ -2063,6 +2064,13 @@ 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;
+	/*
+	 * The DMA layer refused to map peer memory to this device: a
+	 * property of the pairing, not a path failure.  Match nvme-pci
+	 * and do not retry (see blk_path_error()).
+	 */
+	else if (err == -EREMOTEIO)
+		ret = BLK_STS_TARGET;
 	else
 		ret = BLK_STS_IOERR;
 	nvme_cleanup_cmd(rq);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios
  2026-07-18 16:25 ` [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-18 16:50   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 16:50 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-pci, yukuai, linux-raid

> md_submit_bio() unconditionally strips REQ_NOMERGE before passing the
> bio to the personality, an optimization from commit 9c573de3283a ("MD:
> make bio mergeable"): a bio that md has split may become mergeable
> again below md.
> 
> For PCI P2PDMA bios the flag is load-bearing, not a hint. The block
> layer sets REQ_NOMERGE on P2PDMA bios (__bio_add_page(), and the
> extraction path of bio_iov_iter_get_pages()) because the DMA mapping
> type of a request is resolved once, from its first segment
> (blk_dma_map_iter_start()), and request-level merging is prevented
> only by REQ_NOMERGE. Stripping it allows the member queue to merge a
> P2PDMA bio with a bio carrying pages of a different pgmap, or host
> memory, mapping the merged segments with the wrong bus address:
> silent data corruption on the member.
> 
> Set the flag for P2PDMA bios instead of merely preserving it. No
> in-tree path currently submits P2PDMA pages through the bvec-iter
> path (bio_iov_bvec_set()), which skips the flagging -- but nothing
> structural prevents one, so setting rather than preserving hardens
> md against that gap at the cost of one branch. Everything else keeps
> the original optimization of clearing the flag. This covers every
> personality that advertises BLK_FEAT_PCI_P2PDMA (raid0, raid1,
> raid10), which is why the fix lives in the shared md_submit_bio()
> path.
> 
> 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/20260718162547.448892-1-mykola@meshstor.io?part=2


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers
  2026-07-18 16:25 ` [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
@ 2026-07-18 16:53   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 16:53 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-pci, yukuai, linux-raid

> Commit 91fb2b6052f7 ("nvme-pci: convert to using dma_map_sgtable()")
> deliberately mapped unsupported P2PDMA transfers to BLK_STS_TARGET,
> matching dma_map_sgtable()'s -EREMOTEIO: "When this happens, return
> BLK_STS_TARGET so the request isn't retried." The conversion to
> blk_rq_dma_map silently changed the status to BLK_STS_INVAL, which
> regresses two consumers:
> 
>  - md/raid1 and raid10 ignore BLK_STS_INVAL leg failures
>    (commit f7b24c7b41f2 ("md/raid1,raid10: don't fail devices for
>    invalid IO errors"), where it means a request-shaped error that
>    fails identically on every member).  Since commit 02666132403a
>    ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
>    P2PDMA bios reach md arrays: a peer-memory write to a member the
>    peer cannot reach is counted as written, the master bio reports
>    success, mirrors silently diverge, and on a topology where no
> [ ... ]
> 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
> 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/20260718162547.448892-1-mykola@meshstor.io?part=1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
  2026-07-18 16:25 ` [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-18 16:58   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 16:58 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: yukuai, linux-pci, linux-raid

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] narrow_write_error() silently drops REQ_FUA, REQ_PREFLUSH, and REQ_SYNC flags during block-by-block write retries by overwriting the bio's op flags with a bare REQ_OP_WRITE.
--

--- Patch [4]: [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones ---
commit 2b72bc4b60daf0429fd1c6c62c181f2b0048e81d
Author: Mykola Marzhan <mykola@meshstor.io>

md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones

This commit ensures that REQ_NOMERGE is retained on P2PDMA retry bios in
narrow_write_error(). It addresses an issue where resetting bi_opf to a
bare REQ_OP_WRITE dropped this protection, potentially allowing unsafe
merging.

> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 57f64e8901024..a300323211911 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c

[ ... ]

> @@ -2565,6 +2565,9 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
>  		}
>  
>  		wbio->bi_opf = REQ_OP_WRITE;

[Severity: High]
This isn't a bug introduced by this patch, but does this overwrite of
bi_opf with a bare REQ_OP_WRITE drop important operational flags?

If the original write contains data integrity flags like REQ_FUA, REQ_PREFLUSH,
or REQ_SYNC, losing these flags during a retry means the retried write might
only be placed in the disk's volatile cache.

Since bio_alloc_clone() already copies bi_opf from the master bio, overwriting
it here wipes out those flags, which could risk silent data corruption on power
loss if a transient failure triggers this retry path in narrow_write_error().

> +		/* 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 0a3cfdd3f5df8..f7ef903a3d4ec 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c

[ ... ]

> @@ -2831,6 +2831,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 similarly here in raid10
narrow_write_error(), does assigning a bare REQ_OP_WRITE silently drop flags
like REQ_FUA and REQ_PREFLUSH?

If an original write has these flags set, the retry might succeed without
them, leading the array to consider the disk perfectly synced while the data
only resides in the disk's volatile cache.

> +		/* 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/20260718162547.448892-1-mykola@meshstor.io?part=4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/6] md/raid1: don't use write-behind for P2PDMA bios
  2026-07-18 16:25 ` [PATCH 3/6] md/raid1: don't use write-behind for " Mykola Marzhan
@ 2026-07-18 16:58   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 16:58 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:
- [Critical] P2PDMA bios bypass write-behind but fail to serialize against outstanding write-behind I/O, leading to silent data corruption on write-mostly devices.
--

--- Patch [3]: [PATCH 3/6] md/raid1: don't use write-behind for P2PDMA bios ---
commit bbb98e00f3bb1bd5712cfce20170706425ddb589
Author: Mykola Marzhan <mykola@meshstor.io>

md/raid1: don't use write-behind for P2PDMA bios

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.

> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index afe2ca96ad8c2..57f64e8901024 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -1575,9 +1575,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 && rdev && test_bit(WriteMostly, &rdev->flags) &&
> +		    !md_bio_is_p2pdma(bio))
>  			write_behind = true;
>  

[Severity: Critical]
This is a pre-existing issue, but by excluding P2PDMA bios from
write-behind, does this expose them to silent data corruption due to
missing serialization checks?

When a normal write to a sector uses write-behind, it can be acknowledged
to the caller once fast devices complete, leaving the write-behind bio
in-flight to the write-mostly device.

If a subsequent P2PDMA write is issued to the same sector, this patch
explicitly disables write-behind for it. Because of this,
r1_bio->behind_master_bio remains NULL, and execution falls into the else
branch further down in raid1_write_request():

drivers/md/raid1.c:raid1_write_request() {
    ...
		if (r1_bio->behind_master_bio) {
			mbio = bio_alloc_clone(rdev->bdev,
					       r1_bio->behind_master_bio,
					       GFP_NOIO, &mddev->bio_set);
			if (test_bit(CollisionCheck, &rdev->flags))
				wait_for_serialization(rdev, r1_bio);
                ...
		} else {
			mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO,
					       &mddev->bio_set);

			if (test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
				wait_for_serialization(rdev, r1_bio);
		}
    ...
}

Because the else branch skips the CollisionCheck test,
wait_for_serialization() is bypassed (unless MD_SERIALIZE_POLICY is globally
set). Can the block layer then reorder these concurrent requests, overwriting
the newer P2PDMA data with the older in-flight write-behind data?

>  		r1_bio->bios[i] = NULL;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260718162547.448892-1-mykola@meshstor.io?part=3

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
  2026-07-18 16:25 ` [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
@ 2026-07-18 17:07   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 17:07 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: yukuai, linux-raid, linux-pci

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] `md_bio_p2pdma_mapping_error()` accesses `bio->bi_io_vec->bv_page` without a valid operation type guard, leading to a wild pointer dereference on data-less bios.
--

--- Patch [5]: [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures ---
commit 56153d6522aaf7c00c17b02d6bbccf2b3fb20978
Author: Mykola Marzhan <mykola@meshstor.io>

md/raid1,raid10: skip futile retries on P2PDMA mapping failures
    
This patch modifies raid1 and raid10 to explicitly handle P2PDMA mapping
failures (BLK_STS_TARGET). It avoids excessive per-block retries and
prevents healthy members from being evicted when peer memory cannot be
DMA-mapped.

> diff --git a/drivers/md/md.h b/drivers/md/md.h
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -40,6 +40,19 @@ static inline bool md_bio_is_p2pdma(struct bio *bio)
>  	       is_pci_p2pdma_page(bio->bi_io_vec->bv_page);
>  }
>  
> +/*
> + * True when a leg bio failed because its P2PDMA pages cannot be DMA-mapped
> + * to this member (BLK_STS_TARGET from blk_dma_map_iter_start()). Usable at
> + * completion time, unlike md_bio_is_p2pdma(): leg bios are clones sharing
> + * the master bio's bvec table, which outlives the leg's end_io, but their
> + * iterator is consumed by then, so bio_has_data() cannot gate the access.
> + */
> +static inline bool md_bio_p2pdma_mapping_error(struct bio *bio)
> +{
> +	return bio->bi_status == BLK_STS_TARGET && bio->bi_io_vec &&
> +	       is_pci_p2pdma_page(bio->bi_io_vec->bv_page);
> +}

[Severity: High]
Can this result in a wild pointer dereference for data-less bios?

Data-less bios (like REQ_OP_FLUSH) can legally be allocated with nr_vecs > 0,
such as when they are reused via bio_reset(). For these bios, bio->bi_io_vec
might be non-NULL while bv_page remains uninitialized.

If such a bio fails with BLK_STS_TARGET (for example, from an NVMe drive
returning NVME_SC_NS_NOT_READY), is it safe to access bv_page here and pass
it to is_pci_p2pdma_page() without a bio_has_data() guard?

[ ... ]

> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -2629,7 +2640,20 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
>  			 * errors.
>  			 */
>  			fail = true;
> -			narrow_write_error(r1_bio, m);
> +			if (md_bio_p2pdma_mapping_error(r1_bio->bios[m]))

[Severity: High]
Will this error path unconditionally pass failed cloned bios, including
data-less ones like REQ_OP_FLUSH, to the mapping check above?

> +				/*
> +				 * A P2PDMA mapping failure fails the whole
> +				 * range identically, so narrowing block by

[ ... ]

> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -2948,7 +2971,23 @@ 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 (md_bio_p2pdma_mapping_error(bio))

[Severity: High]
Similarly, does this RAID10 error path pass failed bios to the mapping
check without verifying if the operation actually carries data?

> +					/*
> +					 * A P2PDMA mapping failure fails
> +					 * the whole range identically, so
> +					 * narrowing block by block cannot

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260718162547.448892-1-mykola@meshstor.io?part=5

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers
  2026-07-18 16:25 ` [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
@ 2026-07-18 17:12   ` sashiko-bot
  0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-07-18 17:12 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] Data corruption and PI validation failure due to modifying integrity metadata after DMA mapping.

Pre-existing issues:
- [High] Use-After-Free of request and PDU on the -EIO error path.
--

--- Patch [6]: [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers ---
commit efc6b55af9178f85e847e999c50cc6efb530a8b9
Author: Mykola Marzhan <mykola@meshstor.io>

nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers

This patch addresses an issue where mapping failures for peer memory are
misreported as path errors instead of target errors. It changes DMA
mapping to use ib_dma_map_sgtable_attrs() to preserve error codes and maps
-EREMOTEIO to BLK_STS_TARGET. It also reorders nvme_start_request() to
execute after the mapping succeeds.

Cc: stable@vger.kernel.org # v7.1

> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
> index 6909e35427942..b8642cd2fb793 100644
> --- a/drivers/nvme/host/rdma.c
> +++ b/drivers/nvme/host/rdma.c
[ ... ]
> @@ -2039,11 +2038,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]
Does moving nvme_start_request() after nvme_rdma_map_data() cause Protection
Information (PI) validation failures?

nvme_start_request() calls blk_mq_start_request(), which eventually calls
blk_integrity_prepare() to insert reference tags into the metadata payload.

Since nvme_rdma_map_data() has already mapped the metadata for DMA
(potentially using SWIOTLB or syncing caches), wouldn't the device receive
stale metadata without the newly generated tags?

>  	sqe->cqe.done = nvme_rdma_send_done;
>  
>  	ib_dma_sync_single_for_device(dev, sqe->dma,
[ ... ]
> @@ -2063,6 +2064,13 @@ 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;
> +	/*
> +	 * The DMA layer refused to map peer memory to this device: a
> +	 * property of the pairing, not a path failure.  Match nvme-pci
> +	 * and do not retry (see blk_path_error()).
> +	 */
> +	else if (err == -EREMOTEIO)
> +		ret = BLK_STS_TARGET;
>  	else
>  		ret = BLK_STS_IOERR;
>  	nvme_cleanup_cmd(rq);

[Severity: High]
This isn't a bug introduced by this patch, but does the -EIO error path
trigger a use-after-free of rq and req?

If nvme_rdma_map_data() fails with -EIO, we call nvme_host_path_error(), which
synchronously completes and frees rq via blk_mq_end_request().

The code then falls through to nvme_cleanup_cmd(rq) and ib_dma_unmap_single(),
which access the freed rq and its PDU (req).

If the request was immediately re-allocated for another command, could this
read the wrong DMA address and unmap an active DMA mapping?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260718162547.448892-1-mykola@meshstor.io?part=6

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-07-18 17:12 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-18 16:25 [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
2026-07-18 16:25 ` [PATCH 1/6] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
2026-07-18 16:53   ` sashiko-bot
2026-07-18 16:25 ` [PATCH 2/6] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
2026-07-18 16:50   ` sashiko-bot
2026-07-18 16:25 ` [PATCH 3/6] md/raid1: don't use write-behind for " Mykola Marzhan
2026-07-18 16:58   ` sashiko-bot
2026-07-18 16:25 ` [PATCH 4/6] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
2026-07-18 16:58   ` sashiko-bot
2026-07-18 16:25 ` [PATCH 5/6] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
2026-07-18 17:07   ` sashiko-bot
2026-07-18 16:25 ` [PATCH 6/6] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers Mykola Marzhan
2026-07-18 17:12   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.