Linux RAID subsystem development
 help / color / mirror / Atom feed
* [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers
@ 2026-07-26 11:32 Mykola Marzhan
  2026-07-26 11:32 ` [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
                   ` (11 more replies)
  0 siblings, 12 replies; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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; v4 onward 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)
 11  nvme-rdma: ratelimit the map-failure error message (cleanup)
 12  nvme-rdma: factor out the scatterlist DMA mapping helper
     (cleanup)

Routing: patch 1 block, 2-8 md, 9-12 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).  11 and 12 are cleanup only and can be
deferred if you would rather not carry them at -rc.

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 v6:
 - patches 7, 9, 10: picked up Logan's Reviewed-by; on 7 it replaces
   his now-stale Signed-off-by, as he asked
 - patch 10: the map-failure ratelimit conversion moves out to its
   own patch 11 (Logan); the stable note now names both prerequisites
 - new patch 12: the data and the metadata mapping share a helper
   (Logan's suggestion on patch 9).  Kept out of 9 so the
   stable-tagged fix stays minimal

Changes in v5:
 - patch 7: From: is now Mykola, at Logan's request; patch 8 follows
   for consistency.  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

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/20260723204206.76930-1-mykola@meshstor.io/ [v5]
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 (10):
  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
  nvme-rdma: ratelimit the map-failure error message
  nvme-rdma: factor out the scatterlist DMA mapping helper

 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  | 54 +++++++++++++++++++++++++++------------
 include/linux/blk_types.h | 10 ++++++++
 9 files changed, 134 insertions(+), 51 deletions(-)


base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f
-- 
2.52.0


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

* [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:06   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
  2026-07-26 11:32 ` [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 11:59   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
  2026-07-26 11:32 ` [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
  2026-07-26 11:32 ` [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:10   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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.

An rdev hot-added under serialize_policy gets no serial tree at all,
because mddev_create_serial_pool() skips one that is not write-behind
eligible: the old gate dereferences NULL there, this one skips
serialization instead.  Martin Wilck's fix repairs that case at the
source, after which CollisionCheck is a strict superset of
MD_SERIALIZE_POLICY.

Link: https://lore.kernel.org/linux-raid/20260723112741.1206836-1-mwilck@suse.com/
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] 25+ messages in thread

* [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (2 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:01   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (3 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 11:57   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (4 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:09   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 07/12] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 07/12] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (5 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:03   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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.  raid10
replacement legs are unchanged: a write error there fails the
replacement, whatever the status.

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: Mykola Marzhan <mykola@meshstor.io>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
 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] 25+ messages in thread

* [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (6 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 07/12] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:06   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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] 25+ messages in thread

* [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs()
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (7 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:01   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
 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] 25+ messages in thread

* [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (8 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:10   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message Mykola Marzhan
  2026-07-26 11:32 ` [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper Mykola Marzhan
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

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.

Start the request only after the mapping succeeds, as nvme-pci and
nvme-tcp do.  A hard status from ->queue_rq is completed by blk-mq
itself, skipping nvme_mpath_end_request(), so the multipath I/O
accounting leaks -- today for -EINVAL, and now for BLK_STS_P2PDMA.

Fixes: 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA transport")
Cc: stable@vger.kernel.org # v7.1: requires BLK_STS_P2PDMA and the ib_dma_map_sgtable_attrs() conversion
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
 drivers/nvme/host/rdma.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 63830334c73e..c38d949502b7 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 ||
@@ -2051,6 +2045,8 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
 		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] 25+ messages in thread

* [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (9 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 11:53   ` sashiko-bot
  2026-07-26 11:32 ` [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper Mykola Marzhan
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

A mapping failure is per-request and, for an unroutable P2P transfer,
deterministic: a workload that keeps issuing one turns this into a
log line per I/O.  Ratelimit it, as nvme's per-I/O error logging
(nvme_log_error()) already is.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
 drivers/nvme/host/rdma.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index c38d949502b7..2f50509a7a61 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -2040,8 +2040,8 @@ 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;
 	}
 
-- 
2.52.0


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

* [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper
  2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
                   ` (10 preceding siblings ...)
  2026-07-26 11:32 ` [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message Mykola Marzhan
@ 2026-07-26 11:32 ` Mykola Marzhan
  2026-07-26 12:02   ` sashiko-bot
  11 siblings, 1 reply; 25+ messages in thread
From: Mykola Marzhan @ 2026-07-26 11:32 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,
	Thorsten Leemhuis, Martin Wilck

The data and the metadata mapping do the same thing: describe the
segments blk_rq_map_sg() produced in a local sg_table -- the embedded
one's orig_nents is the allocated size, not the valid-segment count --
map that, and hand back the DMA-mapped count.  Fold both into a
helper.

No functional change.

Suggested-by: Logan Gunthorpe <logang@deltatee.com>
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@meshstor.io>
---
 drivers/nvme/host/rdma.c | 42 +++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 2f50509a7a61..525e5aea65a1 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1465,11 +1465,33 @@ static int nvme_rdma_map_sg_pi(struct nvme_rdma_queue *queue,
 	return -EINVAL;
 }
 
+/*
+ * Map the segments blk_rq_map_sg() produced.  They are described by a local
+ * sg_table because the embedded one's orig_nents is the allocated size, not
+ * the number of valid segments; rdma_rw_ctx_init() does the same.
+ */
+static int nvme_rdma_dma_map_sgl(struct ib_device *ibdev,
+		struct scatterlist *sgl, int nents,
+		enum dma_data_direction dir, int *count)
+{
+	struct sg_table sgt = {
+		.sgl		= sgl,
+		.orig_nents	= nents,
+	};
+	int ret;
+
+	ret = ib_dma_map_sgtable_attrs(ibdev, &sgt, dir, 0);
+	if (unlikely(ret))
+		return ret;
+
+	*count = sgt.nents;
+	return 0;
+}
+
 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);
@@ -1481,14 +1503,10 @@ 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);
 
-	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);
+	ret = nvme_rdma_dma_map_sgl(ibdev, req->data_sgl.sg_table.sgl,
+				    req->data_sgl.nents, rq_dma_dir(rq), count);
 	if (unlikely(ret))
 		goto out_free_table;
-	*count = sgt.nents;
 
 	if (blk_integrity_rq(rq)) {
 		req->metadata_sgl->sg_table.sgl =
@@ -1504,14 +1522,12 @@ 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);
-		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);
+		ret = nvme_rdma_dma_map_sgl(ibdev,
+					    req->metadata_sgl->sg_table.sgl,
+					    req->metadata_sgl->nents,
+					    rq_dma_dir(rq), pi_count);
 		if (unlikely(ret))
 			goto out_free_pi_table;
-		*pi_count = sgt.nents;
 	}
 
 	return 0;
-- 
2.52.0


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

* Re: [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message
  2026-07-26 11:32 ` [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message Mykola Marzhan
@ 2026-07-26 11:53   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 11:53 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: yukuai, linux-pci, linux-raid

> A mapping failure is per-request and, for an unroutable P2P transfer,
> deterministic: a workload that keeps issuing one turns this into a
> log line per I/O.  Ratelimit it, as nvme's per-I/O error logging
> (nvme_log_error()) already is.
> 
> Assisted-by: Claude:claude-fable-5
> 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/20260726113229.2154269-1-mykola@meshstor.io?part=11


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

* Re: [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper
  2026-07-26 11:32 ` [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
@ 2026-07-26 11:57   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 11:57 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/20260726113229.2154269-1-mykola@meshstor.io?part=5


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

* Re: [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios
  2026-07-26 11:32 ` [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
@ 2026-07-26 11:59   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 11:59 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/20260726113229.2154269-1-mykola@meshstor.io?part=2


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

* Re: [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios
  2026-07-26 11:32 ` [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
@ 2026-07-26 12:01   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:01 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-raid, yukuai, 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>

Sashiko has reviewed this patch and found no issues. It looks great!

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


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

* Re: [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs()
  2026-07-26 11:32 ` [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
@ 2026-07-26 12:01   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:01 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-pci, linux-raid, yukuai

> 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/20260726113229.2154269-1-mykola@meshstor.io?part=9


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

* Re: [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper
  2026-07-26 11:32 ` [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper Mykola Marzhan
@ 2026-07-26 12:02   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:02 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci

> The data and the metadata mapping do the same thing: describe the
> segments blk_rq_map_sg() produced in a local sg_table -- the embedded
> one's orig_nents is the allocated size, not the valid-segment count --
> map that, and hand back the DMA-mapped count.  Fold both into a
> helper.
> 
> No functional change.
> 
> Suggested-by: Logan Gunthorpe <logang@deltatee.com>
> Assisted-by: Claude:claude-fable-5
> 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/20260726113229.2154269-1-mykola@meshstor.io?part=12


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

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

> 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.  raid10
> replacement legs are unchanged: a write error there fails the
> replacement, whatever the status.
> 
> 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: Mykola Marzhan <mykola@meshstor.io>

Sashiko has reviewed this patch and found no issues. It looks great!

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


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

* Re: [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
  2026-07-26 11:32 ` [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
@ 2026-07-26 12:06   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:06 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] BLK_STS_INVAL is omitted from the non-retryable list in blk_path_error(), causing genuine invalid requests to trigger infinite path-flapping loops in dm-mpath.
--

--- Patch [1]: [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers ---
commit d0a7b922115b23f3d33b00adcd1560732559cd9f
Author: Logan Gunthorpe <logang@deltatee.com>

block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers

This commit introduces BLK_STS_P2PDMA to handle unsupported peer-to-peer DMA 
transfers that cannot be routed by the PCIe topology. It returns this new 
status from blk_dma_map_iter_start() instead of BLK_STS_INVAL to distinguish 
it from standard invalid requests. It also adds BLK_STS_P2PDMA to 
blk_path_error() so multipath does not infinitely retry these failures.

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

> diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> index 8808ee76e73c0..c8de7809320ad 100644
> --- a/include/linux/blk_types.h
> +++ b/include/linux/blk_types.h

[ ... ]

> @@ -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:

[Severity: High]
This is a pre-existing issue, but should BLK_STS_INVAL also be included in
this non-retryable list?

Currently, because BLK_STS_INVAL is omitted from blk_path_error(), it falls
through to the default case and returns true (treating it as a retryable
path failure).

If a user submits an inherently invalid I/O request (such as an atomic write
exceeding hardware limits via RWF_ATOMIC), the block layer correctly rejects
it and returns BLK_STS_INVAL.

However, dm-mpath will incorrectly treat this user error as a path failure,
fail the active path, and requeue the I/O. Since the request is genuinely
invalid, it fails on all paths, causing an infinite path-flapping loop as
the path checker periodically reinstates them.

Could this lead to a Denial of Service for all legitimate I/O on the
affected multipath device?

>  		return false;
>  	}
>  
>  	/* Anything else could be a path failure, so should be retried */
>  	return true;
>  }

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

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

* Re: [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
  2026-07-26 11:32 ` [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
@ 2026-07-26 12:06   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:06 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-raid, linux-pci, yukuai

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

Pre-existing issues:
- [High] A pre-existing ABBA deadlock can occur if a read error is processed by `raid1d` or `raid10d` concurrently with a management thread calling `freeze_array(conf, 0)`.
--

--- Patch [8]: [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA ---
commit f4692375c384d52b343af556c8a63ea0e3e48c0f
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.
This skips the read-error machinery to avoid evicting a healthy member
under a P2P workload.

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

[Severity: High]
This isn't a bug introduced by this patch, but does extending this condition
expose a pre-existing ABBA deadlock to P2PDMA failures?

If a management thread concurrently calls freeze_array(conf, 0) (setting
conf->array_frozen = 1 and waiting for nr_pending to drop to 0), and
raid1d takes this branch, it skips calling freeze_array(conf, 1) and
unfreeze_array(conf).

Later in handle_read_error(), the daemon thread invokes
raid1_read_request(), which blocks on wait_read_barrier() waiting for
conf->array_frozen to be cleared.

The management thread is blocked waiting for the daemon to drop its
nr_pending count, while the daemon is blocked waiting for the management
thread to unfreeze the array. Can this cause both threads to hang
indefinitely?

>  		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 77c7d8a599160..462f622556ed0 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c

[ ... ]

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

[Severity: High]
This isn't a bug introduced by this patch, but does this branch suffer from
a similar pre-existing deadlock in raid10?

If the daemon thread takes this path, it skips the freeze/unfreeze cycle
and later calls raid10_read_request(). This blocks on wait_barrier(),
waiting for the concurrent management freeze_array(conf, 0) to clear,
creating the same deadlock between the daemon and management thread.

>  		r10_bio->devs[slot].bio = IO_BLOCKED;
>  	else if (!test_bit(FailFast, &rdev->flags)) {
>  		freeze_array(conf, 1);

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

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

* Re: [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
  2026-07-26 11:32 ` [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
@ 2026-07-26 12:09   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:09 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci

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

Pre-existing issues:
- [High] Unconditional assignment to `wbio->bi_opf` destroys all request flags cloned from the master bio, leading to potential data corruption.
--

--- Patch [6]: [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones ---
commit 333e1430f8770afc53cf111be8c36899e6c4c5e3
Author: Mykola Marzhan <mykola@meshstor.io>

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

This commit restores REQ_NOMERGE on P2PDMA retry clones in
narrow_write_error() to prevent member queues from improperly merging
segments.

> 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 isn't a bug introduced by this patch, but does this assignment
unintentionally overwrite all request flags?

Since bio_alloc_clone() copies the original bi_opf, setting it directly to
REQ_OP_WRITE zeroes out the upper 24 flag bits, which drops flags like
REQ_FUA and REQ_PREFLUSH.

Could this result in data corruption on power loss if the original write
requested forced unit access?

> +		/* 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 is the same pre-existing issue as in raid1.c. Does overwriting bi_opf
drop critical data integrity flags like REQ_FUA cloned from the master bio?

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

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

* Re: [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
  2026-07-26 11:32 ` [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
@ 2026-07-26 12:10   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:10 UTC (permalink / raw)
  To: Mykola Marzhan; +Cc: linux-raid, linux-pci, yukuai

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

New issues:
- [High] DMA API violation causing stale PI metadata (Data Corruption).

Pre-existing issues:
- [High] Incomplete fix: multipath I/O accounting leak on nvme_rdma_post_send() failure.
--

--- Patch [10]: [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers ---
commit d3d8febf3662843e41333a6adc3c3ea1d88ff373
Author: Mykola Marzhan <mykola@meshstor.io>

nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers

This commit propagates -EREMOTEIO from ib_dma_map_sgtable_attrs() as BLK_STS_P2PDMA
to prevent infinite multipath requeues. It also delays nvme_start_request() until
after data mapping succeeds to avoid multipath accounting leaks on mapping errors.

Cc: stable@vger.kernel.org # v7.1: requires BLK_STS_P2PDMA and the ib_dma_map_sgtable_attrs() conversion

> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
> index 63830334c73e2..c38d949502b7c 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 ||
> @@ -2051,6 +2045,8 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
>  		goto err;
>  	}
>  
> +	nvme_start_request(rq);
> +

[Severity: High]
Does moving nvme_start_request() after nvme_rdma_map_data() violate the DMA API?

nvme_start_request() calls blk_mq_start_request(), which invokes
blk_integrity_prepare(). This modifies the PI metadata buffers in CPU memory
to update reference tags.

Because nvme_rdma_map_data() has already mapped these buffers for DMA
(DMA_TO_DEVICE), could CPU writes happening after the map operation remain
unsynced on non-coherent architectures or when bounce buffers are used?

This might cause the device to read stale PI metadata, leading to PI
verification failures or data corruption.

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

[Severity: High]
This is a pre-existing issue, but I noticed a potential multipath I/O
accounting leak if nvme_rdma_post_send() fails.

The commit message notes that starting the request before a failure causes
multipath accounting leaks when blk-mq hard-completes the request directly.

If nvme_rdma_post_send() fails (e.g., returns -EINVAL), execution jumps to
err_unmap and falls through to the err: block, returning a hard error status
like BLK_STS_IOERR:

	err = nvme_rdma_post_send(queue, sqe, ...);
	if (unlikely(err))
		goto err_unmap;
	...
err:
	...
	else
		ret = BLK_STS_IOERR;

Since nvme_start_request() was already called, will blk-mq hard-complete the
request directly, bypassing the driver's nvme_mpath_end_request() and leaking
multipath accounting like bd_stats->in_flight and nr_active?

This could permanently block block device teardown.

>  	nvme_cleanup_cmd(rq);

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

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

* Re: [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
  2026-07-26 11:32 ` [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
@ 2026-07-26 12:10   ` sashiko-bot
  0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-26 12:10 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] A Time-of-Check to Time-of-Use (TOCTOU) race condition exists during concurrent device removal, leading to a NULL pointer dereference in both the read and write I/O submission paths.
--

--- Patch [3]: [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs ---
commit c5339979d08defedb52fda8881d721fd1180c172
Author: Mykola Marzhan <mykola@meshstor.io>
    
md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
    
This commit serializes non-write-behind writes on CollisionCheck rdevs
to prevent older behind data from landing last and leaving stale data.
It replaces the previous MD_SERIALIZE_POLICY check with CollisionCheck,
which properly accounts for write-mostly members.

Link: https://lore.kernel.org/linux-raid/20260723112741.1206836-1-mwilck@suse.com/

> 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

[ ... ]

> @@ -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);
>  

[Severity: High]
This is a pre-existing issue, but can this second loop in
raid1_write_request() encounter a NULL pointer dereference during
concurrent device removal?

The loop re-fetches rdev from conf->mirrors[i].rdev which might have
been set to NULL by a concurrent raid1_remove_conf(). If
raid1_remove_conf() executes after the first loop incremented
nr_pending on the old pointer, re-fetching it here and accessing
rdev->bdev in bio_alloc_clone() could trigger a kernel panic.

A similar issue appears to exist in the read path where
update_read_sectors() implicitly re-fetches info->rdev after an active
disk is selected in choose_best_rdev(), potentially crashing on
atomic_inc(&info->rdev->nr_pending).

> -			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);
>  		}

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

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

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

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-26 11:32 [PATCH v6 00/12] block,md,nvme: correct handling of unsupported P2PDMA transfers Mykola Marzhan
2026-07-26 11:32 ` [PATCH v6 01/12] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers Mykola Marzhan
2026-07-26 12:06   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 02/12] md: ensure REQ_NOMERGE is set on P2PDMA bios Mykola Marzhan
2026-07-26 11:59   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 03/12] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs Mykola Marzhan
2026-07-26 12:10   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 04/12] md/raid1: don't use write-behind for P2PDMA bios Mykola Marzhan
2026-07-26 12:01   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 05/12] md/raid1,raid10: factor out raid1_write_error() helper Mykola Marzhan
2026-07-26 11:57   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 06/12] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones Mykola Marzhan
2026-07-26 12:09   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 07/12] md/raid1,raid10: skip futile retries on P2PDMA mapping failures Mykola Marzhan
2026-07-26 12:03   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 08/12] md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA Mykola Marzhan
2026-07-26 12:06   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 09/12] nvme-rdma: use ib_dma_map_sgtable_attrs() Mykola Marzhan
2026-07-26 12:01   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 10/12] nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers Mykola Marzhan
2026-07-26 12:10   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 11/12] nvme-rdma: ratelimit the map-failure error message Mykola Marzhan
2026-07-26 11:53   ` sashiko-bot
2026-07-26 11:32 ` [PATCH v6 12/12] nvme-rdma: factor out the scatterlist DMA mapping helper Mykola Marzhan
2026-07-26 12:02   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox