* [PATCH v4 2/9] md: ensure REQ_NOMERGE is set on P2PDMA bios
From: Mykola Marzhan @ 2026-07-22 18:58 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260722185841.449934-1-mykola@meshstor.io>
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
* [PATCH v4 1/9] block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
From: Mykola Marzhan @ 2026-07-22 18:58 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260722185841.449934-1-mykola@meshstor.io>
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
* [PATCH v4 0/9] block,md,nvme: correct handling of unsupported P2PDMA transfers
From: Mykola Marzhan @ 2026-07-22 18:58 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, linux-kernel, linux-rdma, linux-pci
md treats an unsupported P2PDMA transfer to a member as success, and
nvme-rdma retries one forever. Whether peer memory can be DMA-mapped
depends on the PCIe topology between the two devices, and since v6.17
the failure completes as BLK_STS_INVAL: retryable for multipath and
deliberately ignored by md for member failures, so mirrors silently
diverge.
v1-v3 restored BLK_STS_TARGET for these failures and tagged P2PDMA
bios in md. Logan Gunthorpe proposed a dedicated BLK_STS_P2PDMA
instead, so no consumer needs to know whether an I/O was P2P, and
wrote the core patches; v4 is built on them. Patches 1, 5, 7 and 8
are his and keep his authorship (7 and 8 with completion-handler
additions).
1 block: add BLK_STS_P2PDMA (stable, v6.17)
2 md: keep REQ_NOMERGE 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: return BLK_STS_P2PDMA (stable, v7.1; needs patch 1)
Routing: patch 1 block, 2-8 md, 9 nvme; 7-9 use the status patch 1
adds. Patch 1 alone already fixes the divergence (md stops ignoring
the failure) and the multipath retry storm; the later patches only
refine the fallout, so the series bisects safely. The md patches
fix a v7.2-rc1 regression (02666132403a) and carry no stable tags.
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 v4:
- new BLK_STS_P2PDMA (Logan) replaces restoring BLK_STS_TARGET; the
R1BIO_P2PDMA/R10BIO_P2PDMA state bits are gone and the md changes
shrink to completion-time status tests
- blk_dma_map_iter_start() gets the explicit
PCI_P2PDMA_MAP_NOT_SUPPORTED case (Logan's patch; v3 kept
default: only)
- write completions record the whole failed range as bad blocks
instead of narrow_write_error()'s per-block retries; v3's coarse
retry is gone -- the new status is deterministic, nothing to
re-attempt
- read path: bi_status is evaluated before bio_put()
Link: https://lore.kernel.org/linux-raid/20260718162547.448892-1-mykola@meshstor.io/ [v1]
Link: https://lore.kernel.org/linux-raid/20260719105327.864949-1-mykola@meshstor.io/ [v2]
Link: https://lore.kernel.org/linux-raid/20260721174502.111503-1-mykola@meshstor.io/ [v3]
Link: https://lore.kernel.org/linux-raid/9f846c85-f427-433a-b87d-8e9e3a4406e7@deltatee.com/ [Logan's proposal]
Logan Gunthorpe (4):
block: add BLK_STS_P2PDMA for unsupported peer-to-peer transfers
md/raid1,raid10: factor out raid1_write_error() helper
md/raid1,raid10: skip futile retries on P2PDMA mapping failures
md/raid1,raid10: set IO_BLOCKED in case of BLK_STS_P2PDMA
Mykola Marzhan (5):
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
nvme-rdma: return BLK_STS_P2PDMA for unsupported P2P transfers
block/blk-core.c | 3 +++
block/blk-mq-dma.c | 3 ++-
drivers/md/md.c | 10 ++++++--
drivers/md/md.h | 15 ++++++++++++
drivers/md/raid1-10.c | 16 +++++++++++++
drivers/md/raid1.c | 48 +++++++++++++++++++++++----------------
drivers/md/raid10.c | 26 ++++++++++++---------
drivers/nvme/host/rdma.c | 38 ++++++++++++++++++-------------
include/linux/blk_types.h | 10 ++++++++
9 files changed, 120 insertions(+), 49 deletions(-)
base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f
--
2.52.0
^ permalink raw reply
* Re: [PATCH v2 6/7] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
From: Leon Romanovsky @ 2026-07-22 8:30 UTC (permalink / raw)
To: Logan Gunthorpe
Cc: Mykola Marzhan, Jens Axboe, Song Liu, Yu Kuai, Keith Busch,
Christoph Hellwig, Sagi Grimberg, linux-block, linux-raid,
linux-nvme, Li Nan, Xiao Ni, Guoqing Jiang, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Bjorn Helgaas,
Shivaji Kant, Pranjal Shrivastava, Henrique Carvalho,
linux-kernel, linux-rdma, linux-pci
In-Reply-To: <9f846c85-f427-433a-b87d-8e9e3a4406e7@deltatee.com>
On Tue, Jul 21, 2026 at 05:38:53PM -0600, Logan Gunthorpe wrote:
>
>
> On 2026-07-21 11:50, Mykola Marzhan wrote:
> > I have split it in v3 into a raid1 patch and a raid10 patch.
> > I am not sure about separate "introduce the bit" patches.
> > R1BIO_P2PDMA and R10BIO_P2PDMA are entries in the two personalities'
> > with own r*bio state enums.
> > The commit messages are shorter now.
>
> That helps a bit, thanks.
>
>
> > An early return doesn't work here: on a mapping failure the
> > function must still fall through to set R1BIO_WriteError, so the
> > completion path runs the (single, whole-range) retry and records
> > the bad range -- only WantReplacement and the FailFast md_error()
> > are skipped. v3 folds the two tests into one branch now,
> > which reads better and keeps the fall-through. Please check v3.
>
> The early return works if you create a helper.
>
> > I compared the two sides after splitting: the P2PDMA handling is
> > symmetric, and the remaining differences are pre-existing
> > personality differences rather than missed fixes.
> > A shared helper would be heavier than the two open-coded branches,
> > so I kept it per-personality.
>
> Yeah, I still think it's a bit of a mess so I dug into it a bit more
> myself.
>
> I realized the real problem in this patch set was reusing
> BLK_STS_TARGET. I think I may have used that a long time ago and those
> paths have since been removed. Using it here doesn't quite make sense
> because we end up with those awkward checks (is P2PDMA enabled *and* the
> error is a BLK_STS_TARGET). I don't think that can ever really be
> correct here seeing something could, in theory return BLK_STS_TARGET for
> another reason even when P2PDMA is enabled. So that should be cleaned up.
I had thoughts about this series too. It seems best to avoid
the P2P path entirely unless all legs support P2P.
Can the md/raid code determine P2P support in advance?
Thanks
^ permalink raw reply
* Re: [PATCH] md/raid10: honor REQ_NOWAIT in wait_blocked_dev()
From: sashiko-bot @ 2026-07-22 7:53 UTC (permalink / raw)
To: dayou5941; +Cc: linux-raid, yukuai
In-Reply-To: <20260722072645.2714771-1-dayou5941@163.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Dropping the barrier in `wait_blocked_dev()` allows concurrent reshape to invalidate the physical mapping, leading to data corruption.
- [High] Pre-existing memory leak of `r10_bio` in `raid10_write_request()` when returning early due to `REQ_NOWAIT`.
- [High] `wait_blocked_dev()` incorrectly checks the first `conf->copies` physical disks for blocked state, rather than the dynamically calculated target disks.
- [High] Allocation of `r10_bio` unconditionally sleeps, violating `REQ_NOWAIT` semantics.
- [High] TOCTOU Use-After-Free of `md_rdev` due to missing `rcu_read_lock()` in `raid10_write_request()` and `wait_blocked_dev()`.
--
--- Patch [1]: [PATCH] md/raid10: honor REQ_NOWAIT in wait_blocked_dev() ---
commit 4ef20896ea5200b8191d2a0afa7c08883eacb0e6
Author: liyouhong <liyouhong@kylinos.cn>
md/raid10: honor REQ_NOWAIT in wait_blocked_dev()
The patch modifies wait_blocked_dev() in the raid10 driver to honor the
REQ_NOWAIT flag. It returns false immediately if a blocked device is
encountered during a nowait write request, instead of sleeping. The write
path then releases the barrier, frees the bio, and returns a wouldblock error.
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 0a3cfdd3f5df8..2ccca42e07029 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -1293,7 +1293,8 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
> }
> }
>
> -static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
> +static bool wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio,
> + bool nowait)
> {
[Severity: High]
This isn't a bug introduced by this patch, but how does wait_blocked_dev()
identify the correct mapped target disks?
The loop examines conf->mirrors[i] instead of using the dynamically
calculated target disk indices:
wait_blocked_dev() {
...
for (i = 0; i < conf->copies; i++) {
struct md_rdev *rdev, *rrdev;
rdev = conf->mirrors[i].rdev;
...
}
Might this loop hardcode the check for the first conf->copies physical
disks, ignoring the actual target disks stored in r10_bio->devs[i].devnum
by raid10_find_phys()? This could risk bypassing blocked checks for disks
that the IO will actually touch.
> struct r10conf *conf = mddev->private;
> struct md_rdev *blocked_rdev;
[ ... ]
> @@ -1323,6 +1324,8 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
> set_bit(BlockedBadBlocks, &rdev->flags);
>
> if (rdev_blocked(rdev)) {
> + if (nowait)
> + return false;
> blocked_rdev = rdev;
> atomic_inc(&rdev->nr_pending);
[Severity: High]
This is a pre-existing issue, but is there a use-after-free risk for
md_rdev here?
Because atomic_inc(&rdev->nr_pending) is executed without holding
rcu_read_lock(), could a concurrent raid10_remove_disk() check the
pending count, find it zero, and call md_kick_rdev_from_array(),
freeing the device before the increment occurs?
A similar pattern happens in raid10_write_request() where
atomic_inc(&rdev->nr_pending) is also called without rcu_read_lock()
protection.
> break;
> }
[ ... ]
> @@ -1347,6 +1352,7 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
> wait_barrier(conf, false);
[Severity: Critical]
This isn't a bug introduced by this patch, but can dropping the barrier
allow a concurrent reshape to invalidate the physical mapping?
When a blocked device is found, the synchronization barrier is dropped:
wait_blocked_dev() {
...
allow_barrier(conf);
md_wait_for_blocked_rdev(blocked_rdev, mddev);
wait_barrier(conf, false);
...
}
Since raid10_find_phys() was already called before wait_blocked_dev(),
could a waiting reshape thread freeze the array, move data, and advance
reshape_progress while the barrier is dropped? If that happens,
raid10_write_request() might use a stale physical mapping in
r10_bio->devs to submit the IO after the barrier is re-acquired.
> goto retry_wait;
> }
> + return true;
> }
>
> static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
> @@ -1421,7 +1427,12 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
> r10_bio->read_slot = -1; /* make sure repl_bio gets freed */
> raid10_find_phys(conf, r10_bio);
>
> - wait_blocked_dev(mddev, r10_bio);
> + if (!wait_blocked_dev(mddev, r10_bio, bio->bi_opf & REQ_NOWAIT)) {
[Severity: High]
This is a pre-existing issue, but are we leaking the r10_bio structure
on early returns in raid10_write_request()?
When REQ_NOWAIT is set and the IO overlaps with a clustered resync or
reshape, the function correctly bails out early, for example:
raid10_write_request() {
...
if (bio->bi_opf & REQ_NOWAIT) {
allow_barrier(conf);
bio_wouldblock_error(bio);
return false;
}
...
}
If the function returns false without calling free_r10bio(r10_bio), will
this permanently leak the slab memory allocated for r10_bio?
[Severity: High]
This isn't a bug introduced by this patch, but would the initial allocation
of r10_bio violate REQ_NOWAIT semantics?
Before reaching the newly added nowait logic, the caller (__make_request)
unconditionally allocates r10_bio using GFP_NOIO:
__make_request() {
...
r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
...
}
Since GFP_NOIO allows direct reclaim, could this allocation sleep if the
mempool is exhausted, regardless of whether bio->bi_opf & REQ_NOWAIT
is set?
> + allow_barrier(conf);
> + free_r10bio(r10_bio);
> + bio_wouldblock_error(bio);
> + return false;
> + }
>
> max_sectors = r10_bio->sectors;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260722072645.2714771-1-dayou5941@163.com?part=1
^ permalink raw reply
* [PATCH] md/raid10: honor REQ_NOWAIT in wait_blocked_dev()
From: dayou5941 @ 2026-07-22 7:26 UTC (permalink / raw)
To: song, yukuai; +Cc: linux-raid, liyouhong
From: liyouhong <liyouhong@kylinos.cn>
raid10_write_request() already respects REQ_NOWAIT for barrier and reshape
waits, but wait_blocked_dev() still always sleeps in
md_wait_for_blocked_rdev() when a member is Blocked / BlockedBadBlocks.
A NOWAIT write can therefore block in the submit path despite the flag.
Mirror raid1's wait_blocked_rdev(): if nowait is set and an rdev is
blocked, return false without waiting. The write path releases the
barrier, frees r10_bio, and completes the bio with wouldblock.
Discard still calls wait_blocked_dev(..., false). REQ_NOWAIT for discard is
already handled at wait_barrier(); honoring it again in wait_blocked_dev()
is a separate, harder-to-hit path (e.g. io_uring discard) and is left for
a follow-up to keep this fix focused on the reproduced write hang.
Fixes: c9aa889b035f ("md: raid10 add nowait support")
Signed-off-by: liyouhong <liyouhong@kylinos.cn>
---
drivers/md/raid10.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 0a3cfdd3f5df..2ccca42e0702 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1293,7 +1293,8 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
}
}
-static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
+static bool wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio,
+ bool nowait)
{
struct r10conf *conf = mddev->private;
struct md_rdev *blocked_rdev;
@@ -1323,6 +1324,8 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
set_bit(BlockedBadBlocks, &rdev->flags);
if (rdev_blocked(rdev)) {
+ if (nowait)
+ return false;
blocked_rdev = rdev;
atomic_inc(&rdev->nr_pending);
break;
@@ -1331,6 +1334,8 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
rrdev = conf->mirrors[i].replacement;
if (rrdev && rdev_blocked(rrdev)) {
+ if (nowait)
+ return false;
atomic_inc(&rrdev->nr_pending);
blocked_rdev = rrdev;
break;
@@ -1347,6 +1352,7 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio)
wait_barrier(conf, false);
goto retry_wait;
}
+ return true;
}
static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
@@ -1421,7 +1427,12 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
r10_bio->read_slot = -1; /* make sure repl_bio gets freed */
raid10_find_phys(conf, r10_bio);
- wait_blocked_dev(mddev, r10_bio);
+ if (!wait_blocked_dev(mddev, r10_bio, bio->bi_opf & REQ_NOWAIT)) {
+ allow_barrier(conf);
+ free_r10bio(r10_bio);
+ bio_wouldblock_error(bio);
+ return false;
+ }
max_sectors = r10_bio->sectors;
@@ -1748,7 +1759,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
r10_bio->sectors = 0;
r10_bio->read_slot = -1;
memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
- wait_blocked_dev(mddev, r10_bio);
+ wait_blocked_dev(mddev, r10_bio, false);
/*
* For far layout it needs more than one r10bio to cover all regions.
^ permalink raw reply related
* Re: [PATCH] md: fix soft lockup during resync when sync is repeatedly skipped
From: yu kuai @ 2026-07-22 4:00 UTC (permalink / raw)
To: Yunye Zhao, Song Liu, yu kuai
Cc: Li Nan, Xiao Ni, Joseph Qi, linux-raid, linux-kernel
In-Reply-To: <20260720062026.19245-1-yunye.zhao@linux.alibaba.com>
Hi,
在 2026/7/20 14:20, Yunye Zhao 写道:
> Hi Kuai,
>
> On 2026/7/17 14:27, Yu Kuai wrote:
>>> md_do_sync()'s main loop advances io_sectors only when I/O is actually
>>> issued (skipped == 0). When sync_request() keeps returning skipped == 1,
>>> io_sectors never increases, [...]
>> That's not expected, io_sectors should always increase in the skip case.
> Sorry, my description was not accurate. The problem is not that io_sectors
> stays 0. md_do_sync()'s loop exit condition is j == max_sectors, and in
> raid10_sync_request()'s recovery path sectors is always 128, so for a very
> large max_sectors the loop iterates a huge number of times.
>
> raid10_sync_request(), recovery branch:
>
> max_sync = RESYNC_PAGES << (PAGE_SHIFT-9); /* 128 */
> must_sync = md_bitmap_start_sync(mddev, sect, &sync_blocks, true);
> if (sync_blocks < max_sync) /* sync_blocks huge, never true */
> max_sync = sync_blocks; /* so max_sync stays 128 */
> ...
> if (biolist == NULL) {
> *skipped = 1;
> return max_sync; /* 128; the large span is discarded */
> }
>
> Back in md_do_sync()'s main loop, j then crawls forward 128 sectors per
> call until it reaches max_sectors:
>
> while (j < max_sectors) {
> sectors = mddev->pers->sync_request(mddev, j, max_sectors, &skipped);
> if (!skipped) /* skipped == 1 -> io_sectors stays 0 */
> io_sectors += sectors;
> j += sectors; /* j += 128 only */
> if (last_check + window > io_sectors || j == max_sectors)
> continue;
> }
>
> From the vmcore:
>
> sync_blocks returned = 0x5ED03680 (~1.59e8 sectors), clamped to 128
> recovery max_sectors = dev_sectors = 2^40
> iterations = 2^40 / 128 = 2^33 (~8.6e9)
Please update and fix the commit message "io_sectors never increases", this
really is misleading.
>
>> What kernel version you're testing?
> 6.6.102. I also tested mainline and hit the same problem.
>
>> If this is latest kernel, bitmap_start_sync() need to be fixed. It can't
>> return skip while setting skipping sectors to 0.
> bitmap_start_sync() is actually fine -- it returns a large clean span
> (0x5ED03680 above). The recovery path just discards it: max_sync is only
> ever clamped down, so it returns 128 regardless.
>
>> And since this is dead loop, a cond_resched() will not fix anything.
> Agreed -- cond_resched() only stops the watchdog; the thread still spins
> ~8.6e9 no-op iterations and pins a CPU for ~313s.
>
> For v2 I'd fix this at the source: make the recovery path honour the clean
> span reported by the bitmap instead of capping the skip at 128. Does that
> direction look right to you?
This patch is still valuable, even a 5s lockup is not acceptable.
And I think this is not a problem for llbitmap, it skips one bit at a time and
bits are limited to at most 4k*128. Perhaps bitmap can do this as well, it can
definably return much bigger skipping sectors from bitmap_start_sync().
>
> Thanks,
> Yunye
--
Thanks,
Kuai
^ permalink raw reply
* Re: [PATCH] md: do overflow check for sb->bblog_shift in super_1_load()
From: yu kuai @ 2026-07-22 2:29 UTC (permalink / raw)
To: colyli, yukuai; +Cc: linux-raid, linux-block, stable, Ramesh Adhikari
In-Reply-To: <20260720111400.2120834-1-colyli@fygo.io>
在 2026/7/20 19:14, colyli@fygo.io 写道:
> In super_1_load(), sb->bblog_shift is an __u8 type value loaded from on-
> disk superblock. It is used for badblocks API badblocks_set() by the
> following sequence,
>
> 1930 rdev->badblocks.shift = sb->bblog_shift;
> 1931 for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
> 1932 u64 bb = le64_to_cpu(*bbp);
> 1933 int count = bb & (0x3ff);
> 1934 u64 sector = bb >> 10;
> 1935 sector <<= sb->bblog_shift;
> 1936 count <<= sb->bblog_shift;
> 1937 if (bb + 1 == 0)
> 1938 break;
> 1939 if (!badblocks_set(&rdev->badblocks, sector, count, 1))
> 1940 return -EINVAL;
> 1941 }
>
> bb->bblog_shit is in range of 0-255, variable sector is 64bit width, for
> an invalid bb->bblog_shit, it is possible to make sector be overflowed
> by the following calculation,
> 1935 sector <<= sb->bblog_shift;
> Then in turn when call badblocks_set() at line 1939 with the invalid
> rdev->badblocks.shift set at line 1930, may result an overflow inside
> _badblocks_clear() in block/badblocks.c.
>
> Although there are many places to call badblocks APIs, the non-zero
> shift value is only used in super_1_load(), other places always use 0 as
> the shift value. Therefore it is unnecessary to do a general shift value
> overflow check inside badblock API, and just check here as the caller.
>
> This may avoid unnecessary check, make the badblocks API code more simple
> and elegant.
>
> Fixes: 2699b67223aca ("md: load/store badblock list from v1.x metadata")
> Fixes: 1726c77467833 ("badblocks: improve badblocks_set() for multiple ranges handling")
> Cc:stable@vger.kernel.org
> Cc: Ramesh Adhikari<adhikari.resume@gmail.com>
> Signed-off-by: Coly Li<colyli@fygo.io>
> ---
> drivers/md/md.c | 7 +++++++
> 1 file changed, 7 insertions(+)
Reviewed-by: Yu Kuai <yukuai@fygo.io>
--
Thanks,
Kuai
^ permalink raw reply
* Re: [PATCH v2 6/7] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
From: Logan Gunthorpe @ 2026-07-21 23:38 UTC (permalink / raw)
To: Mykola Marzhan
Cc: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme, Li Nan,
Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Bjorn Helgaas,
Shivaji Kant, Pranjal Shrivastava, Henrique Carvalho,
linux-kernel, linux-rdma, linux-pci
In-Reply-To: <CAPzsNDsLr_BABgizujN0CgnLNfzS3S37vQ133fjbJzFMvRs7CQ@mail.gmail.com>
On 2026-07-21 11:50, Mykola Marzhan wrote:
> I have split it in v3 into a raid1 patch and a raid10 patch.
> I am not sure about separate "introduce the bit" patches.
> R1BIO_P2PDMA and R10BIO_P2PDMA are entries in the two personalities'
> with own r*bio state enums.
> The commit messages are shorter now.
That helps a bit, thanks.
> An early return doesn't work here: on a mapping failure the
> function must still fall through to set R1BIO_WriteError, so the
> completion path runs the (single, whole-range) retry and records
> the bad range -- only WantReplacement and the FailFast md_error()
> are skipped. v3 folds the two tests into one branch now,
> which reads better and keeps the fall-through. Please check v3.
The early return works if you create a helper.
> I compared the two sides after splitting: the P2PDMA handling is
> symmetric, and the remaining differences are pre-existing
> personality differences rather than missed fixes.
> A shared helper would be heavier than the two open-coded branches,
> so I kept it per-personality.
Yeah, I still think it's a bit of a mess so I dug into it a bit more
myself.
I realized the real problem in this patch set was reusing
BLK_STS_TARGET. I think I may have used that a long time ago and those
paths have since been removed. Using it here doesn't quite make sense
because we end up with those awkward checks (is P2PDMA enabled *and* the
error is a BLK_STS_TARGET). I don't think that can ever really be
correct here seeing something could, in theory return BLK_STS_TARGET for
another reason even when P2PDMA is enabled. So that should be cleaned up.
Instead, I think it makes more sense to introduce a new BLK_STS_P2PDMA
error and use that. Then we don't need to know if a transaction is
P2PDMA when processing the result which simplifies patch 6 and 7
significantly.
I created a basic sketch which demonstrates what I'm thinking here:
https://github.com/lsgunth/linux blk-sts-p2p-err
Feel free to run with that.
(Note: I omitted the narrow_write_error() from the sketch because I
didn't dig into it enough to understand it. It'll need to be added to
this, preferably cleaning up the tabbing style a bit.)
Logan
^ permalink raw reply
* Re: [PATCH v3 8/8] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers
From: sashiko-bot @ 2026-07-21 18:13 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
In-Reply-To: <20260721174502.111503-9-mykola@meshstor.io>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Calling `nvme_cleanup_cmd(rq)` and unmapping the command after `nvme_host_path_error(rq)` causes a Use-After-Free.
- [Medium] Multipath accounting and I/O stats leak when `nvme_rdma_post_send` fails with unhandled errors.
--
--- Patch [8]: [PATCH v3 8/8] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers ---
commit 2c3b755710303fa594bcae5bdde333ab1b8dc356
Author: Mykola Marzhan <mykola@meshstor.io>
nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers
This commit updates nvme-rdma to map scatterlists using
ib_dma_map_sgtable_attrs() to properly capture -EREMOTEIO. It returns
BLK_STS_TARGET for this error to avoid infinite requeuing of unsupported
P2P transfers. Additionally, it delays starting the request until after
mapping succeeds to avoid taking multipath accounting for unmapped requests.
Cc: stable@vger.kernel.org # v7.1
> diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
> index 6909e35427942..f8edb23552ad2 100644
> --- a/drivers/nvme/host/rdma.c
> +++ b/drivers/nvme/host/rdma.c
[ ... ]
> @@ -2063,6 +2066,9 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
> ret = nvme_host_path_error(rq);
[Severity: Critical]
This is a pre-existing issue, but does this error path cause a use-after-free
if err == -EIO?
Looking at nvme_host_path_error(), it synchronously completes and frees the
request back to the blk-mq tag pool:
drivers/nvme/host/core.c:nvme_host_path_error() {
nvme_req(req)->status = NVME_SC_HOST_PATH_ERROR;
blk_mq_set_request_complete(req);
__nvme_complete_rq(req);
return BLK_STS_OK;
}
After it returns, the code falls through to nvme_cleanup_cmd(rq) and
unmaps the sqe.dma:
unmap_qe:
ib_dma_unmap_single(dev, req->sqe.dma, sizeof(struct nvme_command),
DMA_TO_DEVICE);
Could a concurrent thread allocate this freed request and map a new DMA
address before we unmap it here, leading to an IOMMU fault or corruption?
> 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_TARGET;
> else
> ret = BLK_STS_IOERR;
[Severity: Medium]
This is a pre-existing issue, but does returning BLK_STS_IOERR for unhandled
errors bypass NVMe multipath cleanup?
If nvme_rdma_post_send() fails with an unexpected error code (like -EINVAL),
we fall through here and return BLK_STS_IOERR. The block layer will directly
complete the request via blk_mq_end_request().
Since nvme_start_request(rq) was called earlier and set NVME_MPATH_CNT_ACTIVE,
does bypassing the NVMe-specific nvme_mpath_end_request() cause multipath
accounting to leak (ns->ctrl->nr_active is never decremented)?
> nvme_cleanup_cmd(rq);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721174502.111503-1-mykola@meshstor.io?part=8
^ permalink raw reply
* Re: [PATCH v3 5/8] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
From: sashiko-bot @ 2026-07-21 18:05 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
In-Reply-To: <20260721174502.111503-6-mykola@meshstor.io>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The patch addresses the loss of the REQ_NOMERGE flag by explicitly checking for P2PDMA, but fails to fix the root cause: the hardcoded assignment wbio->bi_opf = REQ_OP_WRITE wipes all critical durability and behavioral flags like REQ_FUA and REQ_SYNC.
--
--- Patch [5]: [PATCH v3 5/8] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones ---
commit 02b26b8d91beb002faa116497233b6b12f8d369f
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
This commit restores the REQ_NOMERGE flag on P2PDMA bios during bad block
isolation retries in narrow_write_error(). The flag is needed to prevent
member queue merging of P2PDMA segments after the bio operation is reset
to a bare REQ_OP_WRITE.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 7d769b6460ca7..16b7465c233ac 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
[ ... ]
> @@ -2570,6 +2570,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;
[Severity: High]
This is a pre-existing issue, but does hardcoding wbio->bi_opf to
REQ_OP_WRITE clear all other upper flag bits inherited from the master bio?
If the original write was synchronous and required Force Unit Access
(REQ_FUA), stripping it would cause the retry to be treated as a normal
write, which might be absorbed by the disk's volatile cache. This could
allow a retry of a physically defective block to erroneously succeed,
bypassing the bad block record and risking data loss if power is lost.
Would it be safer to mask the operation instead, preserving all durability
and behavioral flags, like:
wbio->bi_opf = (wbio->bi_opf & ~REQ_OP_MASK) | REQ_OP_WRITE;
This approach would naturally preserve REQ_NOMERGE as well, without needing
a P2PDMA-specific check.
> 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;
> + /* Keep P2PDMA retry bios unmergeable, like the original */
> + if (md_bio_is_p2pdma(wbio))
> + wbio->bi_opf |= REQ_NOMERGE;
[Severity: High]
This is a pre-existing issue, but the same logic flaw appears here in the
RAID10 bad block isolation path. Does overwriting bi_opf silently drop
important flags like REQ_FUA and REQ_SYNC?
>
> if (submit_bio_wait(wbio) &&
> !rdev_set_badblocks(rdev, wsector, sectors, 0)) {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721174502.111503-1-mykola@meshstor.io?part=5
^ permalink raw reply
* Re: [PATCH v3 3/8] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
From: sashiko-bot @ 2026-07-21 18:04 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-pci, yukuai, linux-raid
In-Reply-To: <20260721174502.111503-4-mykola@meshstor.io>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Using CollisionCheck to gate serialization for normal writes exposes a pre-existing serial_info_pool allocation failure, causing continuous WARN_ON log spam on the hot path.
- [High] Hot-added devices silently bypass write serialization under MD_SERIALIZE_POLICY, risking data corruption.
--
--- Patch [3]: [PATCH v3 3/8] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs ---
commit d02939f61be1abbb8081faa2ba5a3540f9714678
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1: serialize non-write-behind writes on CollisionCheck rdevs
This commit modifies the write path to serialize non-write-behind writes
based on the CollisionCheck flag rather than MD_SERIALIZE_POLICY. This
ensures they are properly ordered against in-flight behind writes. It also
updates the completion path to use the same condition for removing
serialization.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index afe2ca96ad8c2..997f79c05db7b 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -564,7 +564,7 @@ static void raid1_end_write_request(struct bio *bio)
> call_bio_endio(r1_bio);
> }
> }
> - } else if (test_bit(MD_SERIALIZE_POLICY, &rdev->mddev->flags))
> + } else if (test_bit(CollisionCheck, &rdev->flags))
> remove_serial(rdev, lo, hi);
[Severity: High]
This isn't a bug introduced by this patch, but changing the gate to
CollisionCheck exposes the normal write path to a pre-existing error handling
bug that results in continuous WARN_ON spam on the hot path.
If mempool allocation fails during a hot-add of a WriteMostly device,
mddev_create_serial_pool() calls rdevs_uninit_serial(). However,
rdevs_uninit_serial() only clears CollisionCheck on devices already in
mddev->disks, leaving the new device with CollisionCheck set but a NULL
serial_info_pool.
Before this patch, normal writes checked MD_SERIALIZE_POLICY and ignored
CollisionCheck, avoiding the bug. Now, does this cause every normal write to
call wait_for_serialization() (triggering the first WARN_ON) and then
remove_serial() (triggering a second WARN(1) since the IO was never recorded)?
> 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);
[Severity: High]
This is a pre-existing issue, but does checking CollisionCheck instead of
MD_SERIALIZE_POLICY silently bypass write serialization for hot-added devices
under MD_SERIALIZE_POLICY?
If MD_SERIALIZE_POLICY is enabled and a normal device is hot-added,
mddev_create_serial_pool() currently lacks a check for MD_SERIALIZE_POLICY
and returns early without creating a serial tree or setting CollisionCheck:
drivers/md/md.c:mddev_create_serial_pool() {
...
if (rdev && !rdev_need_serial(rdev) &&
!test_bit(CollisionCheck, &rdev->flags))
return;
...
}
Previously, checking MD_SERIALIZE_POLICY here caused a NULL pointer
dereference when trying to serialize the hot-added device without a serial
tree. By checking CollisionCheck instead, the crash is avoided, but does
this mean overlapping writes can now complete out of order on the
reconstructed mirror, leading to silent data corruption?
> }
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721174502.111503-1-mykola@meshstor.io?part=3
^ permalink raw reply
* Re: [PATCH v3 6/8] md/raid1: skip futile retries on P2PDMA mapping failures
From: sashiko-bot @ 2026-07-21 18:02 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
In-Reply-To: <20260721174502.111503-7-mykola@meshstor.io>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Coarse retries for persistent P2PDMA mapping failures exhaust the array's badblock list, forcing healthy device eviction.
Pre-existing issues:
- [High] Data corruption and kernel panics result from wbio->bi_opf = REQ_OP_WRITE; stripping critical flags and modifying bodiless bios.
- [High] A NULL pointer dereference occurs on rdev->bdev during concurrent read errors and hot-removes.
--
--- Patch [6]: [PATCH v3 6/8] md/raid1: skip futile retries on P2PDMA mapping failures ---
commit 808452a00e434c54e8f26591e906bbf52dd9e7f0
Author: Mykola Marzhan <mykola@meshstor.io>
md/raid1: skip futile retries on P2PDMA mapping failures
A member that cannot DMA-map peer memory fails the leg bio with
BLK_STS_TARGET. The failure is a property of the peer/member
pairing -- retrying the same pages against the same member cannot
succeed, and there is nothing on the medium to repair. This patch
flags P2PDMA master bios with a new R1BIO_P2PDMA state bit at
submission to avoid evicting healthy members during unroutable I/O.
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 16b7465c233ac..6334032c4bb8e 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
[ ... ]
> @@ -1378,6 +1384,8 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
> else
> init_r1bio(r1_bio, mddev, bio);
> r1_bio->sectors = max_read_sectors;
> + if (md_bio_is_p2pdma(bio))
> + set_bit(R1BIO_P2PDMA, &r1_bio->state);
>
> /*
> * make_request() can abort the operation when read-ahead is being
[Severity: High]
This isn't a bug introduced by this patch, but could this lead to a NULL
pointer dereference?
When a read fails, handle_read_error() drops the reference to the rdev via
rdev_dec_pending(), which may trigger device removal if it's faulty. It then
calls raid1_read_request() to retry.
In raid1_read_request(), if read_balance() returns -1 (no other disks are
available), we hit a pr_crit_ratelimited statement:
rdisk = read_balance(conf, r1_bio, &max_sectors);
if (rdisk < 0) {
/* couldn't find anywhere to read from */
if (r1bio_existed)
pr_crit_ratelimited("md/raid1:%s: %pg: unrecoverable I/O read error for block %llu\n",
mdname(mddev),
conf->mirrors[r1_bio->read_disk].rdev->bdev,
r1_bio->sector);
Is it possible that conf->mirrors[r1_bio->read_disk].rdev is NULL here due
to a concurrent hot-remove, causing a crash when dereferencing ->bdev?
[ ... ]
> @@ -2550,9 +2563,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;
[Severity: High]
This is a pre-existing issue, but does overwriting the operation entirely
strip critical upper durability flags here?
In narrow_write_error(), the cloned wbio is modified like this:
wbio->bi_opf = REQ_OP_WRITE;
Could this strip flags like REQ_FUA or REQ_PREFLUSH, leading to potential
data loss on power failure? Furthermore, if a bodiless bio like
REQ_OP_WRITE_ZEROES is converted into a regular REQ_OP_WRITE, could this
cause a NULL pointer dereference in the block layer during segment mapping
(e.g., bio_for_each_segment) since it has no data vectors?
[Severity: High]
With the new coarse retry mechanism, what happens when persistent P2PDMA
mapping failures repeatedly occur?
If a P2PDMA write fails with BLK_STS_TARGET, we enter narrow_write_error()
with coarse = true. Later in this loop, if the retried coarse I/O also
fails, it adds a badblock:
if (submit_bio_wait(wbio) &&
!rdev_set_badblocks(rdev, sector, sectors, 0)) {
Since P2PDMA pairing failures are a persistent property of the peer/member
combination, repeated coarse retries will keep adding large badblock entries.
If this quickly exhausts the array's finite badblocks list, won't
rdev_set_badblocks() return 0, triggering a call to md_error() that forces
the eviction of a healthy device? Does this defeat the patch's purpose of
preventing healthy devices from being evicted?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721174502.111503-1-mykola@meshstor.io?part=6
^ permalink raw reply
* Re: [PATCH v3 7/8] md/raid10: skip futile retries on P2PDMA mapping failures
From: sashiko-bot @ 2026-07-21 18:01 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, yukuai, linux-pci
In-Reply-To: <20260721174502.111503-8-mykola@meshstor.io>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] narrow_write_error() drops critical bio flags (e.g., REQ_FUA, REQ_PREFLUSH) during write error retries.
- [High] fix_read_error() incorrectly attributes read errors on a replacement device to the primary device.
--
--- Patch [7]: [PATCH v3 7/8] md/raid10: skip futile retries on P2PDMA mapping failures ---
commit 850a2ebdf179522973aa4c7332fa7233fdc47fc1
Author: Mykola Marzhan <mykola@meshstor.io>
Subject: md/raid10: skip futile retries on P2PDMA mapping failures
This commit handles P2PDMA mapping failures by skipping futile retries
and retrying the write once as a whole range. It also leaves replacement
legs with the default fail-on-write-error policy to avoid silent holes
during rebuilds.
Fixes: 02666132403a ("md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device")
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index f7ef903a3d4ec..4c3da50250a5a 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
[ ... ]
> @@ -2814,9 +2831,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;
[Severity: High]
This is a pre-existing issue, but does narrow_write_error() drop critical
bio flags like REQ_FUA and REQ_PREFLUSH here?
Further down in narrow_write_error(), the clone's operation flags are
overwritten with a bare write:
drivers/md/raid10.c:narrow_write_error() {
...
wbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO, &mddev->bio_set);
bio_trim(wbio, sector - bio->bi_iter.bi_sector, sectors);
...
wbio->bi_opf = REQ_OP_WRITE;
...
}
If the master bio was submitted with REQ_FUA to guarantee data persistence,
these flags are stripped and the retry is issued as a normal asynchronous
write.
If the retry succeeds by placing data in the drive's volatile cache, the
master bio completes successfully. Could a power failure before the cache
is flushed to stable media result in silent data loss, violating the
original REQ_FUA guarantees?
(Note: The identical logic appears to be present in drivers/md/raid1.c in
narrow_write_error() as well).
[ ... ]
> @@ -2856,6 +2876,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 +2889,24 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
> * frozen.
> */
> bio = r10_bio->devs[slot].bio;
> + /* evaluate before the bio_put() below */
> + p2pdma_error = bio->bi_status == BLK_STS_TARGET &&
> + test_bit(R10BIO_P2PDMA, &r10_bio->state);
> bio_put(bio);
> r10_bio->devs[slot].bio = NULL;
>
> if (mddev->ro)
> r10_bio->devs[slot].bio = IO_BLOCKED;
> - else if (!test_bit(FailFast, &rdev->flags)) {
> + else if (p2pdma_error) {
> + /* Peer can't reach this member: just redirect the read. */
> + 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;
[Severity: High]
This is a pre-existing issue, but does fix_read_error() incorrectly attribute
read errors on a replacement device to the primary device?
When read_balance() selects the replacement device for a read and it fails,
handle_read_error() passes the context to fix_read_error().
However, fix_read_error() ignores the actual device that failed
(r10_bio->devs[slot].rdev) and blindly selects the primary device:
drivers/md/raid10.c:fix_read_error() {
...
/* still own a reference to this rdev, so it cannot
* have been cleared recently.
*/
rdev = conf->mirrors[d].rdev;
if (test_bit(Faulty, &rdev->flags))
...
}
Later, bad blocks are also recorded on the primary device:
drivers/md/raid10.c:fix_read_error() {
...
int dn = r10_bio->devs[slot].devnum;
rdev = conf->mirrors[dn].rdev;
if (!rdev_set_badblocks(
rdev,
r10_bio->devs[slot].addr
+ sect,
s, 0)) {
...
}
Could this result in the healthy primary device being penalized via
exceed_read_errors() and incorrectly targeted for bad block recording,
while the faulty replacement device continues to operate without
correction?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721174502.111503-1-mykola@meshstor.io?part=7
^ permalink raw reply
* Re: [PATCH v3 1/8] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers
From: sashiko-bot @ 2026-07-21 18:00 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-raid, linux-pci, yukuai
In-Reply-To: <20260721174502.111503-2-mykola@meshstor.io>
> Unsupported P2P transfers used to fail with BLK_STS_TARGET, chosen
> by commit 91fb2b6052f7 ("nvme-pci: convert to using
> dma_map_sgtable()") for dma_map_sgtable()'s -EREMOTEIO: an I/O that
> can never succeed on this device must not be retried. The
> blk_rq_dma_map conversion changed it to BLK_STS_INVAL, which
> blk_path_error() treats as retryable and md deliberately ignores
> for member failures -- a P2P write to a member the peer cannot
> reach is counted as written and the mirrors silently diverge.
>
> Restore BLK_STS_TARGET. Hit e.g. with CMB memory of one NVMe
> device used as the data buffer for I/O to a second one behind a
> different host bridge.
>
> 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
> 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/20260721174502.111503-1-mykola@meshstor.io?part=1
^ permalink raw reply
* Re: [PATCH v3 4/8] md/raid1: don't use write-behind for P2PDMA bios
From: sashiko-bot @ 2026-07-21 17:54 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: yukuai, linux-raid, linux-pci
In-Reply-To: <20260721174502.111503-5-mykola@meshstor.io>
> 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/20260721174502.111503-1-mykola@meshstor.io?part=4
^ permalink raw reply
* Re: [PATCH v3 2/8] md: ensure REQ_NOMERGE is set on P2PDMA bios
From: sashiko-bot @ 2026-07-21 17:54 UTC (permalink / raw)
To: Mykola Marzhan; +Cc: linux-pci, yukuai, linux-raid
In-Reply-To: <20260721174502.111503-3-mykola@meshstor.io>
> 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/20260721174502.111503-1-mykola@meshstor.io?part=2
^ permalink raw reply
* Re: [PATCH 0/6] block,md,nvme: correct handling of unsupported P2PDMA transfers
From: Mykola Marzhan @ 2026-07-21 17:51 UTC (permalink / raw)
To: Leon Romanovsky
Cc: Keith Busch, Jens Axboe, Song Liu, Yu Kuai, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme, Li Nan,
Xiao Ni, Jason Gunthorpe, Kiran Kumar Modukuri,
Chaitanya Kulkarni, Logan Gunthorpe, Bjorn Helgaas, Shivaji Kant,
Pranjal Shrivastava, Henrique Carvalho, linux-kernel, linux-rdma,
linux-pci
In-Reply-To: <20260721124944.GL110966@unreal>
On Tue, Jul 21, 2026 at 03:49:44PM +0300, Leon Romanovsky wrote:
> Yeah, too verbose and conveys almost no useful information.
>
> Lately, I've started ignoring such submissions to prioritize commits
> from people who put real effort into explaining what they are doing.
Point taken, thank you. Please check v3, I reworked descriptions.
In any case, the problem is real, Leon, I would really appreciate if
you could review the RDMA changes.
Mykola
^ permalink raw reply
* Re: [PATCH v2 6/7] md/raid1,raid10: skip futile retries on P2PDMA mapping failures
From: Mykola Marzhan @ 2026-07-21 17:50 UTC (permalink / raw)
To: Logan Gunthorpe
Cc: Jens Axboe, Song Liu, Yu Kuai, Keith Busch, Christoph Hellwig,
Sagi Grimberg, linux-block, linux-raid, linux-nvme, Li Nan,
Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Bjorn Helgaas,
Shivaji Kant, Pranjal Shrivastava, Henrique Carvalho,
linux-kernel, linux-rdma, linux-pci
In-Reply-To: <ee312bcb-92f4-4884-91cd-3bd56180c8c7@deltatee.com>
Thanks you!
On 2026-07-20 10:49, Logan Gunthorpe wrote:
> My eyes glazed over a bit. But one point is that maybe this could be
> split into multiple patches: one patch introduces R1BIO_P2PDMA, one
> patch for raid1 and another patch for raid10? That might make it a bit
> more manageable.
I have split it in v3 into a raid1 patch and a raid10 patch.
I am not sure about separate "introduce the bit" patches.
R1BIO_P2PDMA and R10BIO_P2PDMA are entries in the two personalities'
with own r*bio state enums.
The commit messages are shorter now.
> Also, I'm not sure if it's possible without digging deeper but, instead
> of adding a !p2pdma_unmappable check to every if() statement in the
> raid*_end_write_request() functions, might it be possible to factor out
> a helper function and return early from it?
An early return doesn't work here: on a mapping failure the
function must still fall through to set R1BIO_WriteError, so the
completion path runs the (single, whole-range) retry and records
the bad range -- only WantReplacement and the FailFast md_error()
are skipped. v3 folds the two tests into one branch now,
which reads better and keeps the fall-through. Please check v3.
> Maybe the helper could be
> shared between raid10 and raid1 as they look very similar. The
> differences between the two are minimal -- there might be a bug fix that
> was applied to one that should be applied to both?
I compared the two sides after splitting: the P2PDMA handling is
symmetric, and the remaining differences are pre-existing
personality differences rather than missed fixes.
A shared helper would be heavier than the two open-coded branches,
so I kept it per-personality.
Mykola
^ permalink raw reply
* Re: [PATCH v2 1/7] blk-mq-dma: restore BLK_STS_TARGET for unsupported P2P transfers
From: Mykola Marzhan @ 2026-07-21 17:49 UTC (permalink / raw)
To: Logan Gunthorpe
Cc: Christoph Hellwig, Jens Axboe, Song Liu, Yu Kuai, Keith Busch,
Sagi Grimberg, linux-block, linux-raid, linux-nvme, Li Nan,
Xiao Ni, Guoqing Jiang, Leon Romanovsky, Jason Gunthorpe,
Kiran Kumar Modukuri, Chaitanya Kulkarni, Bjorn Helgaas,
Shivaji Kant, Pranjal Shrivastava, Henrique Carvalho,
linux-kernel, linux-rdma, linux-pci
In-Reply-To: <cb297ebf-3731-425a-8f48-6be3c7944366@deltatee.com>
Thank you for review!
> (Arguably it might have been clearer if the switch case explicitly
> stated PCI_P2PDMA_MAP_NOT_SUPPORTED instead of relying on the
> default).
Unchanged in v3 to keep the stable backport minimal.
Mykola
^ permalink raw reply
* [PATCH v3 8/8] nvme-rdma: return BLK_STS_TARGET for unsupported P2P transfers
From: Mykola Marzhan @ 2026-07-21 17:45 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260721174502.111503-1-mykola@meshstor.io>
ib_dma_map_sg() returns 0 on a P2P mapping failure, losing the DMA
layer's -EREMOTEIO, so nvme-rdma reports a path error.
Default multipath requeues the I/O forever, and without multipath
it burns nvme_max_retries requeues, then completes as retryable
BLK_STS_TRANSPORT.
Map the data and metadata scatterlists with
ib_dma_map_sgtable_attrs() and return BLK_STS_TARGET for -EREMOTEIO,
matching nvme-pci (commit 91fb2b6052f7 ("nvme-pci: convert to using
dma_map_sgtable()")).
Scatterlists stay: ib_map_mr_sg() consumes them, so blk_rq_dma_map
does not apply.
Start the request only after mapping succeeds so multipath
accounting is never taken for an unmapped request, and ratelimit
the map-failure message.
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 | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 6909e3542794..f8edb23552ad 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,14 @@ 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 = (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))
goto out_free_table;
- }
+ *count = sgt.nents;
if (blk_integrity_rq(rq)) {
req->metadata_sgl->sg_table.sgl =
@@ -1501,14 +1504,14 @@ 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)) {
- ret = -EIO;
+ 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))
goto out_free_pi_table;
- }
+ *pi_count = sgt.nents;
}
return 0;
@@ -2026,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 ||
@@ -2039,11 +2040,13 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
err = nvme_rdma_map_data(queue, rq, c);
if (unlikely(err < 0)) {
- dev_err(queue->ctrl->ctrl.device,
- "Failed to map data (%d)\n", err);
+ dev_err_ratelimited(queue->ctrl->ctrl.device,
+ "Failed to map data (%d)\n", err);
goto err;
}
+ nvme_start_request(rq);
+
sqe->cqe.done = nvme_rdma_send_done;
ib_dma_sync_single_for_device(dev, sqe->dma,
@@ -2063,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_TARGET;
else
ret = BLK_STS_IOERR;
nvme_cleanup_cmd(rq);
--
2.52.0
^ permalink raw reply related
* [PATCH v3 7/8] md/raid10: skip futile retries on P2PDMA mapping failures
From: Mykola Marzhan @ 2026-07-21 17:45 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260721174502.111503-1-mykola@meshstor.io>
Same handling as the preceding raid1 commit: flag P2PDMA master
bios with R10BIO_P2PDMA at submission; on a BLK_STS_TARGET leg
failure retry writes once as a whole range, don't set
WantReplacement, block the leg for reads without charging the
read-error budget, don't trip FailFast.
Replacement legs keep the stock fail-on-write-error policy:
badblocks are never recorded on a replacement, so failing it is
the only outcome that cannot leave a silent hole in a rebuild.
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/raid10.c | 63 ++++++++++++++++++++++++++++++++++-----------
drivers/md/raid10.h | 2 ++
2 files changed, 50 insertions(+), 15 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index f7ef903a3d4e..4c3da50250a5 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -482,15 +482,23 @@ 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);
+ /* Peer/member pairing failure, not member health. */
+ bool p2pdma_unmappable =
+ bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R10BIO_P2PDMA, &r10_bio->state);
+ set_bit(WriteErrorSeen, &rdev->flags);
dec_rdev = 0;
- if (test_bit(FailFast, &rdev->flags) &&
- (bio->bi_opf & MD_FAILFAST)) {
- md_error(rdev->mddev, rdev);
+ if (!p2pdma_unmappable) {
+ if (!test_and_set_bit(WantReplacement,
+ &rdev->flags))
+ set_bit(MD_RECOVERY_NEEDED,
+ &rdev->mddev->recovery);
+
+ if (test_bit(FailFast, &rdev->flags) &&
+ (bio->bi_opf & MD_FAILFAST)) {
+ md_error(rdev->mddev, rdev);
+ }
}
/*
@@ -1170,6 +1178,9 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
*/
gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R10BIO_P2PDMA, &r10_bio->state);
+
if (slot >= 0 && r10_bio->devs[slot].rdev) {
/*
* This is an error retry, but we cannot
@@ -1357,6 +1368,9 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
sector_t sectors;
int max_sectors;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R10BIO_P2PDMA, &r10_bio->state);
+
if ((mddev_is_clustered(mddev) &&
mddev->cluster_ops->area_resyncing(mddev, WRITE,
bio->bi_iter.bi_sector,
@@ -2786,7 +2800,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 +2814,9 @@ 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: P2PDMA
+ * mapping failures fail every block identically.
+ *
* We currently own a reference to the rdev.
*/
@@ -2814,9 +2831,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 +2876,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 +2889,24 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
* frozen.
*/
bio = r10_bio->devs[slot].bio;
+ /* evaluate before the bio_put() below */
+ p2pdma_error = bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R10BIO_P2PDMA, &r10_bio->state);
bio_put(bio);
r10_bio->devs[slot].bio = NULL;
if (mddev->ro)
r10_bio->devs[slot].bio = IO_BLOCKED;
- else if (!test_bit(FailFast, &rdev->flags)) {
+ else if (p2pdma_error) {
+ /* Peer can't reach this member: just redirect the read. */
+ r10_bio->devs[slot].bio = IO_BLOCKED;
+ } else if (test_bit(FailFast, &rdev->flags)) {
+ md_error(mddev, rdev);
+ } else {
freeze_array(conf, 1);
fix_read_error(conf, mddev, r10_bio);
unfreeze_array(conf);
- } else
- md_error(mddev, rdev);
+ }
rdev_dec_pending(rdev, mddev);
r10_bio->state = 0;
@@ -2947,8 +2975,13 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
r10_bio->sectors, 0);
rdev_dec_pending(rdev, conf->mddev);
} else if (bio != NULL && bio->bi_status) {
+ bool coarse = bio->bi_status ==
+ BLK_STS_TARGET &&
+ test_bit(R10BIO_P2PDMA,
+ &r10_bio->state);
+
fail = true;
- narrow_write_error(r10_bio, m);
+ narrow_write_error(r10_bio, m, coarse);
rdev_dec_pending(rdev, conf->mddev);
}
bio = r10_bio->devs[m].repl_bio;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index ec79d87fb92f..a2e1554f77db 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -174,6 +174,8 @@ enum r10bio_state {
R10BIO_Previous,
/* failfast devices did receive failfast requests. */
R10BIO_FailFast,
+/* the master bio carries PCI P2PDMA (peer device memory) pages */
+ R10BIO_P2PDMA,
R10BIO_Discard,
};
#endif
--
2.52.0
^ permalink raw reply related
* [PATCH v3 6/8] md/raid1: skip futile retries on P2PDMA mapping failures
From: Mykola Marzhan @ 2026-07-21 17:45 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260721174502.111503-1-mykola@meshstor.io>
A member that cannot DMA-map peer memory fails the leg bio with
BLK_STS_TARGET. The failure is a property of the peer/member
pairing -- retrying the same pages against the same member cannot
succeed, and there is nothing on the medium to repair -- but the
error machinery treats it as a medium error: narrow_write_error()
grinds through hundreds of doomed chunk retries per write,
WantReplacement burns a spare and evicts the healthy member,
fix_read_error()'s host-page probe "succeeds" so mixed host/P2P
reads charge the read-error budget until a healthy member is
kicked (~1s in testing), and FailFast evicts on the first
unroutable I/O.
Flag P2PDMA master bios with a new R1BIO_P2PDMA state bit at
submission. On a BLK_STS_TARGET leg failure for a flagged bio:
retry writes once as a single whole range, recording one bad range
if that also fails (TARGET can be a transient device condition,
e.g. NVME_SC_NS_NOT_READY, so the retry is not skipped outright);
don't set WantReplacement; mark failed read legs IO_BLOCKED without
charging the read-error budget; and don't treat the failure as
FailFast evidence -- the request never reached the wire -- as
commit f7b24c7b41f2 ("md/raid1,raid10: don't fail devices for
invalid IO errors") did for BLK_STS_INVAL.
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 | 54 ++++++++++++++++++++++++++++++++++------------
drivers/md/raid1.h | 2 ++
2 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 16b7465c233a..6334032c4bb8 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -483,16 +483,22 @@ 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);
+ /* Peer/member pairing failure, not member health. */
+ bool p2pdma_unmappable = bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
- 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);
+ set_bit(WriteErrorSeen, &rdev->flags);
+ if (!p2pdma_unmappable) {
+ 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);
+ }
}
/*
@@ -1378,6 +1384,8 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
else
init_r1bio(r1_bio, mddev, bio);
r1_bio->sectors = max_read_sectors;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R1BIO_P2PDMA, &r1_bio->state);
/*
* make_request() can abort the operation when read-ahead is being
@@ -1557,6 +1565,8 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
r1_bio = alloc_r1bio(mddev, bio);
r1_bio->sectors = max_sectors;
+ if (md_bio_is_p2pdma(bio))
+ set_bit(R1BIO_P2PDMA, &r1_bio->state);
/* first select target devices under rcu_lock and
* inc refcount on their rdev. Record them by setting
@@ -2522,7 +2532,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;
@@ -2536,6 +2546,9 @@ 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: P2PDMA
+ * mapping failures fail every block identically.
+ *
* We currently own a reference on the rdev.
*/
@@ -2550,9 +2563,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;
@@ -2633,8 +2649,12 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
* narrow down and record precise write
* errors.
*/
+ bool coarse = r1_bio->bios[m]->bi_status ==
+ BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
+
fail = true;
- narrow_write_error(r1_bio, m);
+ narrow_write_error(r1_bio, m, coarse);
rdev_dec_pending(conf->mirrors[m].rdev,
conf->mddev);
}
@@ -2661,6 +2681,9 @@ static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio)
{
struct md_rdev *rdev = conf->mirrors[r1_bio->read_disk].rdev;
struct bio *bio = r1_bio->bios[r1_bio->read_disk];
+ /* evaluate before the bio_put() below */
+ bool p2pdma_error = bio->bi_status == BLK_STS_TARGET &&
+ test_bit(R1BIO_P2PDMA, &r1_bio->state);
struct mddev *mddev = conf->mddev;
sector_t sector;
@@ -2680,6 +2703,9 @@ 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) {
+ /* Peer can't reach this member: just redirect the read. */
+ r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED;
} else if (test_bit(FailFast, &rdev->flags)) {
md_error(mddev, rdev);
} else {
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index c98d43a7ae99..61b788a99d14 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -184,6 +184,8 @@ enum r1bio_state {
R1BIO_MadeGood,
R1BIO_WriteError,
R1BIO_FailFast,
+/* the master bio carries PCI P2PDMA (peer device memory) pages */
+ R1BIO_P2PDMA,
};
static inline int sector_to_idx(sector_t sector)
--
2.52.0
^ permalink raw reply related
* [PATCH v3 5/8] md/raid1,raid10: keep REQ_NOMERGE on narrow_write_error() retry clones
From: Mykola Marzhan @ 2026-07-21 17:44 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260721174502.111503-1-mykola@meshstor.io>
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 7d769b6460ca..16b7465c233a 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2570,6 +2570,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.52.0
^ permalink raw reply related
* [PATCH v3 4/8] md/raid1: don't use write-behind for P2PDMA bios
From: Mykola Marzhan @ 2026-07-21 17:44 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, linux-kernel, linux-rdma, linux-pci
In-Reply-To: <20260721174502.111503-1-mykola@meshstor.io>
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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox