Since commit 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA transport") nvme-rdma accepts P2PDMA bios, but a mapping failure for peer memory the HCA cannot reach is misreported as a path error: ib_dma_map_sg() returns 0, discarding the -EREMOTEIO that dma_map_sgtable() documents for exactly this case, and the driver converts it to -EIO -> nvme_host_path_error(). Under the default multipath configuration (the multipath head node advertises BLK_FEAT_PCI_P2PDMA since commit fb0eeeed91f3 ("nvme-multipath: enable PCI P2PDMA for multipath devices")) the path-error status makes nvme_failover_req() requeue the bios with a fresh retry budget each cycle, and since the mapping failure is a deterministic property of the peer/device pairing the I/O simply never completes: a hot requeue livelock, and a stacked md mirror hangs instead of failing over to its other leg. With nvme_core.multipath=N the request burns nvme_max_retries requeues -- nothing ever reaching the wire -- and completes as BLK_STS_TRANSPORT, which blk_path_error() classifies as retryable and md/raid1,raid10 treat as a genuine device error: retry storms on writes, read-error-budget eviction of a healthy member on reads (see the preceding md patches, whose mapping-failure handling keys on BLK_STS_TARGET). Map both the data and the metadata scatterlists with ib_dma_map_sgtable_attrs() so the DMA layer's error code is preserved, and translate -EREMOTEIO to BLK_STS_TARGET: the classification nvme-pci established in commit 91fb2b6052f7 ("nvme-pci: convert to using dma_map_sgtable()") and the preceding blk-mq-dma patch restores. The metadata path needs the same treatment because integrity buffers are not always host memory: bio_integrity_map_user() allows P2P pages whenever the queue advertises P2PDMA, and nvme-pci already maps P2P metadata. Returning BLK_STS_TARGET from queue_rq is a deliberate trade-off: it forecloses failover for a configuration where a sibling path through a different HCA could reach the peer. This matches nvme-pci, which has returned BLK_STS_TARGET for unsupported P2P mappings since that same commit and equally forecloses cross-controller failover; distinguishing this-path-unreachable from all-paths-unreachable at completion time would cost a spurious requeue cycle per I/O, and a stacking consumer (md) routes around the leg one layer up. Start the request only after mapping succeeds, as nvme-pci does: a mapping failure now errors out of queue_rq on a not-yet-started request, so nvme_mpath_start_request() accounting is never taken and cannot leak on the direct blk-mq completion (this also closes the same latent leak for mapping-failure BLK_STS_IOERR returns; the post-send error tail is untouched). A mapping -ENOMEM now takes the existing BLK_STS_RESOURCE branch instead of masquerading as a path error, and a DMA-layer -EINVAL completes as BLK_STS_IOERR instead of a path error; generic -EIO keeps today's host-path-error behavior, and virt-DMA devices are unaffected. Ratelimit the map-failure message -- with an md mirror steering peer-memory I/O around an unreachable leg it fires per redirected I/O, not per rare event. Fixes: 23528aa3320a ("nvme: enable PCI P2PDMA support for RDMA transport") Cc: stable@vger.kernel.org # v7.1 Assisted-by: Claude:claude-fable-5 Signed-off-by: Mykola Marzhan --- drivers/nvme/host/rdma.c | 42 +++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 6909e3542794..9017d927edc4 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,13 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, ret = nvme_host_path_error(rq); else if (err == -ENOMEM || err == -EAGAIN) ret = BLK_STS_RESOURCE; + /* + * The DMA layer refused to map peer memory to this device: a + * property of the pairing, not a path failure. Match nvme-pci + * and do not retry (see blk_path_error()). + */ + else if (err == -EREMOTEIO) + ret = BLK_STS_TARGET; else ret = BLK_STS_IOERR; nvme_cleanup_cmd(rq); -- 2.43.0