From: Chuck Lever <cel@kernel.org>
To: Jason Gunthorpe <jgg@nvidia.com>,
Leon Romanovsky <leon@kernel.org>, Christoph Hellwig <hch@lst.de>
Cc: NeilBrown <neilb@ownmail.net>, Jeff Layton <jlayton@kernel.org>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <dai.ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
<linux-rdma@vger.kernel.org>, <linux-nfs@vger.kernel.org>,
Chuck Lever <chuck.lever@oracle.com>
Subject: [PATCH v3 3/5] RDMA/core: add MR support for bvec-based RDMA operations
Date: Thu, 22 Jan 2026 17:03:59 -0500 [thread overview]
Message-ID: <20260122220401.1143331-4-cel@kernel.org> (raw)
In-Reply-To: <20260122220401.1143331-1-cel@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
The bvec-based RDMA API currently returns -EOPNOTSUPP when Memory
Region registration is required. This prevents iWARP devices from
using the bvec path, since iWARP requires MR registration for RDMA
READ operations. The force_mr debug parameter is also unusable with
bvec input.
Add rdma_rw_init_mr_wrs_bvec() to handle MR registration for bvec
arrays. The approach creates a synthetic scatterlist populated with
DMA addresses from the bvecs, then reuses the existing ib_map_mr_sg()
infrastructure. This avoids driver changes while keeping the
implementation small.
The synthetic scatterlist is stored in the rdma_rw_ctx for cleanup.
On destroy, the MRs are returned to the pool and the bvec DMA
mappings are released using the stored addresses.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
drivers/infiniband/core/rw.c | 250 ++++++++++++++++++------
drivers/infiniband/ulp/isert/ib_isert.c | 4 +-
drivers/nvme/target/rdma.c | 4 +-
include/rdma/rw.h | 17 +-
4 files changed, 206 insertions(+), 69 deletions(-)
diff --git a/drivers/infiniband/core/rw.c b/drivers/infiniband/core/rw.c
index 393a9a4d551c..3a00b788417d 100644
--- a/drivers/infiniband/core/rw.c
+++ b/drivers/infiniband/core/rw.c
@@ -38,6 +38,20 @@ static inline bool rdma_rw_can_use_mr(struct ib_device *dev, u32 port_num)
return false;
}
+/*
+ * Check if the device requires memory registration for RDMA READs.
+ * iWARP always requires MR for RDMA READ due to protocol limitations.
+ */
+static inline bool rdma_rw_io_requires_mr(struct ib_device *dev, u32 port_num,
+ enum dma_data_direction dir)
+{
+ if (dir == DMA_FROM_DEVICE && rdma_protocol_iwarp(dev, port_num))
+ return true;
+ if (unlikely(rdma_rw_force_mr))
+ return true;
+ return false;
+}
+
/*
* Check if the device will use memory registration for this RW operation.
* For RDMA READs we must use MRs on iWarp and can optionally use them as an
@@ -47,13 +61,10 @@ static inline bool rdma_rw_can_use_mr(struct ib_device *dev, u32 port_num)
static inline bool rdma_rw_io_needs_mr(struct ib_device *dev, u32 port_num,
enum dma_data_direction dir, int dma_nents)
{
- if (dir == DMA_FROM_DEVICE) {
- if (rdma_protocol_iwarp(dev, port_num))
- return true;
- if (dev->attrs.max_sgl_rd && dma_nents > dev->attrs.max_sgl_rd)
- return true;
- }
- if (unlikely(rdma_rw_force_mr))
+ if (rdma_rw_io_requires_mr(dev, port_num, dir))
+ return true;
+ if (dir == DMA_FROM_DEVICE &&
+ dev->attrs.max_sgl_rd && dma_nents > dev->attrs.max_sgl_rd)
return true;
return false;
}
@@ -132,14 +143,14 @@ static int rdma_rw_init_mr_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
int i, j, ret = 0, count = 0;
ctx->nr_ops = DIV_ROUND_UP(sg_cnt, pages_per_mr);
- ctx->reg = kcalloc(ctx->nr_ops, sizeof(*ctx->reg), GFP_KERNEL);
- if (!ctx->reg) {
+ ctx->reg.ctx = kcalloc(ctx->nr_ops, sizeof(*ctx->reg.ctx), GFP_KERNEL);
+ if (!ctx->reg.ctx) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < ctx->nr_ops; i++) {
- struct rdma_rw_reg_ctx *reg = &ctx->reg[i];
+ struct rdma_rw_reg_ctx *reg = &ctx->reg.ctx[i];
u32 nents = min(sg_cnt, pages_per_mr);
ret = rdma_rw_init_one_mr(qp, port_num, reg, sg, sg_cnt,
@@ -187,12 +198,118 @@ static int rdma_rw_init_mr_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
out_free:
while (--i >= 0)
- ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg[i].mr);
- kfree(ctx->reg);
+ ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg.ctx[i].mr);
+ kfree(ctx->reg.ctx);
out:
return ret;
}
+static int rdma_rw_init_mr_wrs_bvec(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
+ u32 port_num, const struct bio_vec *bvecs, u32 nr_bvec,
+ struct bvec_iter *iter, u64 remote_addr, u32 rkey,
+ enum dma_data_direction dir)
+{
+ struct ib_device *dev = qp->pd->device;
+ struct rdma_rw_reg_ctx *prev = NULL;
+ u32 pages_per_mr = rdma_rw_fr_page_list_len(dev, qp->integrity_en);
+ struct scatterlist *sg;
+ int i, ret, count = 0;
+ u32 nents = 0;
+
+ /*
+ * Build scatterlist from bvecs using the iterator. This follows
+ * the pattern from __blk_rq_map_sg.
+ */
+ ctx->reg.sgt.sgl = kmalloc_array(nr_bvec, sizeof(*ctx->reg.sgt.sgl),
+ GFP_KERNEL);
+ if (!ctx->reg.sgt.sgl)
+ return -ENOMEM;
+ sg_init_table(ctx->reg.sgt.sgl, nr_bvec);
+
+ for (sg = ctx->reg.sgt.sgl; iter->bi_size; sg = sg_next(sg)) {
+ struct bio_vec bv = mp_bvec_iter_bvec(bvecs, *iter);
+
+ if (nents >= nr_bvec) {
+ ret = -EINVAL;
+ goto out_free_sgl;
+ }
+ sg_set_page(sg, bv.bv_page, bv.bv_len, bv.bv_offset);
+ bvec_iter_advance(bvecs, iter, bv.bv_len);
+ nents++;
+ }
+ sg_mark_end(sg_last(ctx->reg.sgt.sgl, nents));
+ ctx->reg.sgt.orig_nents = nents;
+
+ /* DMA map the scatterlist */
+ ret = ib_dma_map_sgtable_attrs(dev, &ctx->reg.sgt, dir, 0);
+ if (ret)
+ goto out_free_sgl;
+
+ ctx->nr_ops = DIV_ROUND_UP(ctx->reg.sgt.nents, pages_per_mr);
+ ctx->reg.ctx = kcalloc(ctx->nr_ops, sizeof(*ctx->reg.ctx), GFP_KERNEL);
+ if (!ctx->reg.ctx) {
+ ret = -ENOMEM;
+ goto out_unmap_sgt;
+ }
+
+ sg = ctx->reg.sgt.sgl;
+ nents = ctx->reg.sgt.nents;
+ for (i = 0; i < ctx->nr_ops; i++) {
+ struct rdma_rw_reg_ctx *reg = &ctx->reg.ctx[i];
+ u32 sge_cnt = min(nents, pages_per_mr);
+
+ ret = rdma_rw_init_one_mr(qp, port_num, reg, sg, sge_cnt, 0);
+ if (ret < 0)
+ goto out_free_mrs;
+ count += ret;
+
+ if (prev) {
+ if (reg->mr->need_inval)
+ prev->wr.wr.next = ®->inv_wr;
+ else
+ prev->wr.wr.next = ®->reg_wr.wr;
+ }
+
+ reg->reg_wr.wr.next = ®->wr.wr;
+
+ reg->wr.wr.sg_list = ®->sge;
+ reg->wr.wr.num_sge = 1;
+ reg->wr.remote_addr = remote_addr;
+ reg->wr.rkey = rkey;
+
+ if (dir == DMA_TO_DEVICE) {
+ reg->wr.wr.opcode = IB_WR_RDMA_WRITE;
+ } else if (!rdma_cap_read_inv(qp->device, port_num)) {
+ reg->wr.wr.opcode = IB_WR_RDMA_READ;
+ } else {
+ reg->wr.wr.opcode = IB_WR_RDMA_READ_WITH_INV;
+ reg->wr.wr.ex.invalidate_rkey = reg->mr->lkey;
+ }
+ count++;
+
+ remote_addr += reg->sge.length;
+ nents -= sge_cnt;
+ sg += sge_cnt;
+ prev = reg;
+ }
+
+ if (prev)
+ prev->wr.wr.next = NULL;
+
+ ctx->type = RDMA_RW_MR;
+ return count;
+
+out_free_mrs:
+ while (--i >= 0)
+ ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg.ctx[i].mr);
+ kfree(ctx->reg.ctx);
+out_unmap_sgt:
+ ib_dma_unmap_sgtable_attrs(dev, &ctx->reg.sgt, dir, 0);
+out_free_sgl:
+ kfree(ctx->reg.sgt.sgl);
+ return ret;
+}
+
static int rdma_rw_init_map_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
struct scatterlist *sg, u32 sg_cnt, u32 offset,
u64 remote_addr, u32 rkey, enum dma_data_direction dir)
@@ -557,19 +674,13 @@ EXPORT_SYMBOL(rdma_rw_ctx_init);
* @rkey: remote key to operate on
* @dir: %DMA_TO_DEVICE for RDMA WRITE, %DMA_FROM_DEVICE for RDMA READ
*
- * Accepts bio_vec arrays directly, avoiding scatterlist conversion for
- * callers that already have data in bio_vec form. Prefer this over
- * rdma_rw_ctx_init() when the source data is a bio_vec array.
- *
- * This function does not support devices requiring memory registration.
- * iWARP devices and configurations with force_mr=1 should use
- * rdma_rw_ctx_init() with a scatterlist instead.
+ * Maps the bio_vec array directly, avoiding intermediate scatterlist
+ * conversion. Supports MR registration for iWARP devices and force_mr mode.
*
* Returns the number of WQEs that will be needed on the workqueue if
* successful, or a negative error code:
*
* * -EINVAL - @nr_bvec is zero or @iter.bi_size is zero
- * * -EOPNOTSUPP - device requires MR path (iWARP or force_mr=1)
* * -ENOMEM - DMA mapping or memory allocation failed
*/
int rdma_rw_ctx_init_bvec(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
@@ -577,14 +688,19 @@ int rdma_rw_ctx_init_bvec(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
struct bvec_iter iter, u64 remote_addr, u32 rkey,
enum dma_data_direction dir)
{
+ struct ib_device *dev = qp->pd->device;
int ret;
if (nr_bvec == 0 || iter.bi_size == 0)
return -EINVAL;
- /* MR path not supported for bvec - reject iWARP and force_mr */
- if (rdma_rw_io_needs_mr(qp->device, port_num, dir, nr_bvec))
- return -EOPNOTSUPP;
+ /*
+ * iWARP requires MR registration for all RDMA READs.
+ */
+ if (rdma_rw_io_requires_mr(dev, port_num, dir))
+ return rdma_rw_init_mr_wrs_bvec(ctx, qp, port_num, bvecs,
+ nr_bvec, &iter, remote_addr,
+ rkey, dir);
if (nr_bvec == 1)
return rdma_rw_init_single_wr_bvec(ctx, qp, bvecs, &iter,
@@ -592,14 +708,23 @@ int rdma_rw_ctx_init_bvec(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
/*
* Try IOVA-based mapping first for multi-bvec transfers.
- * This reduces IOTLB sync overhead by batching all mappings.
- * rdma_rw_init_iova_wrs_bvec() does not modify iter on -EOPNOTSUPP.
+ * IOVA coalesces bvecs into a single DMA-contiguous region,
+ * reducing the number of WRs needed and avoiding MR overhead.
*/
ret = rdma_rw_init_iova_wrs_bvec(ctx, qp, bvecs, &iter, remote_addr,
rkey, dir);
if (ret != -EOPNOTSUPP)
return ret;
+ /*
+ * IOVA mapping not available. Check if MR registration provides
+ * better performance than multiple SGE entries.
+ */
+ if (rdma_rw_io_needs_mr(dev, port_num, dir, nr_bvec))
+ return rdma_rw_init_mr_wrs_bvec(ctx, qp, port_num, bvecs,
+ nr_bvec, &iter, remote_addr,
+ rkey, dir);
+
return rdma_rw_init_map_wrs_bvec(ctx, qp, bvecs, nr_bvec, &iter,
remote_addr, rkey, dir);
}
@@ -660,23 +785,23 @@ int rdma_rw_ctx_signature_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
ctx->type = RDMA_RW_SIG_MR;
ctx->nr_ops = 1;
- ctx->reg = kzalloc(sizeof(*ctx->reg), GFP_KERNEL);
- if (!ctx->reg) {
+ ctx->reg.ctx = kzalloc(sizeof(*ctx->reg.ctx), GFP_KERNEL);
+ if (!ctx->reg.ctx) {
ret = -ENOMEM;
goto out_unmap_prot_sg;
}
- ctx->reg->mr = ib_mr_pool_get(qp, &qp->sig_mrs);
- if (!ctx->reg->mr) {
+ ctx->reg.ctx->mr = ib_mr_pool_get(qp, &qp->sig_mrs);
+ if (!ctx->reg.ctx->mr) {
ret = -EAGAIN;
goto out_free_ctx;
}
- count += rdma_rw_inv_key(ctx->reg);
+ count += rdma_rw_inv_key(ctx->reg.ctx);
- memcpy(ctx->reg->mr->sig_attrs, sig_attrs, sizeof(struct ib_sig_attrs));
+ memcpy(ctx->reg.ctx->mr->sig_attrs, sig_attrs, sizeof(struct ib_sig_attrs));
- ret = ib_map_mr_sg_pi(ctx->reg->mr, sg, sgt.nents, NULL, prot_sg,
+ ret = ib_map_mr_sg_pi(ctx->reg.ctx->mr, sg, sgt.nents, NULL, prot_sg,
prot_sgt.nents, NULL, SZ_4K);
if (unlikely(ret)) {
pr_err("failed to map PI sg (%u)\n",
@@ -684,24 +809,24 @@ int rdma_rw_ctx_signature_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
goto out_destroy_sig_mr;
}
- ctx->reg->reg_wr.wr.opcode = IB_WR_REG_MR_INTEGRITY;
- ctx->reg->reg_wr.wr.wr_cqe = NULL;
- ctx->reg->reg_wr.wr.num_sge = 0;
- ctx->reg->reg_wr.wr.send_flags = 0;
- ctx->reg->reg_wr.access = IB_ACCESS_LOCAL_WRITE;
+ ctx->reg.ctx->reg_wr.wr.opcode = IB_WR_REG_MR_INTEGRITY;
+ ctx->reg.ctx->reg_wr.wr.wr_cqe = NULL;
+ ctx->reg.ctx->reg_wr.wr.num_sge = 0;
+ ctx->reg.ctx->reg_wr.wr.send_flags = 0;
+ ctx->reg.ctx->reg_wr.access = IB_ACCESS_LOCAL_WRITE;
if (rdma_protocol_iwarp(qp->device, port_num))
- ctx->reg->reg_wr.access |= IB_ACCESS_REMOTE_WRITE;
- ctx->reg->reg_wr.mr = ctx->reg->mr;
- ctx->reg->reg_wr.key = ctx->reg->mr->lkey;
+ ctx->reg.ctx->reg_wr.access |= IB_ACCESS_REMOTE_WRITE;
+ ctx->reg.ctx->reg_wr.mr = ctx->reg.ctx->mr;
+ ctx->reg.ctx->reg_wr.key = ctx->reg.ctx->mr->lkey;
count++;
- ctx->reg->sge.addr = ctx->reg->mr->iova;
- ctx->reg->sge.length = ctx->reg->mr->length;
+ ctx->reg.ctx->sge.addr = ctx->reg.ctx->mr->iova;
+ ctx->reg.ctx->sge.length = ctx->reg.ctx->mr->length;
if (sig_attrs->wire.sig_type == IB_SIG_TYPE_NONE)
- ctx->reg->sge.length -= ctx->reg->mr->sig_attrs->meta_length;
+ ctx->reg.ctx->sge.length -= ctx->reg.ctx->mr->sig_attrs->meta_length;
- rdma_wr = &ctx->reg->wr;
- rdma_wr->wr.sg_list = &ctx->reg->sge;
+ rdma_wr = &ctx->reg.ctx->wr;
+ rdma_wr->wr.sg_list = &ctx->reg.ctx->sge;
rdma_wr->wr.num_sge = 1;
rdma_wr->remote_addr = remote_addr;
rdma_wr->rkey = rkey;
@@ -709,15 +834,15 @@ int rdma_rw_ctx_signature_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
rdma_wr->wr.opcode = IB_WR_RDMA_WRITE;
else
rdma_wr->wr.opcode = IB_WR_RDMA_READ;
- ctx->reg->reg_wr.wr.next = &rdma_wr->wr;
+ ctx->reg.ctx->reg_wr.wr.next = &rdma_wr->wr;
count++;
return count;
out_destroy_sig_mr:
- ib_mr_pool_put(qp, &qp->sig_mrs, ctx->reg->mr);
+ ib_mr_pool_put(qp, &qp->sig_mrs, ctx->reg.ctx->mr);
out_free_ctx:
- kfree(ctx->reg);
+ kfree(ctx->reg.ctx);
out_unmap_prot_sg:
if (prot_sgt.nents)
ib_dma_unmap_sgtable_attrs(dev, &prot_sgt, dir, 0);
@@ -765,16 +890,16 @@ struct ib_send_wr *rdma_rw_ctx_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
case RDMA_RW_SIG_MR:
case RDMA_RW_MR:
for (i = 0; i < ctx->nr_ops; i++) {
- rdma_rw_update_lkey(&ctx->reg[i],
- ctx->reg[i].wr.wr.opcode !=
+ rdma_rw_update_lkey(&ctx->reg.ctx[i],
+ ctx->reg.ctx[i].wr.wr.opcode !=
IB_WR_RDMA_READ_WITH_INV);
}
- if (ctx->reg[0].inv_wr.next)
- first_wr = &ctx->reg[0].inv_wr;
+ if (ctx->reg.ctx[0].inv_wr.next)
+ first_wr = &ctx->reg.ctx[0].inv_wr;
else
- first_wr = &ctx->reg[0].reg_wr.wr;
- last_wr = &ctx->reg[ctx->nr_ops - 1].wr.wr;
+ first_wr = &ctx->reg.ctx[0].reg_wr.wr;
+ last_wr = &ctx->reg.ctx[ctx->nr_ops - 1].wr.wr;
break;
case RDMA_RW_IOVA:
first_wr = &ctx->iova.wr.wr;
@@ -844,9 +969,11 @@ void rdma_rw_ctx_destroy(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
switch (ctx->type) {
case RDMA_RW_MR:
+ /* Bvec MR contexts must use rdma_rw_ctx_destroy_bvec() */
+ WARN_ON_ONCE(ctx->reg.sgt.sgl);
for (i = 0; i < ctx->nr_ops; i++)
- ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg[i].mr);
- kfree(ctx->reg);
+ ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg.ctx[i].mr);
+ kfree(ctx->reg.ctx);
break;
case RDMA_RW_MULTI_WR:
kfree(ctx->map.wrs);
@@ -891,6 +1018,13 @@ void rdma_rw_ctx_destroy_bvec(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
u32 i;
switch (ctx->type) {
+ case RDMA_RW_MR:
+ for (i = 0; i < ctx->nr_ops; i++)
+ ib_mr_pool_put(qp, &qp->rdma_mrs, ctx->reg.ctx[i].mr);
+ kfree(ctx->reg.ctx);
+ ib_dma_unmap_sgtable_attrs(dev, &ctx->reg.sgt, dir, 0);
+ kfree(ctx->reg.sgt.sgl);
+ break;
case RDMA_RW_IOVA:
dma_iova_destroy(dev->dma_device, &ctx->iova.state,
ctx->iova.mapped_len, dir, 0);
@@ -932,8 +1066,8 @@ void rdma_rw_ctx_destroy_signature(struct rdma_rw_ctx *ctx, struct ib_qp *qp,
if (WARN_ON_ONCE(ctx->type != RDMA_RW_SIG_MR))
return;
- ib_mr_pool_put(qp, &qp->sig_mrs, ctx->reg->mr);
- kfree(ctx->reg);
+ ib_mr_pool_put(qp, &qp->sig_mrs, ctx->reg.ctx->mr);
+ kfree(ctx->reg.ctx);
if (prot_sg_cnt)
ib_dma_unmap_sg(qp->pd->device, prot_sg, prot_sg_cnt, dir);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index af811d060cc8..0c6152b7660e 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -1589,7 +1589,7 @@ isert_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc)
isert_dbg("Cmd %p\n", isert_cmd);
- ret = isert_check_pi_status(cmd, isert_cmd->rw.reg->mr);
+ ret = isert_check_pi_status(cmd, isert_cmd->rw.reg.ctx->mr);
isert_rdma_rw_ctx_destroy(isert_cmd, isert_conn);
if (ret) {
@@ -1635,7 +1635,7 @@ isert_rdma_read_done(struct ib_cq *cq, struct ib_wc *wc)
iscsit_stop_dataout_timer(cmd);
if (isert_prot_cmd(isert_conn, se_cmd))
- ret = isert_check_pi_status(se_cmd, isert_cmd->rw.reg->mr);
+ ret = isert_check_pi_status(se_cmd, isert_cmd->rw.reg.ctx->mr);
isert_rdma_rw_ctx_destroy(isert_cmd, isert_conn);
cmd->write_data_done = 0;
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 9c12b2361a6d..a4aa6719a86e 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -767,7 +767,7 @@ static void nvmet_rdma_read_data_done(struct ib_cq *cq, struct ib_wc *wc)
}
if (rsp->req.metadata_len)
- status = nvmet_rdma_check_pi_status(rsp->rw.reg->mr);
+ status = nvmet_rdma_check_pi_status(rsp->rw.reg.ctx->mr);
nvmet_rdma_rw_ctx_destroy(rsp);
if (unlikely(status))
@@ -808,7 +808,7 @@ static void nvmet_rdma_write_data_done(struct ib_cq *cq, struct ib_wc *wc)
* - if succeeded send good NVMe response
* - if failed send bad NVMe response with appropriate error
*/
- status = nvmet_rdma_check_pi_status(rsp->rw.reg->mr);
+ status = nvmet_rdma_check_pi_status(rsp->rw.reg.ctx->mr);
if (unlikely(status))
rsp->req.cqe->status = cpu_to_le16(status << 1);
nvmet_rdma_rw_ctx_destroy(rsp);
diff --git a/include/rdma/rw.h b/include/rdma/rw.h
index 205e16ed6cd8..53ed0f05fa25 100644
--- a/include/rdma/rw.h
+++ b/include/rdma/rw.h
@@ -41,13 +41,16 @@ struct rdma_rw_ctx {
} iova;
/* for registering multiple WRs: */
- struct rdma_rw_reg_ctx {
- struct ib_sge sge;
- struct ib_rdma_wr wr;
- struct ib_reg_wr reg_wr;
- struct ib_send_wr inv_wr;
- struct ib_mr *mr;
- } *reg;
+ struct {
+ struct rdma_rw_reg_ctx {
+ struct ib_sge sge;
+ struct ib_rdma_wr wr;
+ struct ib_reg_wr reg_wr;
+ struct ib_send_wr inv_wr;
+ struct ib_mr *mr;
+ } *ctx;
+ struct sg_table sgt;
+ } reg;
};
};
--
2.52.0
next prev parent reply other threads:[~2026-01-22 22:04 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-22 22:03 [PATCH v3 0/5] Add a bio_vec based API to core/rw.c Chuck Lever
2026-01-22 22:03 ` [PATCH v3 1/5] RDMA/core: add bio_vec based RDMA read/write API Chuck Lever
2026-01-23 6:26 ` Christoph Hellwig
2026-01-22 22:03 ` [PATCH v3 2/5] RDMA/core: use IOVA-based DMA mapping for bvec RDMA operations Chuck Lever
2026-01-23 6:28 ` Christoph Hellwig
2026-01-23 15:04 ` Chuck Lever
2026-01-26 6:14 ` Christoph Hellwig
2026-01-22 22:03 ` Chuck Lever [this message]
2026-01-23 6:36 ` [PATCH v3 3/5] RDMA/core: add MR support for bvec-based " Christoph Hellwig
2026-01-23 15:06 ` Chuck Lever
2026-01-26 6:17 ` Christoph Hellwig
2026-01-26 16:48 ` Chuck Lever
2026-01-23 16:47 ` Chuck Lever
2026-01-26 6:16 ` Christoph Hellwig
2026-01-22 22:04 ` [PATCH v3 4/5] RDMA/core: add rdma_rw_max_sge() helper for SQ sizing Chuck Lever
2026-01-23 6:36 ` Christoph Hellwig
2026-01-22 22:04 ` [PATCH v3 5/5] svcrdma: use bvec-based RDMA read/write API Chuck Lever
2026-01-23 6:04 ` [PATCH v3 0/5] Add a bio_vec based API to core/rw.c Zhu Yanjun
2026-01-23 14:13 ` Chuck Lever
2026-01-24 18:19 ` Zhu Yanjun
2026-01-26 17:13 ` Jason Gunthorpe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260122220401.1143331-4-cel@kernel.org \
--to=cel@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=dai.ngo@oracle.com \
--cc=hch@lst.de \
--cc=jgg@nvidia.com \
--cc=jlayton@kernel.org \
--cc=leon@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=neilb@ownmail.net \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox