From 5911f6d6e7cce5f35bcaabc1895616e10a6d0aa2 Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Mon, 15 Jun 2026 08:36:46 +0800 Subject: [PATCH 001/160] RDMA/nldev: Add resource summary max values for usage display Add RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX netlink attribute to expose device resource limits (max_qp, max_cq, max_mr, max_pd, max_srq) in the resource summary alongside the existing current count. This allows userspace tools like iproute2's rdma to display resource usage in curr/max format. Expected output from "rdma resource show": Before: 0: mlx5_0: qp 123 cq 45 mr 200 pd 10 After: 0: mlx5_0: qp 123/131072 cq 45/65536 mr 200/1000000 pd 10/32768 In JSON output, both "curr" and "max" fields will be provided so that scripts can compute percentages if needed. The new attribute is optional and backward compatible - old userspace tools will simply ignore it. Signed-off-by: Tao Cui Link: https://patch.msgid.link/20260615003646.168704-1-cui.tao@linux.dev Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/nldev.c | 29 ++++++++++++++++++++++++++--- include/uapi/rdma/rdma_netlink.h | 5 +++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 02a0a9c0a4a6..f599c24b34e8 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -188,6 +188,7 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX] = { .type = NLA_U64 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -413,7 +414,7 @@ static int fill_port_info(struct sk_buff *msg, } static int fill_res_info_entry(struct sk_buff *msg, - const char *name, u64 curr) + const char *name, u64 curr, u64 max) { struct nlattr *entry_attr; @@ -427,6 +428,9 @@ static int fill_res_info_entry(struct sk_buff *msg, if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR, curr, RDMA_NLDEV_ATTR_PAD)) goto err; + if (max && nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, + max, RDMA_NLDEV_ATTR_PAD)) + goto err; nla_nest_end(msg, entry_attr); return 0; @@ -450,7 +454,7 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, }; struct nlattr *table_attr; - int ret, i, curr; + int ret, i, curr, max; if (fill_nldev_handle(msg, device)) return -EMSGSIZE; @@ -463,7 +467,26 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, if (!names[i]) continue; curr = rdma_restrack_count(device, i, show_details); - ret = fill_res_info_entry(msg, names[i], curr); + switch (i) { + case RDMA_RESTRACK_QP: + max = device->attrs.max_qp; + break; + case RDMA_RESTRACK_CQ: + max = device->attrs.max_cq; + break; + case RDMA_RESTRACK_MR: + max = device->attrs.max_mr; + break; + case RDMA_RESTRACK_PD: + max = device->attrs.max_pd; + break; + case RDMA_RESTRACK_SRQ: + max = device->attrs.max_srq; + break; + default: + max = 0; + } + ret = fill_res_info_entry(msg, names[i], curr, max); if (ret) goto err; } diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index aac9782ddc09..3af946ecbac3 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -604,6 +604,11 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */ RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */ + /* + * Resource summary entry maximum value. + */ + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, /* u64 */ + /* * Always the end */ From 011199f46f44a9fd93a9e5ab5d7fd1328d80e9bf Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 2 Jul 2026 13:47:10 -0300 Subject: [PATCH 002/160] RDMA/uverbs: Add UVERBS_ATTR_UHW to UVERBS_METHOD_REG_MR The original commit missed that three drivers (mthca, irdma, siw) have UHW data associated with reg_mr that cannot be passed through the ioctl. They also assume that the udata cannot be NULL, so failing to pass a valid udata can trigger a NULL udata crash in those drivers. This never happens in real systems since in rdma-core ibv_cmd_reg_mr_ex() does not accept a udata and those three drivers don't use it, however a malicious userspace could trigger it. Cc: stable@vger.kernel.org Fixes: 5b2e45049dc0 ("IB/core: Add UVERBS_METHOD_REG_MR on the MR object") Reported-by: Jacob Moroni Closes: https://lore.kernel.org/all/CAHYDg1TOGxRGZrS69d4Y--Shj_DZv0nJuM73iHUBwBM70g_t3Q@mail.gmail.com Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_std_types_mr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/uverbs_std_types_mr.c b/drivers/infiniband/core/uverbs_std_types_mr.c index 570b9656801d..0c72f801e0d3 100644 --- a/drivers/infiniband/core/uverbs_std_types_mr.c +++ b/drivers/infiniband/core/uverbs_std_types_mr.c @@ -364,7 +364,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_REG_MR)( dmah, attrs); else mr = pd->device->ops.reg_user_mr(pd, addr, length, iova, - access_flags, dmah, NULL); + access_flags, dmah, + &attrs->driver_udata); if (IS_ERR(mr)) return PTR_ERR(mr); @@ -527,7 +528,8 @@ DECLARE_UVERBS_NAMED_METHOD( UA_MANDATORY), UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_REG_MR_RESP_RKEY, UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); + UA_MANDATORY), + UVERBS_ATTR_UHW()); DECLARE_UVERBS_NAMED_METHOD_DESTROY( UVERBS_METHOD_MR_DESTROY, From ce334fce5412f6ad687baa2c408d74a8636667bf Mon Sep 17 00:00:00 2001 From: Manuel Ebner Date: Sat, 27 Jun 2026 11:31:08 +0200 Subject: [PATCH 003/160] docs: infiniband: fix bracket Remove needless ')'. Link: https://patch.msgid.link/r/20260627093107.31068-2-manuelebner@mailbox.org Signed-off-by: Manuel Ebner Acked-by: Randy Dunlap Signed-off-by: Jason Gunthorpe --- Documentation/infiniband/user_mad.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/infiniband/user_mad.rst b/Documentation/infiniband/user_mad.rst index d88abfc0e370..cd66e7623d66 100644 --- a/Documentation/infiniband/user_mad.rst +++ b/Documentation/infiniband/user_mad.rst @@ -62,7 +62,7 @@ Receiving MADs struct ib_user_mad *mad; mad = malloc(sizeof *mad + 256); ret = read(fd, mad, sizeof *mad + 256); - if (ret == -ENOSPC)) { + if (ret == -ENOSPC) { length = mad.length; free(mad); mad = malloc(sizeof *mad + length); From 593afe7114ce2fb972fed7690d7210dffe2cd70e Mon Sep 17 00:00:00 2001 From: luoqing Date: Mon, 29 Jun 2026 10:31:53 +0800 Subject: [PATCH 004/160] IB/iwpm: Fix spelling errors in comments Fix spelling errors in iwpm_msg.c, changing 'quite' to 'quiet'. Link: https://patch.msgid.link/r/20260629023153.357709-1-l1138897701@163.com Signed-off-by: luoqing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iwpm_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c index 4625abd29ac0..1b10f2973ad9 100644 --- a/drivers/infiniband/core/iwpm_msg.c +++ b/drivers/infiniband/core/iwpm_msg.c @@ -268,7 +268,7 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) if (ret) goto query_mapping_error; - /* If flags are required and we're not V4, then return a quite error */ + /* If flags are required and we're not V4, then return a quiet error */ if (pm_msg->flags && iwpm_ulib_version == IWPM_UABI_VERSION_MIN) { ret = -EINVAL; goto query_mapping_error_nowarn; From c3fd3966f7dd871e47f9bcd8fe90d6e23e4cdb1a Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 29 Jun 2026 12:15:37 +0300 Subject: [PATCH 005/160] RDMA/mlx5: Remove kernel-doc warning in umr.c Remove extra asterisk to avoid the following kernel-doc warning: Warning: drivers/infiniband/hw/mlx5/umr.c:986 This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst Fixes: e73242aa14d2 ("RDMA/mlx5: Optimize DMABUF mkey page size") Link: https://patch.msgid.link/r/20260629-kdoc-fix-v1-1-735a90dede7f@nvidia.com Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/umr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 48cae5cc1c1b..c595b85b428c 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -978,7 +978,7 @@ static inline int _mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, start_block, nblocks); } -/** +/* * This function makes an mkey non-present by zapping the translation entries of * the mkey by zapping (zeroing out) the first N entries, where N is determined * by the largest page size supported by the device and the MR length. From 097f50384e1877b7cf3ace12ff0d1beed19f2088 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 18 Jun 2026 20:14:55 +0000 Subject: [PATCH 006/160] RDMA/irdma: Deduplicate the irdma_del_memlist logic Simplify/dedup the irdma_del_memlist logic in preparation for the QP/CQ/SRQ ring MR refcounting change that will follow in a subsequent commit. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260618201458.875740-2-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 31 +++++++++++------------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index cb54c7c8fcd8..cd34a9629e5e 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3933,35 +3933,28 @@ static void irdma_del_memlist(struct irdma_mr *iwmr, { struct irdma_pbl *iwpbl = &iwmr->iwpbl; unsigned long flags; + spinlock_t *lock; switch (iwmr->type) { case IRDMA_MEMREG_TYPE_CQ: - spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); + lock = &ucontext->cq_reg_mem_list_lock; break; case IRDMA_MEMREG_TYPE_QP: - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + lock = &ucontext->qp_reg_mem_list_lock; break; case IRDMA_MEMREG_TYPE_SRQ: - spin_lock_irqsave(&ucontext->srq_reg_mem_list_lock, flags); - if (iwpbl->on_list) { - iwpbl->on_list = false; - list_del(&iwpbl->list); - } - spin_unlock_irqrestore(&ucontext->srq_reg_mem_list_lock, flags); + lock = &ucontext->srq_reg_mem_list_lock; break; default: - break; + return; } + + spin_lock_irqsave(lock, flags); + if (iwpbl->on_list) { + iwpbl->on_list = false; + list_del(&iwpbl->list); + } + spin_unlock_irqrestore(lock, flags); } /** From a7d0a6b58256a77566e9088a99e1594bf35821ec Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 18 Jun 2026 20:14:56 +0000 Subject: [PATCH 007/160] RDMA/irdma: Add a refcount to track user ring MR associations User QP/CQ/SRQ rings are registered with the normal reg_mr mechanism prior to creating the actual QP/CQ/SRQ object. In order to prevent userspace from deregistering these special MRs while the child object still exists, a refcount will be used. This commit adds the refcount and logic to reject a dereg_mr with active references. Subsequent commits will add logic to bump this refcount when the user QP/CQ/SRQ objects are created. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260618201458.875740-3-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 21 +++++++++++++++++---- drivers/infiniband/hw/irdma/verbs.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index cd34a9629e5e..3dd9b4991a42 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3363,6 +3363,7 @@ static struct irdma_mr *irdma_alloc_iwmr(struct ib_umem *region, if (!iwmr) return ERR_PTR(-ENOMEM); + refcount_set(&iwmr->user_ring_refs, 1); iwpbl = &iwmr->iwpbl; iwpbl->iwmr = iwmr; iwmr->region = region; @@ -3927,13 +3928,16 @@ static struct ib_mr *irdma_get_dma_mr(struct ib_pd *pd, int acc) * irdma_del_memlist - Deleting pbl list entries for CQ/QP * @iwmr: iwmr for IB's user page addresses * @ucontext: ptr to user context + * + * Return: True if the MR is currently in-use by a QP/CQ/SRQ ring. */ -static void irdma_del_memlist(struct irdma_mr *iwmr, +static bool irdma_del_memlist(struct irdma_mr *iwmr, struct irdma_ucontext *ucontext) { struct irdma_pbl *iwpbl = &iwmr->iwpbl; unsigned long flags; spinlock_t *lock; + bool in_use = false; switch (iwmr->type) { case IRDMA_MEMREG_TYPE_CQ: @@ -3946,15 +3950,19 @@ static void irdma_del_memlist(struct irdma_mr *iwmr, lock = &ucontext->srq_reg_mem_list_lock; break; default: - return; + return false; } spin_lock_irqsave(lock, flags); - if (iwpbl->on_list) { + if (!refcount_dec_if_one(&iwmr->user_ring_refs)) { + in_use = true; + } else if (iwpbl->on_list) { iwpbl->on_list = false; list_del(&iwpbl->list); } spin_unlock_irqrestore(lock, flags); + + return in_use; } /** @@ -3977,7 +3985,12 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) ucontext = rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - irdma_del_memlist(iwmr, ucontext); + + /* Do not allow the MR to be unpinned if it is still + * backing a user ring. + */ + if (irdma_del_memlist(iwmr, ucontext)) + return -EBUSY; } goto done; } diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index 289ebc9b23ca..fbd487dbebfb 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -120,6 +120,7 @@ struct irdma_mr { u64 len; u64 pgaddrmem[IRDMA_MAX_SAVED_PHY_PGADDR]; struct irdma_pbl iwpbl; + refcount_t user_ring_refs; }; struct irdma_srq { From 971e99623ed7a0d75a719021cf4fd64e5f9e44e5 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 18 Jun 2026 20:14:57 +0000 Subject: [PATCH 008/160] RDMA/irdma: Add irdma_cq fields to track pbl allocations These fields will be used in a subsequent commit which adds refcounting to user CQ MRs. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260618201458.875740-4-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 25 +++++++++++++++---------- drivers/infiniband/hw/irdma/verbs.h | 2 ++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 3dd9b4991a42..74a3904c66e4 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2129,6 +2129,11 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, goto error; spin_lock_irqsave(&iwcq->lock, flags); + if (udata) + /* Only update if the resize was successful. Otherwise, HW is + * still pointing to the old PBL. + */ + iwcq->iwpbl = iwpbl_buf; if (cq_buf) { cq_buf->kmem_buf = iwcq->kmem; cq_buf->hw = dev->hw; @@ -2499,6 +2504,8 @@ static int irdma_create_cq(struct ib_cq *ibcq, INIT_LIST_HEAD(&iwcq->resize_list); INIT_LIST_HEAD(&iwcq->cmpl_generated); iwcq->cq_num = cq_num; + iwcq->iwpbl = NULL; + iwcq->iwpbl_shadow = NULL; info.dev = dev; ukinfo->cq_size = max(entries, 4); ukinfo->cq_id = cq_num; @@ -2518,8 +2525,6 @@ static int irdma_create_cq(struct ib_cq *ibcq, struct irdma_ucontext *ucontext; struct irdma_create_cq_req req = {}; struct irdma_cq_mr *cqmr; - struct irdma_pbl *iwpbl; - struct irdma_pbl *iwpbl_shadow; struct irdma_cq_mr *cqmr_shadow; iwcq->user_mode = true; @@ -2533,34 +2538,34 @@ static int irdma_create_cq(struct ib_cq *ibcq, } spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf, - &ucontext->cq_reg_mem_list); + iwcq->iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf, + &ucontext->cq_reg_mem_list); spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); - if (!iwpbl) { + if (!iwcq->iwpbl) { err_code = -EPROTO; goto cq_free_rsrc; } - cqmr = &iwpbl->cq_mr; + cqmr = &iwcq->iwpbl->cq_mr; if (rf->sc_dev.hw_attrs.uk_attrs.feature_flags & IRDMA_FEATURE_CQ_RESIZE) { spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); - iwpbl_shadow = irdma_get_pbl( + iwcq->iwpbl_shadow = irdma_get_pbl( (unsigned long)req.user_shadow_area, &ucontext->cq_reg_mem_list); spin_unlock_irqrestore(&ucontext->cq_reg_mem_list_lock, flags); - if (!iwpbl_shadow) { + if (!iwcq->iwpbl_shadow) { err_code = -EPROTO; goto cq_free_rsrc; } - cqmr_shadow = &iwpbl_shadow->cq_mr; + cqmr_shadow = &iwcq->iwpbl_shadow->cq_mr; info.shadow_area_pa = cqmr_shadow->cq_pbl.addr; } else { info.shadow_area_pa = cqmr->shadow; } - if (iwpbl->pbl_allocated) { + if (iwcq->iwpbl->pbl_allocated) { info.virtual_map = true; info.pbl_chunk_size = 1; info.first_pm_pbl_idx = cqmr->cq_pbl.idx; diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index fbd487dbebfb..a1651641eb71 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -153,6 +153,8 @@ struct irdma_cq { struct list_head resize_list; struct irdma_cq_poll_info cur_cqe; struct list_head cmpl_generated; + struct irdma_pbl *iwpbl; + struct irdma_pbl *iwpbl_shadow; }; struct irdma_cmpl_gen { From f67d8a08f60c9217df6d40da56422d2049f5e334 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 18 Jun 2026 20:14:58 +0000 Subject: [PATCH 009/160] RDMA/irdma: Add refcounting to user ring MRs Prevent userspace from deregistering the MRs that back QP/CQ/SRQ rings by bumping the MR's refcount upon association. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260618201458.875740-5-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/utils.c | 6 ++++ drivers/infiniband/hw/irdma/verbs.c | 45 +++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index e4037d5ef899..290ad02ed657 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -1168,6 +1168,12 @@ void irdma_free_qp_rsrc(struct irdma_qp *iwqp) iwqp->kqp.dma_mem.va = NULL; kfree(iwqp->kqp.sq_wrid_mem); kfree(iwqp->kqp.rq_wrid_mem); + + if (iwqp->user_mode && iwqp->iwpbl) { + struct irdma_mr *iwmr = iwqp->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } } /** diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 74a3904c66e4..5c907ffce99b 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -464,6 +464,9 @@ static struct irdma_pbl *irdma_get_pbl(unsigned long va, list_for_each_entry (iwpbl, pbl_list, list) { if (iwpbl->user_base == va) { + struct irdma_mr *iwmr = iwpbl->iwmr; + + refcount_inc(&iwmr->user_ring_refs); list_del(&iwpbl->list); iwpbl->on_list = false; return iwpbl; @@ -1881,6 +1884,11 @@ static void irdma_srq_free_rsrc(struct irdma_pci_f *rf, struct irdma_srq *iwsrq) dma_free_coherent(rf->sc_dev.hw->device, iwsrq->kmem.size, iwsrq->kmem.va, iwsrq->kmem.pa); iwsrq->kmem.va = NULL; + } else { + /* Not called in any failure path, so iwpbl is valid. */ + struct irdma_mr *iwmr = iwsrq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); } irdma_free_rsrc(rf, rf->allocated_srqs, srq->srq_uk.srq_id); @@ -1903,6 +1911,21 @@ static void irdma_cq_free_rsrc(struct irdma_pci_f *rf, struct irdma_cq *iwcq) iwcq->kmem_shadow.size, iwcq->kmem_shadow.va, iwcq->kmem_shadow.pa); iwcq->kmem_shadow.va = NULL; + } else { + struct irdma_mr *iwmr; + + /* May be called in a failure path before iwpbl is valid. */ + if (iwcq->iwpbl) { + iwmr = iwcq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } + + if (iwcq->iwpbl_shadow) { + iwmr = iwcq->iwpbl_shadow->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } } irdma_free_rsrc(rf, rf->allocated_cqs, cq->cq_uk.cq_id); @@ -2018,7 +2041,7 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, struct irdma_modify_cq_info info = {}; struct irdma_dma_mem kmem_buf; struct irdma_cq_mr *cqmr_buf; - struct irdma_pbl *iwpbl_buf; + struct irdma_pbl *iwpbl_buf = NULL; struct irdma_device *iwdev; struct irdma_pci_f *rf; struct irdma_cq_buf *cq_buf = NULL; @@ -2129,11 +2152,19 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, goto error; spin_lock_irqsave(&iwcq->lock, flags); - if (udata) + if (udata) { + struct irdma_pbl *old_iwpbl = iwcq->iwpbl; + /* Only update if the resize was successful. Otherwise, HW is * still pointing to the old PBL. */ iwcq->iwpbl = iwpbl_buf; + if (old_iwpbl) { + struct irdma_mr *old_iwmr = old_iwpbl->iwmr; + + refcount_dec(&old_iwmr->user_ring_refs); + } + } if (cq_buf) { cq_buf->kmem_buf = iwcq->kmem; cq_buf->hw = dev->hw; @@ -2149,6 +2180,11 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, return 0; error: + if (iwpbl_buf) { + struct irdma_mr *iwmr = iwpbl_buf->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } if (!udata) { dma_free_coherent(dev->hw->device, kmem_buf.size, kmem_buf.va, kmem_buf.pa); @@ -2425,6 +2461,11 @@ static int irdma_create_srq(struct ib_srq *ibsrq, dma_free_coherent(rf->hw.device, iwsrq->kmem.size, iwsrq->kmem.va, iwsrq->kmem.pa); free_rsrc: + if (iwsrq->user_mode && iwsrq->iwpbl) { + struct irdma_mr *iwmr = iwsrq->iwpbl->iwmr; + + refcount_dec(&iwmr->user_ring_refs); + } irdma_free_rsrc(rf, rf->allocated_srqs, iwsrq->srq_num); return err_code; } From 4dc9c884c0aafac1e8d37536d88d485135e26883 Mon Sep 17 00:00:00 2001 From: Seyeong Kim Date: Fri, 19 Jun 2026 05:00:44 +0000 Subject: [PATCH 010/160] RDMA/irdma: Suppress PF reset on HMC error The irdma driver currently issues an unconditional PF reset whenever the HMC Error interrupt (PFINT_OICR bit 26) fires: if (event->reg & IRDMAPFINT_OICR_HMC_ERR_M) { ibdev_err(&iwdev->ibdev, "HMC Error\n"); iwdev->rf->reset = true; } request_reset() issues an IIDC_PFR to ice. In practice a single HMC_ERR can trigger cascading PF resets, IOMMU faults during teardown, and teardown of every RDMA connection on the device. i40e handles the identically-named interrupt by reading PFHMC_ERRORINFO and PFHMC_ERRORDATA and logging them without touching device state; see commit 9c010ee0ea5f ("i40e: Suppress HMC error to Interrupt message level") which removed the reset as "not necessary". This patch mirrors that handling on irdma. With this change, repeated HMC_ERR no longer produces a reset storm and RDMA traffic on the device continues uninterrupted. Signed-off-by: Seyeong Kim Link: https://patch.msgid.link/20260619050044.1807044-1-seyeong.kim@canonical.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/i40iw_hw.c | 4 +++- drivers/infiniband/hw/irdma/icrdma_hw.c | 2 ++ drivers/infiniband/hw/irdma/icrdma_hw.h | 2 ++ drivers/infiniband/hw/irdma/icrdma_if.c | 8 ++++++-- drivers/infiniband/hw/irdma/irdma.h | 2 ++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.c b/drivers/infiniband/hw/irdma/i40iw_hw.c index 60c1f2b1811d..8301938b4543 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.c +++ b/drivers/infiniband/hw/irdma/i40iw_hw.c @@ -29,7 +29,9 @@ static u32 i40iw_regs[IRDMA_MAX_REGS] = { I40E_PFHMC_PDINV, I40E_GLHMC_VFPDINV(0), I40E_GLPE_CRITERR, - 0xffffffff /* PFINT_RATEN not used in FPK */ + 0xffffffff, /* PFINT_RATEN not used in FPK */ + 0xffffffff, /* PFHMC_ERRORINFO not used in FPK */ + 0xffffffff /* PFHMC_ERRORDATA not used in FPK */ }; static u32 i40iw_stat_offsets[] = { diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.c b/drivers/infiniband/hw/irdma/icrdma_hw.c index 32f26284a788..b1f1b5485762 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.c +++ b/drivers/infiniband/hw/irdma/icrdma_hw.c @@ -29,6 +29,8 @@ static u32 icrdma_regs[IRDMA_MAX_REGS] = { GLHMC_VFPDINV(0), GLPE_CRITERR, GLINT_RATE(0), + PFHMC_ERRORINFO, + PFHMC_ERRORDATA, }; static u64 icrdma_masks[IRDMA_MAX_MASKS] = { diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.h b/drivers/infiniband/hw/irdma/icrdma_hw.h index d97944ab45da..0acdeda1236d 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.h +++ b/drivers/infiniband/hw/irdma/icrdma_hw.h @@ -40,6 +40,8 @@ #define GLHMC_VFPDINV(_i) (0x00528300 + ((_i) * 4)) /* _i=0...31 */ #define GLPE_CRITERR 0x00534000 #define GLINT_RATE(_INT) (0x0015A000 + ((_INT) * 4)) /* _i=0...2047 */ /* Reset Source: CORER */ +#define PFHMC_ERRORINFO 0x00520400 +#define PFHMC_ERRORDATA 0x00520500 #define ICRDMA_DB_ADDR_OFFSET (8 * 1024 * 1024 - 64 * 1024) diff --git a/drivers/infiniband/hw/irdma/icrdma_if.c b/drivers/infiniband/hw/irdma/icrdma_if.c index 2172a2092e3f..4b451d8482a4 100644 --- a/drivers/infiniband/hw/irdma/icrdma_if.c +++ b/drivers/infiniband/hw/irdma/icrdma_if.c @@ -91,8 +91,12 @@ static void icrdma_iidc_event_handler(struct iidc_rdma_core_dev_info *cdev_info, } } if (event->reg & IRDMAPFINT_OICR_HMC_ERR_M) { - ibdev_err(&iwdev->ibdev, "HMC Error\n"); - iwdev->rf->reset = true; + u32 hmc_errinfo = readl(iwdev->rf->sc_dev.hw_regs[IRDMA_PFHMC_ERRORINFO]); + u32 hmc_errdata = readl(iwdev->rf->sc_dev.hw_regs[IRDMA_PFHMC_ERRORDATA]); + + /* Log diagnostics; do not reset here. */ + ibdev_warn(&iwdev->ibdev, "HMC Error: errinfo=0x%08x errdata=0x%08x\n", + hmc_errinfo, hmc_errdata); } if (event->reg & IRDMAPFINT_OICR_PE_PUSH_M) { ibdev_err(&iwdev->ibdev, "PE Push Error\n"); diff --git a/drivers/infiniband/hw/irdma/irdma.h b/drivers/infiniband/hw/irdma/irdma.h index b5ce515f4ee8..d4530520e39f 100644 --- a/drivers/infiniband/hw/irdma/irdma.h +++ b/drivers/infiniband/hw/irdma/irdma.h @@ -66,6 +66,8 @@ enum irdma_registers { IRDMA_GLHMC_VFPDINV, IRDMA_GLPE_CRITERR, IRDMA_GLINT_RATE, + IRDMA_PFHMC_ERRORINFO, + IRDMA_PFHMC_ERRORDATA, IRDMA_MAX_REGS, /* Must be last entry */ }; From fbbe1d5cd7d400a98afcf337969cfdb808cb198f Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <202275009@qq.com> Date: Fri, 26 Jun 2026 09:59:20 +0800 Subject: [PATCH 011/160] RDMA/rxe: Check PDs for memory window binds The IBTA Software Transport Verbs specification requires the QP, Memory Window and Memory Region for a Bind Memory Window operation to belong to the same HCA and protection domain. rxe only checked the QP and MW protection domain for type 2 MWs. Move the QP/MW PD check to the common bind path and also reject binding an MW to an MR from a different PD. Invalid bind requests continue to fail with IB_WC_MW_BIND_ERR. Reviewed-by: Zhu Yanjun Signed-off-by: Zhiwei Zhang <202275009@qq.com> Link: https://patch.msgid.link/tencent_FD4FB25AA4FFA845E63F5AC36CF4A46CDC0A@qq.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_mw.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c index 379e65bfcd49..bddb7a257831 100644 --- a/drivers/infiniband/sw/rxe/rxe_mw.c +++ b/drivers/infiniband/sw/rxe/rxe_mw.c @@ -72,13 +72,6 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, return -EINVAL; } - /* C10-72 */ - if (unlikely(qp->pd != to_rpd(mw->ibmw.pd))) { - rxe_dbg_mw(mw, - "attempt to bind type 2 MW with qp with different PD\n"); - return -EINVAL; - } - /* o10-37.2.40 */ if (unlikely(!mr || wqe->wr.wr.mw.length == 0)) { rxe_dbg_mw(mw, @@ -87,10 +80,21 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } } - /* remaining checks only apply to a nonzero MR */ + /* C10-72 */ + if (unlikely(qp->pd != rxe_mw_pd(mw))) { + rxe_dbg_mw(mw, "attempt to bind MW with qp with different PD\n"); + return -EINVAL; + } + if (!mr) return 0; + /* remaining checks only apply to a nonzero MR */ + if (unlikely(qp->pd != mr_pd(mr))) { + rxe_dbg_mw(mw, "attempt to bind MW/QP to MR with different PD\n"); + return -EINVAL; + } + if (unlikely(mr->access & IB_ZERO_BASED)) { rxe_dbg_mw(mw, "attempt to bind MW to zero based MR\n"); return -EINVAL; From 297b5b747a0a2c6b63088d3f5cc102a6cffaf292 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Wed, 1 Jul 2026 17:05:10 +0200 Subject: [PATCH 012/160] RDMA/hfi1: Remove unused non-user-accessible device class The driver defines two device classes: "hfi1" (mode 0600) and "hfi1_user" (mode 0666), selected by a user_accessible parameter to hfi1_cdev_init(). The only caller always passes user_accessible=true, so the "hfi1" class is registered but never used. The 0600 class was originally used by the diagnostics UI char device (hfi1_ui*), but that was removed over 10 years ago in commit 7312f29d8ee5 ("IB/hfi1: Remove UI char device"). The class and the user_accessible parameter were left behind. Remove the unused class and the user_accessible parameter. Now that there's only one class, it might make sense to change its name from "hfi1_user" to just "hfi1", but not knowing whether userspace would mind, keep the name as is. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Michal Schmidt Link: https://patch.msgid.link/20260701150510.384858-1-mschmidt@redhat.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/device.c | 32 ++------------------------- drivers/infiniband/hw/hfi1/device.h | 1 - drivers/infiniband/hw/hfi1/file_ops.c | 2 +- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/device.c b/drivers/infiniband/hw/hfi1/device.c index a98a4175e53b..adcfb80d52d4 100644 --- a/drivers/infiniband/hw/hfi1/device.c +++ b/drivers/infiniband/hw/hfi1/device.c @@ -10,18 +10,6 @@ #include "hfi.h" #include "device.h" -static char *hfi1_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0600; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - -static const struct class class = { - .name = "hfi1", - .devnode = hfi1_devnode, -}; - static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) { if (mode) @@ -38,7 +26,6 @@ static dev_t hfi1_dev; int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, struct cdev *cdev, struct device **devp, - bool user_accessible, struct kobject *parent) { const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); @@ -57,10 +44,7 @@ int hfi1_cdev_init(int minor, const char *name, goto done; } - if (user_accessible) - device = device_create(&user_class, NULL, dev, NULL, "%s", name); - else - device = device_create(&class, NULL, dev, NULL, "%s", name); + device = device_create(&user_class, NULL, dev, NULL, "%s", name); if (IS_ERR(device)) { ret = PTR_ERR(device); @@ -100,33 +84,21 @@ int __init dev_init(void) ret = alloc_chrdev_region(&hfi1_dev, 0, HFI1_NMINORS, DRIVER_NAME); if (ret < 0) { pr_err("Could not allocate chrdev region (err %d)\n", -ret); - goto done; - } - - ret = class_register(&class); - if (ret) { - pr_err("Could not create device class (err %d)\n", -ret); - unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); - goto done; + return ret; } ret = class_register(&user_class); if (ret) { pr_err("Could not create device class for user accessible files (err %d)\n", -ret); - class_unregister(&class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); - goto done; } -done: return ret; } void dev_cleanup(void) { - class_unregister(&class); class_unregister(&user_class); - unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); } diff --git a/drivers/infiniband/hw/hfi1/device.h b/drivers/infiniband/hw/hfi1/device.h index a91bea426ba5..3e2d21770e6c 100644 --- a/drivers/infiniband/hw/hfi1/device.h +++ b/drivers/infiniband/hw/hfi1/device.h @@ -9,7 +9,6 @@ int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, struct cdev *cdev, struct device **devp, - bool user_accessible, struct kobject *parent); void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp); const char *class_name(void); diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index 56031becb273..dc548e6802e2 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -1689,7 +1689,7 @@ static int user_add(struct hfi1_devdata *dd) snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); ret = hfi1_cdev_init(dd->unit, name, &hfi1_file_ops, &dd->user_cdev, &dd->user_device, - true, &dd->verbs_dev.rdi.ibdev.dev.kobj); + &dd->verbs_dev.rdi.ibdev.dev.kobj); if (ret) user_remove(dd); From 5f9576c6734abca88a02db72c466e09d2eddf160 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 08:36:14 +0800 Subject: [PATCH 013/160] RDMA/bng_re: return a timeout when firmware responses stall __wait_for_resp() documents that it returns a non-zero error when a firmware command does not complete, and bng_re_rcfw_send_message() already marks the firmware as stalled when the helper returns -ENODEV. However, the helper ignores wait_event_timeout() expiry. If the response slot remains in use after the timeout and after the polled CREQ service attempt, the loop starts another full timeout period and can repeat forever. Return -ENODEV after a timed out wait that still has no response. The existing caller then marks FIRMWARE_STALL_DETECTED and returns -ETIMEDOUT to the command issuer. Fixes: 53c6ee7d7f68 ("RDMA/bng_re: Enable Firmware channel and query device attributes") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260625003614.27515-1-pengpeng@iscas.ac.cn Reviewed-by: Siva Reddy Kallam Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bng_re/bng_fw.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bng_re/bng_fw.c b/drivers/infiniband/hw/bng_re/bng_fw.c index 50156c300b33..ab6a2d2e95b5 100644 --- a/drivers/infiniband/hw/bng_re/bng_fw.c +++ b/drivers/infiniband/hw/bng_re/bng_fw.c @@ -401,14 +401,15 @@ static int __wait_for_resp(struct bng_re_rcfw *rcfw, u16 cookie) { struct bng_re_cmdq_ctx *cmdq; struct bng_re_crsqe *crsqe; + unsigned long time_left; cmdq = &rcfw->cmdq; crsqe = &rcfw->crsqe_tbl[cookie]; do { - wait_event_timeout(cmdq->waitq, - !crsqe->is_in_used, - secs_to_jiffies(rcfw->max_timeout)); + time_left = wait_event_timeout(cmdq->waitq, + !crsqe->is_in_used, + secs_to_jiffies(rcfw->max_timeout)); if (!crsqe->is_in_used) return 0; @@ -417,6 +418,9 @@ static int __wait_for_resp(struct bng_re_rcfw *rcfw, u16 cookie) if (!crsqe->is_in_used) return 0; + + if (!time_left) + return -ENODEV; } while (true); }; From b21d9bf627dd4162dd485f5f8d7d0fbdd71a175e Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Wed, 8 Jul 2026 17:07:34 -0400 Subject: [PATCH 014/160] RDMA/ionic: Remove duplicate IONIC_SPEC_HIGH definition The macro IONIC_SPEC_HIGH is defined twice - remove it. Signed-off-by: Kamal Heib Link: https://patch.msgid.link/20260708210734.641411-1-kheib@redhat.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/ionic/ionic_ibdev.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index 63828240d659..53dab2d54844 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -33,7 +33,6 @@ #define IONIC_MAX_QPID 0xffffff #define IONIC_SPEC_HIGH 8 #define IONIC_MAX_PD 1024 -#define IONIC_SPEC_HIGH 8 #define IONIC_SQCMB_ORDER 5 #define IONIC_RQCMB_ORDER 0 From a833ce42d05624f27ac9b32e939df9caa7b06af3 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 2 Jul 2026 17:06:46 +0000 Subject: [PATCH 015/160] RDMA/core: Add ib_no_udata_io() helper In many cases, a driver op accepts no input data and provides no response. This helper can be used in those handlers to adhere to the uAPI forward/backward compat rules by failing early if invalid udata is provided (whether input or output). Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260702170652.4159201-2-jmoroni@google.com Signed-off-by: Leon Romanovsky --- include/rdma/uverbs_ioctl.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h index 24fd36213023..80f3ba6663d0 100644 --- a/include/rdma/uverbs_ioctl.h +++ b/include/rdma/uverbs_ioctl.h @@ -1151,4 +1151,24 @@ static inline int ib_respond_empty_udata(struct ib_udata *udata) return 0; } +/** + * ib_no_udata_io - Ensure no input data and zero fill the response buffer + * @udata: The system call's ib_udata struct + * + * Driver ops which do not accept any input data and do not provide any response + * data may call this at the beginning of their handler to fully adhere to the + * uAPI forward/backward compatibility rules. + * + * Return: Negative failure code if the op should be denied, 0 otherwise. + */ +static inline int ib_no_udata_io(struct ib_udata *udata) +{ + int ret = ib_is_udata_in_empty(udata); + + if (ret) + return ret; + + return ib_respond_empty_udata(udata); +} + #endif From 7d75592114d1664623c8cf191a12b38052c04483 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 12 Jul 2026 04:37:35 -0400 Subject: [PATCH 016/160] RDMA/core: Wait for RCU callbacks before unloading ib_core put_gid_ndev() is queued with call_rcu() and implemented in ib_core. Stopping the workqueues does not drain callbacks already queued, so RCU could invoke it after the module code has been unloaded. synchronize_rcu() does not wait for callbacks. Wait for them after all producers have stopped. Fixes: 943bd984b108 ("RDMA/core: Allow detaching gid attribute netdevice for RoCE") Reported-by: Sebastian Andrzej Siewior Closes: https://lore.kernel.org/linux-rdma/20260708092316.Qb39F_B0@linutronix.de/ Link: https://patch.msgid.link/20260709-unload-rcu-v1-1-fccd27211e5a@nvidia.com Acked-by: Sebastian Andrzej Siewior Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index b8193e077a74..d954eda63134 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -3150,6 +3150,7 @@ static void __exit ib_core_cleanup(void) /* Make sure that any pending umem accounting work is done. */ destroy_workqueue(ib_wq); destroy_workqueue(ib_unreg_wq); + rcu_barrier(); WARN_ON(!xa_empty(&clients)); WARN_ON(!xa_empty(&devices)); } From e37cdd75f8d61c1123d324ae5667ac3da562290e Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 12 Jul 2026 04:37:36 -0400 Subject: [PATCH 017/160] RDMA/mlx5: Drain RCU callbacks during module teardown devx_free_subscription() can remain queued after the last DevX event file drops its module reference or an auxiliary driver detaches its devices. mlx5_ib can then unload before the callback runs. Registration error unwind has the same risk because driver registration can attach existing devices before failing. Wait after all drivers have stopped. Fixes: 6898d1c661d7 ("RDMA/mlx5: Use RCU and direct refcounts to keep memory alive") Reported-by: Sebastian Andrzej Siewior Closes: https://lore.kernel.org/linux-rdma/20260708092316.Qb39F_B0@linutronix.de/ Link: https://patch.msgid.link/20260709-unload-rcu-v1-2-fccd27211e5a@nvidia.com Acked-by: Sebastian Andrzej Siewior Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 02809114fc79..4ff6cca7e581 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -5538,6 +5538,7 @@ static int __init mlx5_ib_init(void) dd_err: mlx5r_rep_cleanup(); rep_err: + rcu_barrier(); mlx5_ib_qp_event_cleanup(); qp_event_err: destroy_workqueue(mlx5_ib_event_wq); @@ -5551,6 +5552,7 @@ static void __exit mlx5_ib_cleanup(void) auxiliary_driver_unregister(&mlx5r_driver); auxiliary_driver_unregister(&mlx5r_mp_driver); mlx5r_rep_cleanup(); + rcu_barrier(); mlx5_ib_qp_event_cleanup(); destroy_workqueue(mlx5_ib_event_wq); From 31b7c700670830a0e8a4cdcd451c88a13cc5dc48 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 12 Jul 2026 04:37:36 -0400 Subject: [PATCH 018/160] RDMA/ipoib: Drain RCU callbacks during module teardown IPoIB reclamation completions can be signaled from inside an RCU callback. Teardown can wake before the callback returns and unload ib_ipoib while its code is still executing. Client registration failure can also remove already-added devices and queue callbacks. Wait after client and workqueue teardown. Fixes: b63b70d87741 ("IPoIB: Use a private hash table for path lookup in xmit path") Reported-by: Sebastian Andrzej Siewior Closes: https://lore.kernel.org/linux-rdma/20260708092316.Qb39F_B0@linutronix.de/ Link: https://patch.msgid.link/20260709-unload-rcu-v1-3-fccd27211e5a@nvidia.com Acked-by: Sebastian Andrzej Siewior Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 16a015b67206..6c14246befb1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -2783,6 +2783,7 @@ static int __init ipoib_init_module(void) err_sa: ib_sa_unregister_client(&ipoib_sa_client); destroy_workqueue(ipoib_workqueue); + rcu_barrier(); err_fs: ipoib_unregister_debugfs(); @@ -2800,6 +2801,7 @@ static void __exit ipoib_cleanup_module(void) ib_sa_unregister_client(&ipoib_sa_client); ipoib_unregister_debugfs(); destroy_workqueue(ipoib_workqueue); + rcu_barrier(); } module_init(ipoib_init_module); From 97ba15272e8e039b767114f244e5f0a5103a6bdf Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Mon, 6 Jul 2026 17:00:07 +0000 Subject: [PATCH 019/160] RDMA/efa: Add initialization of AH cache rhashtable New EFA devices don't support the creation of multiple address handles to the same remote on the same PD. To overcome this limitation, introduce an AH cache rhashtable which will store the user refcounts of the same AH creation on the same PD and will allow the driver to manage AH reuse. The hashtable key is the combination of PD and GID. Add initialization and teardown logic for the rhashtable. Each entry holds a refcount to manage the entry lifetime in the hashtable and a user count that indicates how many users are using the address handle. Reviewed-by: Firas Jahjah Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Link: https://patch.msgid.link/20260706170008.1039417-2-ynachum@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/Makefile | 4 +-- drivers/infiniband/hw/efa/efa_ah_cache.c | 41 ++++++++++++++++++++++++ drivers/infiniband/hw/efa/efa_ah_cache.h | 36 +++++++++++++++++++++ drivers/infiniband/hw/efa/efa_com.c | 12 ++++++- drivers/infiniband/hw/efa/efa_com.h | 3 ++ 5 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 drivers/infiniband/hw/efa/efa_ah_cache.c create mode 100644 drivers/infiniband/hw/efa/efa_ah_cache.h diff --git a/drivers/infiniband/hw/efa/Makefile b/drivers/infiniband/hw/efa/Makefile index 6e83083af0bc..a6a433b0ba2f 100644 --- a/drivers/infiniband/hw/efa/Makefile +++ b/drivers/infiniband/hw/efa/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause -# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All rights reserved. +# Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. # # Makefile for Amazon Elastic Fabric Adapter (EFA) device driver. # obj-$(CONFIG_INFINIBAND_EFA) += efa.o -efa-y := efa_com_cmd.o efa_com.o efa_main.o efa_verbs.o +efa-y := efa_com_cmd.o efa_ah_cache.o efa_com.o efa_main.o efa_verbs.o diff --git a/drivers/infiniband/hw/efa/efa_ah_cache.c b/drivers/infiniband/hw/efa/efa_ah_cache.c new file mode 100644 index 000000000000..39d0d2e97589 --- /dev/null +++ b/drivers/infiniband/hw/efa/efa_ah_cache.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Copyright 2026 Amazon.com, Inc. or its affiliates. All rights reserved. + */ + +#include + +#include "efa_ah_cache.h" + +static const struct rhashtable_params ah_cache_params = { + .key_len = sizeof(struct efa_ah_cache_key), + .key_offset = offsetof(struct efa_ah_cache_entry, key), + .head_offset = offsetof(struct efa_ah_cache_entry, linkage), +}; + +int efa_ah_cache_init(struct efa_ah_cache *ah_cache) +{ + int err; + + mutex_init(&ah_cache->lock); + err = rhashtable_init(&ah_cache->hashtable, &ah_cache_params); + if (err) + mutex_destroy(&ah_cache->lock); + + return err; +} + +static void efa_ah_cache_entry_free(void *ptr, void *arg) +{ + struct efa_ah_cache_entry *entry = ptr; + + WARN_ON(entry->usecnt); + mutex_destroy(&entry->lock); + kfree(entry); +} + +void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache) +{ + rhashtable_free_and_destroy(&ah_cache->hashtable, efa_ah_cache_entry_free, NULL); + mutex_destroy(&ah_cache->lock); +} diff --git a/drivers/infiniband/hw/efa/efa_ah_cache.h b/drivers/infiniband/hw/efa/efa_ah_cache.h new file mode 100644 index 000000000000..1d1fadb591cf --- /dev/null +++ b/drivers/infiniband/hw/efa/efa_ah_cache.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* + * Copyright 2026 Amazon.com, Inc. or its affiliates. All rights reserved. + */ + +#ifndef _EFA_AH_CACHE_H_ +#define _EFA_AH_CACHE_H_ + +#include +#include + +#define EFA_AH_GID_SIZE 16 + +struct efa_ah_cache_key { + u8 gid[EFA_AH_GID_SIZE]; + u16 pd; +}; + +struct efa_ah_cache_entry { + struct efa_ah_cache_key key; + u16 ah; + unsigned int usecnt; + refcount_t refcount; + struct rhash_head linkage; + struct mutex lock; /* Serializes device commands per cache entry */ +}; + +struct efa_ah_cache { + struct rhashtable hashtable; + struct mutex lock; /* Protects AH cache hashtable */ +}; + +int efa_ah_cache_init(struct efa_ah_cache *ah_cache); +void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache); + +#endif /* _EFA_AH_CACHE_H_ */ diff --git a/drivers/infiniband/hw/efa/efa_com.c b/drivers/infiniband/hw/efa/efa_com.c index 7cc3f4af0bb9..7097d1c2f23d 100644 --- a/drivers/infiniband/hw/efa/efa_com.c +++ b/drivers/infiniband/hw/efa/efa_com.c @@ -724,6 +724,8 @@ void efa_com_admin_destroy(struct efa_com_dev *edev) size = aenq->depth * sizeof(*aenq->entries); dma_free_coherent(edev->dmadev, size, aenq->entries, aenq->dma_addr); + + efa_ah_cache_destroy(&edev->ah_cache); } /** @@ -782,6 +784,12 @@ int efa_com_admin_init(struct efa_com_dev *edev, return -ENODEV; } + err = efa_ah_cache_init(&edev->ah_cache); + if (err) { + ibdev_err(edev->efa_dev, "Failed to init AH cache\n"); + return err; + } + aq->depth = EFA_ADMIN_QUEUE_DEPTH; aq->dmadev = edev->dmadev; @@ -794,7 +802,7 @@ int efa_com_admin_init(struct efa_com_dev *edev, err = efa_com_init_comp_ctxt(aq); if (err) - return err; + goto err_destroy_ah_cache; err = efa_com_admin_init_sq(edev); if (err) @@ -832,6 +840,8 @@ int efa_com_admin_init(struct efa_com_dev *edev, aq->sq.entries, aq->sq.dma_addr); err_destroy_comp_ctxt: devm_kfree(edev->dmadev, aq->comp_ctx); +err_destroy_ah_cache: + efa_ah_cache_destroy(&edev->ah_cache); return err; } diff --git a/drivers/infiniband/hw/efa/efa_com.h b/drivers/infiniband/hw/efa/efa_com.h index f8c692b0e092..599db9d583bf 100644 --- a/drivers/infiniband/hw/efa/efa_com.h +++ b/drivers/infiniband/hw/efa/efa_com.h @@ -14,6 +14,7 @@ #include +#include "efa_ah_cache.h" #include "efa_common_defs.h" #include "efa_admin_defs.h" #include "efa_admin_cmds_defs.h" @@ -113,6 +114,8 @@ struct efa_com_dev { u32 supported_features; u32 dma_addr_bits; + struct efa_ah_cache ah_cache; + u32 dev_api_ver; struct efa_com_mmio_read mmio_read; }; From 234895fa8be06e5085ec07fb665d391a88aa5746 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Mon, 6 Jul 2026 17:00:08 +0000 Subject: [PATCH 020/160] RDMA/efa: Add AH cache handling on create and destroy AH On create AH, first check if the AH cache entry already exists and if so, returns the already stored AH number. If the entry doesn't exist, the driver creates it and calls the device to create the AH. A per-entry mutex serializes concurrent device commands on the same AH cache entry, ensuring only one thread issues the device create while others wait and reuse the result. If the device create fails, the entry's user count remains zero so subsequent threads will retry the device create. On destroy AH, the user count is decremented under the entry mutex. If it reaches zero, the driver issues the device destroy command. After the device destroy completes, it removes the entry from the hashtable and frees it if no other references exist. If new users arrived during the destroy, the entry remains in the hashtable for reuse. Reviewed-by: Firas Jahjah Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Link: https://patch.msgid.link/20260706170008.1039417-3-ynachum@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_ah_cache.c | 94 ++++++++++++++++++++++++ drivers/infiniband/hw/efa/efa_ah_cache.h | 3 + drivers/infiniband/hw/efa/efa_com_cmd.c | 41 ++++++++++- drivers/infiniband/hw/efa/efa_com_cmd.h | 1 + drivers/infiniband/hw/efa/efa_verbs.c | 9 ++- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_ah_cache.c b/drivers/infiniband/hw/efa/efa_ah_cache.c index 39d0d2e97589..6219529fa889 100644 --- a/drivers/infiniband/hw/efa/efa_ah_cache.c +++ b/drivers/infiniband/hw/efa/efa_ah_cache.c @@ -39,3 +39,97 @@ void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache) rhashtable_free_and_destroy(&ah_cache->hashtable, efa_ah_cache_entry_free, NULL); mutex_destroy(&ah_cache->lock); } + +static struct efa_ah_cache_entry *efa_ah_cache_lookup_locked(struct efa_ah_cache *ah_cache, u16 pd, + u8 *gid) + __must_hold(&ah_cache->lock) +{ + struct efa_ah_cache_key key = {}; + + memcpy(key.gid, gid, sizeof(key.gid)); + key.pd = pd; + + return rhashtable_lookup_fast(&ah_cache->hashtable, &key, ah_cache_params); +} + +struct efa_ah_cache_entry *efa_ah_cache_lookup(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid) +{ + struct efa_ah_cache_entry *entry; + + mutex_lock(&ah_cache->lock); + entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid); + mutex_unlock(&ah_cache->lock); + + return entry; +} + +/** + * efa_ah_cache_get - Get or create an AH cache entry + * @ah_cache: AH cache + * @pd: Protection domain number + * @gid: GID address + * + * Look up an AH cache entry by PD and GID. If found, take a reference and + * return it. If not found, allocate a new entry and insert it. The caller must lock + * the entry mutex and check usecnt to determine whether a device create + * command is needed. + * + * Return: Pointer to the entry on success, ERR_PTR on failure. + */ +struct efa_ah_cache_entry *efa_ah_cache_get(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid) +{ + struct efa_ah_cache_entry *entry; + int err; + + mutex_lock(&ah_cache->lock); + + entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid); + if (entry) { + refcount_inc(&entry->refcount); + mutex_unlock(&ah_cache->lock); + return entry; + } + + entry = kzalloc_obj(*entry); + if (!entry) { + mutex_unlock(&ah_cache->lock); + return ERR_PTR(-ENOMEM); + } + + memcpy(entry->key.gid, gid, sizeof(entry->key.gid)); + entry->key.pd = pd; + refcount_set(&entry->refcount, 1); + mutex_init(&entry->lock); + + err = rhashtable_insert_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params); + if (err) { + mutex_destroy(&entry->lock); + kfree(entry); + mutex_unlock(&ah_cache->lock); + return ERR_PTR(err); + } + + mutex_unlock(&ah_cache->lock); + return entry; +} + +/** + * efa_ah_cache_put - Put a refcount of an AH cache entry + * @ah_cache: AH cache + * @entry: AH cache entry + * + * Drop the refcount. If it reaches zero, remove the entry from the hashtable + * and free it. + */ +void efa_ah_cache_put(struct efa_ah_cache *ah_cache, struct efa_ah_cache_entry *entry) +{ + if (!refcount_dec_and_mutex_lock(&entry->refcount, &ah_cache->lock)) + return; + + /* AH cache lock is held here */ + rhashtable_remove_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params); + mutex_unlock(&ah_cache->lock); + + mutex_destroy(&entry->lock); + kfree(entry); +} diff --git a/drivers/infiniband/hw/efa/efa_ah_cache.h b/drivers/infiniband/hw/efa/efa_ah_cache.h index 1d1fadb591cf..e7cdcbb64070 100644 --- a/drivers/infiniband/hw/efa/efa_ah_cache.h +++ b/drivers/infiniband/hw/efa/efa_ah_cache.h @@ -32,5 +32,8 @@ struct efa_ah_cache { int efa_ah_cache_init(struct efa_ah_cache *ah_cache); void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache); +struct efa_ah_cache_entry *efa_ah_cache_get(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid); +struct efa_ah_cache_entry *efa_ah_cache_lookup(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid); +void efa_ah_cache_put(struct efa_ah_cache *ah_cache, struct efa_ah_cache_entry *entry); #endif /* _EFA_AH_CACHE_H_ */ diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 5db4f5805b59..0b96862c2787 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -322,8 +322,21 @@ int efa_com_create_ah(struct efa_com_dev *edev, struct efa_admin_create_ah_resp cmd_completion; struct efa_com_admin_queue *aq = &edev->aq; struct efa_admin_create_ah_cmd ah_cmd = {}; + struct efa_ah_cache_entry *entry; int err; + entry = efa_ah_cache_get(&edev->ah_cache, params->pdn, params->dest_addr); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + mutex_lock(&entry->lock); + if (entry->usecnt) { + result->ah = entry->ah; + entry->usecnt++; + mutex_unlock(&entry->lock); + return 0; + } + ah_cmd.aq_common_desc.opcode = EFA_ADMIN_CREATE_AH; memcpy(ah_cmd.dest_addr, params->dest_addr, sizeof(ah_cmd.dest_addr)); @@ -335,13 +348,18 @@ int efa_com_create_ah(struct efa_com_dev *edev, (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { + mutex_unlock(&entry->lock); + efa_ah_cache_put(&edev->ah_cache, entry); ibdev_err_ratelimited(edev->efa_dev, "Failed to create ah for %pI6 [%d]\n", ah_cmd.dest_addr, err); return err; } + entry->ah = cmd_completion.ah; result->ah = cmd_completion.ah; + entry->usecnt++; + mutex_unlock(&entry->lock); return 0; } @@ -352,11 +370,20 @@ int efa_com_destroy_ah(struct efa_com_dev *edev, struct efa_admin_destroy_ah_resp cmd_completion; struct efa_admin_destroy_ah_cmd ah_cmd = {}; struct efa_com_admin_queue *aq = &edev->aq; - int err; + struct efa_ah_cache_entry *entry; + int err = 0; + + entry = efa_ah_cache_lookup(&edev->ah_cache, params->pdn, params->gid); + if (!entry) + return -EINVAL; + + mutex_lock(&entry->lock); + if (entry->usecnt > 1) + goto out_put; ah_cmd.aq_common_desc.opcode = EFA_ADMIN_DESTROY_AH; - ah_cmd.ah = params->ah; - ah_cmd.pd = params->pdn; + ah_cmd.ah = entry->ah; + ah_cmd.pd = entry->key.pd; err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&ah_cmd, @@ -364,13 +391,19 @@ int efa_com_destroy_ah(struct efa_com_dev *edev, (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { + mutex_unlock(&entry->lock); ibdev_err_ratelimited(edev->efa_dev, "Failed to destroy ah-%d pd-%d [%d]\n", ah_cmd.ah, ah_cmd.pd, err); return err; } - return 0; +out_put: + entry->usecnt--; + mutex_unlock(&entry->lock); + efa_ah_cache_put(&edev->ah_cache, entry); + + return err; } bool diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index ef15b3c38429..39bd4e06684a 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -106,6 +106,7 @@ struct efa_com_create_ah_result { struct efa_com_destroy_ah_params { u16 ah; + u8 gid[EFA_GID_SIZE]; u16 pdn; }; diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 06d3365aeb56..ec124fbda637 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -2054,10 +2054,11 @@ int efa_mmap(struct ib_ucontext *ibucontext, static int efa_ah_destroy(struct efa_dev *dev, struct efa_ah *ah) { - struct efa_com_destroy_ah_params params = { - .ah = ah->ah, - .pdn = to_epd(ah->ibah.pd)->pdn, - }; + struct efa_com_destroy_ah_params params = {}; + + params.ah = ah->ah; + memcpy(params.gid, ah->id, sizeof(params.gid)); + params.pdn = to_epd(ah->ibah.pd)->pdn; return efa_com_destroy_ah(&dev->edev, ¶ms); } From 923e1cb4e525bb3d6dd9c72a2821f924794ef570 Mon Sep 17 00:00:00 2001 From: Alexander Chesnokov Date: Thu, 9 Jul 2026 08:03:27 +0300 Subject: [PATCH 021/160] RDMA/hns: Compute HEM index in 64-bit in hns_roce_v2_set_hem() In hns_roce_v2_set_hem() the HEM address indices are computed from i, j and k (the base-chunk_ba_num decomposition of the 32-bit table_idx) in 32-bit arithmetic and then assigned to u64 fields. The recombined value always equals table_idx and cannot exceed U32_MAX, so this is not a reachable overflow and has no user-visible impact. Declare i, j and k as u64 so the calculation is done in 64-bit and the pattern no longer trips static analyzers. No functional change intended. Found by Linux Verification Center (linuxtesting.org) with SVACE. Suggested-by: David Laight Signed-off-by: Alexander Chesnokov Link: https://patch.msgid.link/20260709050327.3547237-1-Alexander.Chesnokov@kaspersky.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 2b3a1cafd1b2..27cc7df55ee7 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4393,7 +4393,7 @@ static int hns_roce_v2_set_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem_mhop mhop; struct hns_roce_hem *hem; unsigned long mhop_obj = obj; - int i, j, k; + u64 i, j, k; int ret = 0; u64 hem_idx = 0; u64 l1_idx = 0; From eebd08d91ddbd6d77cdde64b854f3f55317e165a Mon Sep 17 00:00:00 2001 From: Erni Sri Satya Vennela Date: Wed, 8 Jul 2026 22:51:29 -0700 Subject: [PATCH 022/160] RDMA: Change capability fields in ib_device_attr from int to u32 The capability counter fields in struct ib_device_attr are declared as signed int, but these values are inherently non-negative. Drivers maintain their cached caps as u32 and assign them directly into these int fields; if a cap exceeds INT_MAX the implicit narrowing yields a negative value visible to the IB core. Change the signed int capability fields to u32 to match the underlying nature of the data. Also update consumers across the IB core, ULPs, NVMe-oF target, RDS, and NFS/RDMA so the new u32 values are not forced back through signed int or u8 via min()/min_t() or narrowing local variables. The nvmet-rdma consumer of max_srq clamps it against ib_device.num_comp_vectors, which stays a signed int, so that site uses min_t() instead of min() to handle the signed/unsigned mismatch. Suggested-by: Jason Gunthorpe Signed-off-by: Erni Sri Satya Vennela Link: https://patch.msgid.link/20260709055211.2498307-1-ernis@linux.microsoft.com Reviewed-by: Andy Shevchenko Acked-by: Stefan Metzmacher # smbdirect Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/cq.c | 3 +- drivers/infiniband/core/nldev.c | 3 +- drivers/infiniband/core/restrack.c | 2 +- drivers/infiniband/hw/qedr/verbs.c | 2 +- drivers/infiniband/sw/rxe/rxe_qp.c | 22 ++++----- drivers/infiniband/sw/rxe/rxe_srq.c | 16 +++---- drivers/infiniband/ulp/ipoib/ipoib_cm.c | 10 ++--- drivers/infiniband/ulp/ipoib/ipoib_verbs.c | 3 +- drivers/infiniband/ulp/iser/iser_verbs.c | 5 +-- drivers/infiniband/ulp/isert/ib_isert.c | 7 ++- drivers/infiniband/ulp/rtrs/rtrs-clt.c | 11 ++--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 11 ++--- drivers/infiniband/ulp/srp/ib_srp.c | 2 +- drivers/infiniband/ulp/srpt/ib_srpt.c | 21 +++++---- drivers/nvme/host/rdma.c | 8 ++-- drivers/nvme/target/rdma.c | 26 ++++++----- fs/smb/smbdirect/accept.c | 5 ++- fs/smb/smbdirect/connect.c | 5 ++- fs/smb/smbdirect/connection.c | 8 ++-- include/linux/sunrpc/svc_rdma.h | 4 +- include/rdma/ib_verbs.h | 52 +++++++++++----------- include/rdma/restrack.h | 2 +- net/rds/ib.c | 10 ++--- net/rds/ib_cm.c | 10 ++--- net/sunrpc/xprtrdma/frwr_ops.c | 7 +-- net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 +-- net/sunrpc/xprtrdma/verbs.c | 2 +- 27 files changed, 129 insertions(+), 133 deletions(-) diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index 3d7b6cddd131..ee98188e57fb 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -393,8 +393,7 @@ static int ib_alloc_cqs(struct ib_device *dev, unsigned int nr_cqes, * a reasonable batch size so that we can share CQs between * multiple users instead of allocating a larger number of CQs. */ - nr_cqes = min_t(unsigned int, dev->attrs.max_cqe, - max(nr_cqes, IB_MAX_SHARED_CQ_SZ)); + nr_cqes = min(dev->attrs.max_cqe, max(nr_cqes, IB_MAX_SHARED_CQ_SZ)); nr_cqs = min_t(unsigned int, dev->num_comp_vectors, num_online_cpus()); for (i = 0; i < nr_cqs; i++) { cq = ib_alloc_cq(dev, NULL, nr_cqes, i, poll_ctx); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index f599c24b34e8..aae4f3f6bcba 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -454,7 +454,8 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, }; struct nlattr *table_attr; - int ret, i, curr, max; + u64 curr, max; + int ret, i; if (fill_nldev_handle(msg, device)) return -EMSGSIZE; diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index cfee2071586c..1b2f9df49e28 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -61,7 +61,7 @@ void rdma_restrack_clean(struct ib_device *dev) * @type: actual type of object to operate * @show_details: count driver specific objects */ -int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, +u32 rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, bool show_details) { struct rdma_restrack_root *rt = &dev->res[type]; diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index c90a1b5c8ee7..d5416b161340 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -148,7 +148,7 @@ int qedr_query_device(struct ib_device *ibdev, attr->max_qp_init_rd_atom = 1 << (fls(qattr->max_qp_req_rd_atomic_resc) - 1); attr->max_qp_rd_atom = - min(1 << (fls(qattr->max_qp_resp_rd_atomic_resc) - 1), + min(1U << (fls(qattr->max_qp_resp_rd_atomic_resc) - 1), attr->max_qp_init_rd_atom); attr->max_srq = qattr->max_srq; diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index f3dff1aea96a..7a0529a17992 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -67,27 +67,27 @@ static int rxe_qp_chk_cap(struct rxe_dev *rxe, struct ib_qp_cap *cap, int has_srq) { if (cap->max_send_wr > rxe->attr.max_qp_wr) { - rxe_dbg_dev(rxe, "invalid send wr = %u > %d\n", - cap->max_send_wr, rxe->attr.max_qp_wr); + rxe_dbg_dev(rxe, "invalid send wr = %u > %u\n", + cap->max_send_wr, rxe->attr.max_qp_wr); goto err1; } if (cap->max_send_sge > rxe->attr.max_send_sge) { - rxe_dbg_dev(rxe, "invalid send sge = %u > %d\n", - cap->max_send_sge, rxe->attr.max_send_sge); + rxe_dbg_dev(rxe, "invalid send sge = %u > %u\n", + cap->max_send_sge, rxe->attr.max_send_sge); goto err1; } if (!has_srq) { if (cap->max_recv_wr > rxe->attr.max_qp_wr) { - rxe_dbg_dev(rxe, "invalid recv wr = %u > %d\n", - cap->max_recv_wr, rxe->attr.max_qp_wr); + rxe_dbg_dev(rxe, "invalid recv wr = %u > %u\n", + cap->max_recv_wr, rxe->attr.max_qp_wr); goto err1; } if (cap->max_recv_sge > rxe->attr.max_recv_sge) { - rxe_dbg_dev(rxe, "invalid recv sge = %u > %d\n", - cap->max_recv_sge, rxe->attr.max_recv_sge); + rxe_dbg_dev(rxe, "invalid recv sge = %u > %u\n", + cap->max_recv_sge, rxe->attr.max_recv_sge); goto err1; } } @@ -537,9 +537,9 @@ int rxe_qp_chk_attr(struct rxe_dev *rxe, struct rxe_qp *qp, if (mask & IB_QP_MAX_QP_RD_ATOMIC) { if (attr->max_rd_atomic > rxe->attr.max_qp_rd_atom) { - rxe_dbg_qp(qp, "invalid max_rd_atomic %d > %d\n", - attr->max_rd_atomic, - rxe->attr.max_qp_rd_atom); + rxe_dbg_qp(qp, "invalid max_rd_atomic %u > %u\n", + attr->max_rd_atomic, + rxe->attr.max_qp_rd_atom); goto err1; } } diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c index c9a7cd38953d..74904a6fdf2b 100644 --- a/drivers/infiniband/sw/rxe/rxe_srq.c +++ b/drivers/infiniband/sw/rxe/rxe_srq.c @@ -13,8 +13,8 @@ int rxe_srq_chk_init(struct rxe_dev *rxe, struct ib_srq_init_attr *init) struct ib_srq_attr *attr = &init->attr; if (attr->max_wr > rxe->attr.max_srq_wr) { - rxe_dbg_dev(rxe, "max_wr(%d) > max_srq_wr(%d)\n", - attr->max_wr, rxe->attr.max_srq_wr); + rxe_dbg_dev(rxe, "max_wr(%u) > max_srq_wr(%u)\n", + attr->max_wr, rxe->attr.max_srq_wr); goto err1; } @@ -27,8 +27,8 @@ int rxe_srq_chk_init(struct rxe_dev *rxe, struct ib_srq_init_attr *init) attr->max_wr = RXE_MIN_SRQ_WR; if (attr->max_sge > rxe->attr.max_srq_sge) { - rxe_dbg_dev(rxe, "max_sge(%d) > max_srq_sge(%d)\n", - attr->max_sge, rxe->attr.max_srq_sge); + rxe_dbg_dev(rxe, "max_sge(%u) > max_srq_sge(%u)\n", + attr->max_sge, rxe->attr.max_srq_sge); goto err1; } @@ -107,8 +107,8 @@ int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq, if (mask & IB_SRQ_MAX_WR) { if (attr->max_wr > rxe->attr.max_srq_wr) { - rxe_dbg_srq(srq, "max_wr(%d) > max_srq_wr(%d)\n", - attr->max_wr, rxe->attr.max_srq_wr); + rxe_dbg_srq(srq, "max_wr(%u) > max_srq_wr(%u)\n", + attr->max_wr, rxe->attr.max_srq_wr); goto err1; } @@ -129,8 +129,8 @@ int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq, if (mask & IB_SRQ_LIMIT) { if (attr->srq_limit > rxe->attr.max_srq_wr) { - rxe_dbg_srq(srq, "srq_limit(%d) > max_srq_wr(%d)\n", - attr->srq_limit, rxe->attr.max_srq_wr); + rxe_dbg_srq(srq, "srq_limit(%u) > max_srq_wr(%u)\n", + attr->srq_limit, rxe->attr.max_srq_wr); goto err1; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 57fec88a1629..ed0592898384 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -1071,8 +1071,7 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ struct ib_qp *tx_qp; if (dev->features & NETIF_F_SG) - attr.cap.max_send_sge = min_t(u32, priv->ca->attrs.max_send_sge, - MAX_SKB_FRAGS + 1); + attr.cap.max_send_sge = min(priv->ca->attrs.max_send_sge, MAX_SKB_FRAGS + 1); tx_qp = ib_create_qp(priv->pd, &attr); tx->max_send_sge = attr.cap.max_send_sge; @@ -1582,7 +1581,8 @@ static void ipoib_cm_create_srq(struct net_device *dev, int max_sge) int ipoib_cm_dev_init(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); - int max_srq_sge, i; + u32 max_srq_sge; + int i; u8 addr; INIT_LIST_HEAD(&priv->cm.passive_ids); @@ -1600,9 +1600,9 @@ int ipoib_cm_dev_init(struct net_device *dev) skb_queue_head_init(&priv->cm.skb_queue); - ipoib_dbg(priv, "max_srq_sge=%d\n", priv->ca->attrs.max_srq_sge); + ipoib_dbg(priv, "max_srq_sge=%u\n", priv->ca->attrs.max_srq_sge); - max_srq_sge = min_t(int, IPOIB_CM_RX_SG, priv->ca->attrs.max_srq_sge); + max_srq_sge = min(priv->ca->attrs.max_srq_sge, IPOIB_CM_RX_SG); ipoib_cm_create_srq(dev, max_srq_sge); if (ipoib_cm_has_srq(dev)) { priv->cm.max_cm_mtu = max_srq_sge * PAGE_SIZE - 0x10; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c index 3ed1ea566690..2490696a1aab 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c @@ -147,8 +147,7 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca) .cap = { .max_send_wr = ipoib_sendq_size, .max_recv_wr = ipoib_recvq_size, - .max_send_sge = min_t(u32, priv->ca->attrs.max_send_sge, - MAX_SKB_FRAGS + 1), + .max_send_sge = min(priv->ca->attrs.max_send_sge, MAX_SKB_FRAGS + 1), .max_recv_sge = IPOIB_UD_RX_SG }, .sq_sig_type = IB_SIGNAL_ALL_WR, diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index f03b3bb3c0c4..55fe68e5b837 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -244,8 +244,7 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn) max_send_wr = ISER_QP_SIG_MAX_REQ_DTOS + 1; else max_send_wr = ISER_QP_MAX_REQ_DTOS + 1; - max_send_wr = min_t(unsigned int, max_send_wr, - (unsigned int)ib_dev->attrs.max_qp_wr); + max_send_wr = min(max_send_wr, ib_dev->attrs.max_qp_wr); cq_size = max_send_wr + ISER_QP_MAX_RECV_DTOS; ib_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_SOFTIRQ); @@ -589,7 +588,7 @@ static void iser_route_handler(struct rdma_cm_id *cma_id) goto failure; memset(&conn_param, 0, sizeof conn_param); - conn_param.responder_resources = ib_dev->attrs.max_qp_rd_atom; + conn_param.responder_resources = min(ib_dev->attrs.max_qp_rd_atom, U8_MAX); conn_param.initiator_depth = 1; conn_param.retry_count = 7; conn_param.rnr_retry_count = 6; diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 1015a51f750a..4691845bf815 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -214,9 +214,9 @@ isert_create_device_ib_res(struct isert_device *device) struct ib_device *ib_dev = device->ib_device; int ret; - isert_dbg("devattr->max_send_sge: %d devattr->max_recv_sge %d\n", + isert_dbg("devattr->max_send_sge: %u devattr->max_recv_sge %u\n", ib_dev->attrs.max_send_sge, ib_dev->attrs.max_recv_sge); - isert_dbg("devattr->max_sge_rd: %d\n", ib_dev->attrs.max_sge_rd); + isert_dbg("devattr->max_sge_rd: %u\n", ib_dev->attrs.max_sge_rd); device->pd = ib_alloc_pd(ib_dev, 0); if (IS_ERR(device->pd)) { @@ -381,8 +381,7 @@ isert_set_nego_params(struct isert_conn *isert_conn, struct ib_device_attr *attr = &isert_conn->device->ib_device->attrs; /* Set max inflight RDMA READ requests */ - isert_conn->initiator_depth = min_t(u8, param->initiator_depth, - attr->max_qp_init_rd_atom); + isert_conn->initiator_depth = min(param->initiator_depth, attr->max_qp_init_rd_atom); isert_dbg("Using initiator_depth: %u\n", isert_conn->initiator_depth); if (param->private_data) { diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index d34d7e5f34d6..7b2c51ae614f 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -1675,8 +1675,7 @@ static int create_con_cq_qp(struct rtrs_clt_con *con) * + 2 for drain and heartbeat * in case qp gets into error state. */ - max_send_wr = - min_t(int, wr_limit, SERVICE_CON_QUEUE_DEPTH * 2 + 2); + max_send_wr = min(wr_limit, SERVICE_CON_QUEUE_DEPTH * 2 + 2); max_recv_wr = max_send_wr; } else { /* @@ -1692,11 +1691,9 @@ static int create_con_cq_qp(struct rtrs_clt_con *con) wr_limit = clt_path->s.dev->ib_dev->attrs.max_qp_wr; /* Shared between connections */ clt_path->s.dev_ref++; - max_send_wr = min_t(int, wr_limit, - /* QD * (REQ + RSP + FR REGS or INVS) + drain */ - clt_path->queue_depth * 4 + 1); - max_recv_wr = min_t(int, wr_limit, - clt_path->queue_depth * 3 + 1); + /* QD * (REQ + RSP + FR REGS or INVS) + drain */ + max_send_wr = min(wr_limit, clt_path->queue_depth * 4 + 1); + max_recv_wr = min(wr_limit, clt_path->queue_depth * 3 + 1); max_send_sge = 2; } atomic_set(&con->c.sq_wr_avail, max_send_wr); diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index 7d8e4422cc57..5314599d29fe 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -1740,21 +1740,16 @@ static int create_con(struct rtrs_srv_path *srv_path, * All receive and all send (each requiring invalidate) * + 2 for drain and heartbeat */ - max_send_wr = min_t(int, wr_limit, - SERVICE_CON_QUEUE_DEPTH * 2 + 2); + max_send_wr = min(wr_limit, SERVICE_CON_QUEUE_DEPTH * 2 + 2); max_recv_wr = max_send_wr; s->signal_interval = min_not_zero(srv->queue_depth, (size_t)SERVICE_CON_QUEUE_DEPTH); } else { /* when always_invlaidate enalbed, we need linv+rinv+mr+imm */ if (always_invalidate) - max_send_wr = - min_t(int, wr_limit, - srv->queue_depth * (1 + 4) + 1); + max_send_wr = min(wr_limit, srv->queue_depth * (1 + 4) + 1); else - max_send_wr = - min_t(int, wr_limit, - srv->queue_depth * (1 + 2) + 1); + max_send_wr = min(wr_limit, srv->queue_depth * (1 + 2) + 1); max_recv_wr = srv->queue_depth + 1; } diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index acbd787de265..0caebbc2810f 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -557,7 +557,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) init_attr->cap.max_send_wr = m * target->queue_size; init_attr->cap.max_recv_wr = target->queue_size + 1; init_attr->cap.max_recv_sge = 1; - init_attr->cap.max_send_sge = min(SRP_MAX_SGE, attr->max_send_sge); + init_attr->cap.max_send_sge = min(attr->max_send_sge, SRP_MAX_SGE); init_attr->sq_sig_type = IB_SIGNAL_REQ_WR; init_attr->qp_type = IB_QPT_RC; init_attr->send_cq = send_cq; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index f66cfd70c263..a760b4fbee90 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -77,8 +77,8 @@ module_param(srp_max_req_size, int, 0444); MODULE_PARM_DESC(srp_max_req_size, "Maximum size of SRP request messages in bytes."); -static int srpt_srq_size = DEFAULT_SRPT_SRQ_SIZE; -module_param(srpt_srq_size, int, 0444); +static unsigned int srpt_srq_size = DEFAULT_SRPT_SRQ_SIZE; +module_param(srpt_srq_size, uint, 0444); MODULE_PARM_DESC(srpt_srq_size, "Shared receive queue (SRQ) size."); @@ -405,8 +405,7 @@ static void srpt_get_ioc(struct srpt_port *sport, u32 slot, if (sdev->use_srq) send_queue_depth = sdev->srq_size; else - send_queue_depth = min(MAX_SRPT_RQ_SIZE, - sdev->device->attrs.max_qp_wr); + send_queue_depth = min(sdev->device->attrs.max_qp_wr, MAX_SRPT_RQ_SIZE); memset(iocp, 0, sizeof(*iocp)); strcpy(iocp->id_string, SRPT_ID_STRING); @@ -1851,7 +1850,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) struct srpt_port *sport = ch->sport; struct srpt_device *sdev = sport->sdev; const struct ib_device_attr *attrs = &sdev->device->attrs; - int sq_size = sport->port_attrib.srp_sq_size; + u32 sq_size = sport->port_attrib.srp_sq_size; int i, ret; WARN_ON(ch->rq_size < 1); @@ -1912,13 +1911,13 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) bool retry = sq_size > MIN_SRPT_SQ_SIZE; if (retry) { - pr_debug("failed to create queue pair with sq_size = %d (%d) - retrying\n", + pr_debug("failed to create queue pair with sq_size = %u (%d) - retrying\n", sq_size, ret); ib_cq_pool_put(ch->cq, ch->cq_size); sq_size = max(sq_size / 2, MIN_SRPT_SQ_SIZE); goto retry; } else { - pr_err("failed to create queue pair with sq_size = %d (%d)\n", + pr_err("failed to create queue pair with sq_size = %u (%d)\n", sq_size, ret); goto err_destroy_cq; } @@ -1926,7 +1925,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); - pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p\n", + pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %u ch= %p\n", __func__, ch->cq->cqe, qp_init->cap.max_send_sge, qp_init->cap.max_send_wr, ch); @@ -2299,7 +2298,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, * depth to avoid that the initiator driver has to report QUEUE_FULL * to the SCSI mid-layer. */ - ch->rq_size = min(MAX_SRPT_RQ_SIZE, sdev->device->attrs.max_qp_wr); + ch->rq_size = min(sdev->device->attrs.max_qp_wr, MAX_SRPT_RQ_SIZE); spin_lock_init(&ch->spinlock); ch->state = CH_CONNECTING; INIT_LIST_HEAD(&ch->cmd_wait_list); @@ -3137,7 +3136,7 @@ static int srpt_alloc_srq(struct srpt_device *sdev) return PTR_ERR(srq); } - pr_debug("create SRQ #wr= %d max_allow=%d dev= %s\n", sdev->srq_size, + pr_debug("create SRQ #wr= %d max_allow=%u dev= %s\n", sdev->srq_size, sdev->device->attrs.max_srq_wr, dev_name(&device->dev)); sdev->req_buf_cache = srpt_cache_get(srp_max_req_size); @@ -3952,7 +3951,7 @@ static int __init srpt_init_module(void) if (srpt_srq_size < MIN_SRPT_SRQ_SIZE || srpt_srq_size > MAX_SRPT_SRQ_SIZE) { - pr_err("invalid value %d for kernel module parameter srpt_srq_size -- must be in the range [%d..%d].\n", + pr_err("invalid value %u for kernel module parameter srpt_srq_size -- must be in the range [%d..%d].\n", srpt_srq_size, MIN_SRPT_SRQ_SIZE, MAX_SRPT_SRQ_SIZE); goto out; } diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 6909e3542794..56cd228af1d5 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -394,8 +394,10 @@ nvme_rdma_find_get_device(struct rdma_cm_id *cm_id) goto out_free_pd; } - ndev->num_inline_segments = min(NVME_RDMA_MAX_INLINE_SEGMENTS, - ndev->dev->attrs.max_send_sge - 1); + ndev->num_inline_segments = ndev->dev->attrs.max_send_sge; + if (ndev->num_inline_segments) + ndev->num_inline_segments--; + ndev->num_inline_segments = min(ndev->num_inline_segments, NVME_RDMA_MAX_INLINE_SEGMENTS); list_add(&ndev->entry, &device_list); out_unlock: mutex_unlock(&device_list_mutex); @@ -1847,7 +1849,7 @@ static int nvme_rdma_route_resolved(struct nvme_rdma_queue *queue) param.qp_num = queue->qp->qp_num; param.flow_control = 1; - param.responder_resources = queue->device->dev->attrs.max_qp_rd_atom; + param.responder_resources = min(queue->device->dev->attrs.max_qp_rd_atom, U8_MAX); /* maximum retry count */ param.retry_count = 7; param.rnr_retry_count = 7; diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index ea1185b8267e..bdd28ce99faa 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -149,10 +149,10 @@ MODULE_PARM_DESC(use_srq, "Use shared receive queue."); static int srq_size_set(const char *val, const struct kernel_param *kp); static const struct kernel_param_ops srq_size_ops = { .set = srq_size_set, - .get = param_get_int, + .get = param_get_uint, }; -static int nvmet_rdma_srq_size = 1024; +static unsigned int nvmet_rdma_srq_size = 1024; module_param_cb(srq_size, &srq_size_ops, &nvmet_rdma_srq_size, 0644); MODULE_PARM_DESC(srq_size, "set Shared Receive Queue (SRQ) size, should >= 256 (default: 1024)"); @@ -180,13 +180,14 @@ static const struct nvmet_fabrics_ops nvmet_rdma_ops; static int srq_size_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; + unsigned int n; + int ret; - ret = kstrtoint(val, 10, &n); + ret = kstrtouint(val, 10, &n); if (ret != 0 || n < 256) return -EINVAL; - return param_set_int(val, kp); + return param_set_uint(val, kp); } static int num_pages(int len) @@ -1153,8 +1154,8 @@ static int nvmet_rdma_init_srqs(struct nvmet_rdma_device *ndev) ndev->srq_size = min(ndev->device->attrs.max_srq_wr, nvmet_rdma_srq_size); - ndev->srq_count = min(ndev->device->num_comp_vectors, - ndev->device->attrs.max_srq); + ndev->srq_count = min_t(u32, ndev->device->num_comp_vectors, + ndev->device->attrs.max_srq); ndev->srqs = kzalloc_objs(*ndev->srqs, ndev->srq_count); if (!ndev->srqs) @@ -1199,7 +1200,7 @@ nvmet_rdma_find_get_device(struct rdma_cm_id *cm_id) struct nvmet_port *nport = port->nport; struct nvmet_rdma_device *ndev; int inline_page_count; - int inline_sge_count; + u32 inline_sge_count; int ret; mutex_lock(&device_list_mutex); @@ -1215,7 +1216,9 @@ nvmet_rdma_find_get_device(struct rdma_cm_id *cm_id) inline_page_count = num_pages(nport->inline_data_size); inline_sge_count = max(cm_id->device->attrs.max_sge_rd, - cm_id->device->attrs.max_recv_sge) - 1; + cm_id->device->attrs.max_recv_sge); + if (inline_sge_count) + inline_sge_count--; if (inline_page_count > inline_sge_count) { pr_warn("inline_data_size %d cannot be supported by device %s. Reducing to %lu.\n", nport->inline_data_size, cm_id->device->name, @@ -1555,8 +1558,9 @@ static int nvmet_rdma_cm_accept(struct rdma_cm_id *cm_id, param.rnr_retry_count = 7; param.flow_control = 1; - param.initiator_depth = min_t(u8, p->initiator_depth, - queue->dev->device->attrs.max_qp_init_rd_atom); + param.initiator_depth = min3(p->initiator_depth, + queue->dev->device->attrs.max_qp_init_rd_atom, + U8_MAX); param.private_data = &priv; param.private_data_len = sizeof(priv); priv.recfmt = cpu_to_le16(NVME_RDMA_CM_FMT_1_0); diff --git a/fs/smb/smbdirect/accept.c b/fs/smb/smbdirect/accept.c index 529740005838..44b681a20725 100644 --- a/fs/smb/smbdirect/accept.c +++ b/fs/smb/smbdirect/accept.c @@ -32,8 +32,9 @@ int smbdirect_accept_connect_request(struct smbdirect_socket *sc, /* * First set what the we as server are able to support */ - sp->initiator_depth = min_t(u8, sp->initiator_depth, - sc->ib.dev->attrs.max_qp_rd_atom); + sp->initiator_depth = min3(sp->initiator_depth, + sc->ib.dev->attrs.max_qp_rd_atom, + U8_MAX); peer_initiator_depth = param->initiator_depth; peer_responder_resources = param->responder_resources; diff --git a/fs/smb/smbdirect/connect.c b/fs/smb/smbdirect/connect.c index cd726b399afe..34a3e72c38fb 100644 --- a/fs/smb/smbdirect/connect.c +++ b/fs/smb/smbdirect/connect.c @@ -182,8 +182,9 @@ static int smbdirect_connect_rdma_connect(struct smbdirect_socket *sc) if (sc->ib.dev->attrs.kernel_cap_flags & IBK_SG_GAPS_REG) sc->mr_io.type = IB_MR_TYPE_SG_GAPS; - sp->responder_resources = min_t(u8, sp->responder_resources, - sc->ib.dev->attrs.max_qp_rd_atom); + sp->responder_resources = min3(sp->responder_resources, + sc->ib.dev->attrs.max_qp_rd_atom, + U8_MAX); smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_INFO, "responder_resources=%d\n", sp->responder_resources); diff --git a/fs/smb/smbdirect/connection.c b/fs/smb/smbdirect/connection.c index 8adf58097534..690acb84e1b5 100644 --- a/fs/smb/smbdirect/connection.c +++ b/fs/smb/smbdirect/connection.c @@ -287,7 +287,7 @@ int smbdirect_connection_create_qp(struct smbdirect_socket *sc) qp_cap.max_send_wr > sc->ib.dev->attrs.max_qp_wr) { pr_err("Possible CQE overrun: max_send_wr %d\n", qp_cap.max_send_wr); - pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + pr_err("device %.*s reporting max_cqe %u max_qp_wr %u\n", IB_DEVICE_NAME_MAX, sc->ib.dev->name, sc->ib.dev->attrs.max_cqe, @@ -302,7 +302,7 @@ int smbdirect_connection_create_qp(struct smbdirect_socket *sc) max_send_wr >= sc->ib.dev->attrs.max_qp_wr)) { pr_err("Possible CQE overrun: rdma_send_wr %d + max_send_wr %d = %d\n", rdma_send_wr, qp_cap.max_send_wr, max_send_wr); - pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + pr_err("device %.*s reporting max_cqe %u max_qp_wr %u\n", IB_DEVICE_NAME_MAX, sc->ib.dev->name, sc->ib.dev->attrs.max_cqe, @@ -316,7 +316,7 @@ int smbdirect_connection_create_qp(struct smbdirect_socket *sc) qp_cap.max_recv_wr > sc->ib.dev->attrs.max_qp_wr) { pr_err("Possible CQE overrun: max_recv_wr %d\n", qp_cap.max_recv_wr); - pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + pr_err("device %.*s reporting max_cqe %u max_qp_wr %u\n", IB_DEVICE_NAME_MAX, sc->ib.dev->name, sc->ib.dev->attrs.max_cqe, @@ -328,7 +328,7 @@ int smbdirect_connection_create_qp(struct smbdirect_socket *sc) if (qp_cap.max_send_sge > sc->ib.dev->attrs.max_send_sge || qp_cap.max_recv_sge > sc->ib.dev->attrs.max_recv_sge) { - pr_err("device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", + pr_err("device %.*s max_send_sge/max_recv_sge = %u/%u too small\n", IB_DEVICE_NAME_MAX, sc->ib.dev->name, sc->ib.dev->attrs.max_send_sge, diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 5aadb47b3b0e..76aa5ec4ab40 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -77,8 +77,8 @@ struct svcxprt_rdma { struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ struct list_head sc_accept_q; /* Conn. waiting accept */ struct rpcrdma_notification sc_rn; /* removal notification */ - int sc_ord; /* RDMA read limit */ - int sc_max_send_sges; + u32 sc_ord; /* RDMA read limit */ + unsigned int sc_max_send_sges; bool sc_snd_w_inv; /* OK to use Send With Invalidate */ atomic_t sc_sq_avail; /* SQEs ready to be consumed */ diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 794746de8db0..8d82d303b723 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -407,36 +407,36 @@ struct ib_device_attr { u32 vendor_id; u32 vendor_part_id; u32 hw_ver; - int max_qp; - int max_qp_wr; + u32 max_qp; + u32 max_qp_wr; u64 device_cap_flags; u64 kernel_cap_flags; - int max_send_sge; - int max_recv_sge; - int max_sge_rd; - int max_cq; - int max_cqe; - int max_mr; - int max_pd; - int max_qp_rd_atom; - int max_ee_rd_atom; - int max_res_rd_atom; - int max_qp_init_rd_atom; - int max_ee_init_rd_atom; + u32 max_send_sge; + u32 max_recv_sge; + u32 max_sge_rd; + u32 max_cq; + u32 max_cqe; + u32 max_mr; + u32 max_pd; + u32 max_qp_rd_atom; + u32 max_ee_rd_atom; + u32 max_res_rd_atom; + u32 max_qp_init_rd_atom; + u32 max_ee_init_rd_atom; enum ib_atomic_cap atomic_cap; enum ib_atomic_cap masked_atomic_cap; - int max_ee; - int max_rdd; - int max_mw; - int max_raw_ipv6_qp; - int max_raw_ethy_qp; - int max_mcast_grp; - int max_mcast_qp_attach; - int max_total_mcast_qp_attach; - int max_ah; - int max_srq; - int max_srq_wr; - int max_srq_sge; + u32 max_ee; + u32 max_rdd; + u32 max_mw; + u32 max_raw_ipv6_qp; + u32 max_raw_ethy_qp; + u32 max_mcast_grp; + u32 max_mcast_qp_attach; + u32 max_total_mcast_qp_attach; + u32 max_ah; + u32 max_srq; + u32 max_srq_wr; + u32 max_srq_sge; unsigned int max_fast_reg_page_list_len; unsigned int max_pi_fast_reg_page_list_len; u16 max_pkeys; diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h index 451f99e3717d..c081384740ce 100644 --- a/include/rdma/restrack.h +++ b/include/rdma/restrack.h @@ -123,7 +123,7 @@ struct rdma_restrack_entry { u32 id; }; -int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, +u32 rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, bool show_details); /** * rdma_is_kernel_res() - check the owner of resource diff --git a/net/rds/ib.c b/net/rds/ib.c index 39f87272e071..c62684d4259c 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -162,12 +162,12 @@ static int rds_ib_add_one(struct ib_device *device) IB_ODP_SUPPORT_READ); rds_ibdev->max_1m_mrs = device->attrs.max_mr ? - min_t(unsigned int, (device->attrs.max_mr / 2), - rds_ib_mr_1m_pool_size) : rds_ib_mr_1m_pool_size; + min(device->attrs.max_mr / 2, + rds_ib_mr_1m_pool_size) : rds_ib_mr_1m_pool_size; rds_ibdev->max_8k_mrs = device->attrs.max_mr ? - min_t(unsigned int, ((device->attrs.max_mr / 2) * RDS_MR_8K_SCALE), - rds_ib_mr_8k_pool_size) : rds_ib_mr_8k_pool_size; + min((device->attrs.max_mr / 2) * RDS_MR_8K_SCALE, + rds_ib_mr_8k_pool_size) : rds_ib_mr_8k_pool_size; rds_ibdev->max_initiator_depth = device->attrs.max_qp_init_rd_atom; rds_ibdev->max_responder_resources = device->attrs.max_qp_rd_atom; @@ -204,7 +204,7 @@ static int rds_ib_add_one(struct ib_device *device) goto put_dev; } - rdsdebug("RDS/IB: max_mr = %d, max_wrs = %d, max_sge = %d, max_1m_mrs = %d, max_8k_mrs = %d\n", + rdsdebug("RDS/IB: max_mr = %u, max_wrs = %d, max_sge = %d, max_1m_mrs = %d, max_8k_mrs = %d\n", device->attrs.max_mr, rds_ibdev->max_wrs, rds_ibdev->max_sge, rds_ibdev->max_1m_mrs, rds_ibdev->max_8k_mrs); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 5667f0173b47..17e587c30076 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -173,11 +173,11 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn, memset(conn_param, 0, sizeof(struct rdma_conn_param)); - conn_param->responder_resources = - min_t(u32, rds_ibdev->max_responder_resources, max_responder_resources); - conn_param->initiator_depth = - min_t(u32, rds_ibdev->max_initiator_depth, max_initiator_depth); - conn_param->retry_count = min_t(unsigned int, rds_ib_retry_count, 7); + conn_param->responder_resources = min3(rds_ibdev->max_responder_resources, + max_responder_resources, U8_MAX); + conn_param->initiator_depth = min3(rds_ibdev->max_initiator_depth, + max_initiator_depth, U8_MAX); + conn_param->retry_count = min(rds_ib_retry_count, 7U); conn_param->rnr_retry_count = 7; if (dp) { diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index e5c71cf705a3..e83cef19e656 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -172,8 +172,9 @@ int frwr_mr_init(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr *mr) int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device) { const struct ib_device_attr *attrs = &device->attrs; - int max_qp_wr, depth, delta; unsigned int max_sge; + u32 max_qp_wr; + int depth, delta; if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) || attrs->max_fast_reg_page_list_len == 0) { @@ -229,10 +230,10 @@ int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device) } max_qp_wr = attrs->max_qp_wr; + if (max_qp_wr < RPCRDMA_BACKWARD_WRS + 1 + RPCRDMA_MIN_SLOT_TABLE) + return -ENOMEM; max_qp_wr -= RPCRDMA_BACKWARD_WRS; max_qp_wr -= 1; - if (max_qp_wr < RPCRDMA_MIN_SLOT_TABLE) - return -ENOMEM; if (ep->re_max_requests > max_qp_wr) ep->re_max_requests = max_qp_wr; ep->re_attr.cap.max_send_wr = ep->re_max_requests * depth; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 7ca71741106b..e0289adce7f5 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -562,8 +562,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) set_bit(RDMAXPRT_CONN_PENDING, &newxprt->sc_flags); memset(&conn_param, 0, sizeof conn_param); conn_param.responder_resources = 0; - conn_param.initiator_depth = min_t(int, newxprt->sc_ord, - dev->attrs.max_qp_init_rd_atom); + conn_param.initiator_depth = min(newxprt->sc_ord, dev->attrs.max_qp_init_rd_atom); if (!conn_param.initiator_depth) { ret = -EINVAL; trace_svcrdma_initdepth_err(newxprt, ret); @@ -588,7 +587,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) dprintk(" local address : %pIS:%u\n", sap, rpc_get_port(sap)); sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr; dprintk(" remote address : %pIS:%u\n", sap, rpc_get_port(sap)); - dprintk(" max_sge : %d\n", newxprt->sc_max_send_sges); + dprintk(" max_sge : %u\n", newxprt->sc_max_send_sges); dprintk(" sq_depth : %d\n", newxprt->sc_sq_depth); dprintk(" rdma_rw_ctxs : %d\n", ctxts); dprintk(" max_requests : %d\n", newxprt->sc_max_requests); diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 04b286223b24..be335eed329d 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -465,7 +465,7 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt) /* Client offers RDMA Read but does not initiate */ ep->re_remote_cma.initiator_depth = 0; ep->re_remote_cma.responder_resources = - min_t(int, U8_MAX, device->attrs.max_qp_rd_atom); + min(device->attrs.max_qp_rd_atom, U8_MAX); /* Limit transport retries so client can detect server * GID changes quickly. RPC layer handles re-establishing From 15ae32c4a3551c4c9da457370bdfdd65d171e512 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sat, 11 Jul 2026 09:54:19 -0700 Subject: [PATCH 023/160] RDMA/rxe: Avoid reprocessing the current packet after the QP enters the error state When do_complete() finds the QP in the error state it returns RESPST_CHK_RESOURCE. Before commit 49dc9c1f0c7e ("RDMA/rxe: Cleanup reset state handling in rxe_resp.c") this was the flush loop: check_resource() had an error-state branch that fetched each remaining recv WQE and completed it with IB_WC_WR_FLUSH_ERR, without touching the current packet. That commit removed the error-state branch from check_resource() (draining is now done at rxe_receiver() entry) but kept the do_complete() error-state return. As a result, when a QP moves to the error state while a packet is being completed - e.g. an rdma_cm disconnect racing with receive processing - the responder state machine loops back into the request processing chain with the already-completed packet still in hand: check_resource() fetches a fresh recv WQE, execute()/send_data_in() copies the same packet payload again, do_complete() posts another IB_WC_SUCCESS CQE (qp->resp.status is still 0), and control returns to the error-state check. The loop re-executes the same packet once per posted recv WQE (observed: ~1000 duplicate IB_WC_SUCCESS completions of one SEND, one per ~8us, matching the RQ occupancy) until the RQ is exhausted, after which qp->resp.wqe is NULL and send_data_in() dereferences it: BUG: kernel NULL pointer dereference, address: 0000000000000014 Workqueue: rxe_wq do_work RIP: copy_data+0x29/0x1f0 Call Trace: send_data_in+0x25/0x50 rxe_receiver+0xf36/0x1dd0 The duplicate completions are indistinguishable from real receives to the ULP. During an rds stress test, the message was accepted as new and delivered the same datagram to user space hundreds of times, corrupting the stream; any ULP that relies on RC exactly-once delivery is affected. A live packet reaching the error-state check in do_complete() has been executed and completed exactly once and must be consumed, not re-processed. Return RESPST_CLEANUP for it (dequeue and free); keep returning RESPST_CHK_RESOURCE for the pkt == NULL case. Fixes: 49dc9c1f0c7e ("RDMA/rxe: Cleanup reset state handling in rxe_resp.c") Assisted-by: Claude-Code:claude-fable-5 Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260711165419.13486-1-achender@kernel.org Reviewed-by: Zhu Yanjun Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_resp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index d8cbdfa70cdb..02b16e2b49b8 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -1217,7 +1217,14 @@ static enum resp_states do_complete(struct rxe_qp *qp, spin_lock_irqsave(&qp->state_lock, flags); if (unlikely(qp_state(qp) == IB_QPS_ERR)) { spin_unlock_irqrestore(&qp->state_lock, flags); - return RESPST_CHK_RESOURCE; + /* The packet was executed and completed before the QP + * moved to ERROR; it must be consumed exactly once. + * Re-entering the request chain with the stale packet + * would copy it into every remaining recv WQE as a new + * completion. Remaining WQEs are flushed by the drain + * path at rxe_receiver() entry. + */ + return pkt ? RESPST_CLEANUP : RESPST_CHK_RESOURCE; } spin_unlock_irqrestore(&qp->state_lock, flags); From 9539e6196604715a93972e4ac31378b358a16767 Mon Sep 17 00:00:00 2001 From: Praveen Kumar Kannoju Date: Mon, 15 Jun 2026 17:17:59 +0000 Subject: [PATCH 024/160] IB/mlx4: delete allocated id_map_entry while sending REJ The mlx4 CM paravirtualization layer rewrites a VF's local communication ID to a PF-visible ID when CM MADs are sent from the VF. For messages that start or advance a connection from the VF side, such as REQ, REP, MRA and SIDR_REQ, mlx4_ib_multiplex_cm_handler() allocates an id_map_entry when no existing mapping is found. A REJ is different because it is a terminal response to an already known exchange. It should either find an existing id_map_entry, rewrite the local communication ID, and schedule that entry for deletion, or it should pass through unchanged when no mapping exists. Some REJ messages, such as rejects for an inbound REQ before an MRA or REP was sent, do not have an id_map_entry because their local_comm_id is zero. Timeout REJ messages are handled in the initial lookup branch, but a lookup miss there must not fall through to id_map_alloc(); such a miss means there is no existing mapping to translate or delete for the REJ. Commit 227a0e142e37 ("IB/mlx4: Add support for REJ due to timeout") added the timeout REJ case to the initial branch so an outgoing timeout REJ could reuse the id_map_entry that was created when the VF's REQ was multiplexed. Reusing that entry is the useful part: it rewrites the timeout REJ local_comm_id to the same PF-visible ID that was sent in the REQ. If the lookup misses, allocating a new id_map_entry does not help because the peer has never seen that new PF-visible ID, and REJ is not starting a new exchange. Keep timeout REJ handling in the initial lookup branch, but return before allocation if no mapping is found. Handle the other REJ cases with the same lookup-only behavior. When a mapping is found, translate the local communication ID and schedule delayed deletion, as is already done for DREQ and for received REJ in the demux path. When no mapping is found, keep the existing pass-through behavior. Signed-off-by: Praveen Kumar Kannoju Link: https://patch.msgid.link/20260615171759.557425-1-praveen.kannoju@oracle.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx4/cm.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index 63a868a3822f..202fd5365e35 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -315,14 +315,20 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); if (id) goto cont; + if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) + return 0; id = id_map_alloc(ibdev, slave_id, sl_cm_id); if (IS_ERR(id)) { mlx4_ib_warn(ibdev, "%s: id{slave: %d, sl_cm_id: 0x%x} Failed to id_map_alloc\n", __func__, slave_id, sl_cm_id); return PTR_ERR(id); } - } else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID || - mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) { + } else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) { + sl_cm_id = get_local_comm_id(mad); + id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); + if (!id) + return 0; + } else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) { return 0; } else { sl_cm_id = get_local_comm_id(mad); @@ -338,7 +344,8 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id cont: set_local_comm_id(mad, id->pv_cm_id); - if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID) + if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID || + mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) schedule_delayed(ibdev, id); return 0; } From 2982eaf3b9d2d953318c74cd6f1b7576ea6d7b1f Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:23 -0400 Subject: [PATCH 025/160] RDMA/rvt: Return NULL after port allocation failure rvt_alloc_device() deallocates the IB device when its port array cannot be allocated but then returns the pointer to the released allocation. Callers treat any non-NULL value as valid and dereference it, resulting in a use-after-free. Return NULL immediately after deallocation so callers can propagate the allocation failure. Fixes: ff6acd69518e ("IB/rdmavt: Add device structure allocation") Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-1-b9e9641268a5@nvidia.com Reviewed-by: Kalesh AP Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rdmavt/vt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index 5fa3a1f33326..f37d6d64adb9 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -55,8 +55,10 @@ struct rvt_dev_info *rvt_alloc_device(size_t size, int nports) return rdi; rdi->ports = kzalloc_objs(*rdi->ports, nports); - if (!rdi->ports) + if (!rdi->ports) { ib_dealloc_device(&rdi->ibdev); + return NULL; + } return rdi; } From 2e3809ad8911f5d5581b3f046bd628417bface76 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:23 -0400 Subject: [PATCH 026/160] RDMA/hfi1: Preserve unit 0 on allocation failure hfi1_free_devdata() assumes that the device was inserted into the unit table and unconditionally erases dd->unit. If xa_alloc_irq() fails, the zero-initialized unit remains zero, so full cleanup can remove an unrelated device from index 0. Release only the rdmavt allocation and return immediately while the unit table has not acquired the device. Fixes: 03b92789e5cf ("hfi1: Convert hfi1_unit_table to XArray") Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-2-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index b7fd8b1fbbbd..3a408399f9ab 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1225,8 +1225,9 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, GFP_KERNEL); if (ret < 0) { dev_err(&pdev->dev, - "Could not allocate unit ID: error %d\n", -ret); - goto bail; + "Could not allocate unit ID: error %pe\n", ERR_PTR(ret)); + rvt_dealloc_device(&dd->verbs_dev.rdi); + return ERR_PTR(ret); } /* From af9117d02f50514c998714b23820de71d0aa5d24 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:24 -0400 Subject: [PATCH 027/160] RDMA/hfi1: Remove redundant PCI device ID validation The PCI core calls init_one() only after pci_match_device() has selected an ID. For normal probing, hfi1_pci_tbl already restricts matches to the two supported Intel device IDs. Dynamic IDs and driver_override are explicit requests to attempt binding, so the probe should not second-guess the PCI core's decision. Remove the redundant check. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-3-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 3a408399f9ab..7c0383657ad0 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1571,15 +1571,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* First, lock the non-writable module parameters */ HFI1_CAP_LOCK(); - /* Validate dev ids */ - if (!(ent->device == PCI_DEVICE_ID_INTEL0 || - ent->device == PCI_DEVICE_ID_INTEL1)) { - dev_err(&pdev->dev, "Failing on unknown Intel deviceid 0x%x\n", - ent->device); - ret = -ENODEV; - goto bail; - } - /* Allocate the dd so we can get to work */ dd = hfi1_alloc_devdata(pdev, NUM_IB_PORTS * sizeof(struct hfi1_pportdata)); From b9cb5e81f7d90ff83c5e18bbdcce6d3e3bd48a2f Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:24 -0400 Subject: [PATCH 028/160] RDMA/hfi1: Pass PCI device to hfi1_pcie_init() hfi1_pcie_init() only needs hfi1_devdata to reach the PCI device. This unnecessary dependency prevents common PCI setup from running before hfi1_devdata is allocated. Pass pci_dev directly and report failures with dev_err(), preserving the device BDF needed to identify the failing adapter on multi-device systems. Use %pe while changing the messages so errno values are decoded. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-4-b9e9641268a5@nvidia.com Reviewed-by: Kalesh AP Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/hfi.h | 2 +- drivers/infiniband/hw/hfi1/init.c | 2 +- drivers/infiniband/hw/hfi1/pcie.c | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 5a0310f758dc..95f86a002a3d 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -2132,7 +2132,7 @@ void hfi1_verbs_unregister_sysfs(struct hfi1_devdata *dd); /* Hook for sysfs read of QSFP */ int qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len); -int hfi1_pcie_init(struct hfi1_devdata *dd); +int hfi1_pcie_init(struct pci_dev *pdev); void hfi1_pcie_cleanup(struct pci_dev *pdev); int hfi1_pcie_ddinit(struct hfi1_devdata *dd, struct pci_dev *pdev); void hfi1_pcie_ddcleanup(struct hfi1_devdata *); diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 7c0383657ad0..a37a875736f7 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1620,7 +1620,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* restrict value of hfi1_rcvarr_split */ hfi1_rcvarr_split = clamp_val(hfi1_rcvarr_split, 0, 100); - ret = hfi1_pcie_init(dd); + ret = hfi1_pcie_init(pdev); if (ret) goto bail; diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c index 7133964749f8..7ca8f07ba43e 100644 --- a/drivers/infiniband/hw/hfi1/pcie.c +++ b/drivers/infiniband/hw/hfi1/pcie.c @@ -21,10 +21,9 @@ /* * Do all the common PCIe setup and initialization. */ -int hfi1_pcie_init(struct hfi1_devdata *dd) +int hfi1_pcie_init(struct pci_dev *pdev) { int ret; - struct pci_dev *pdev = dd->pcidev; ret = pci_enable_device(pdev); if (ret) { @@ -40,13 +39,15 @@ int hfi1_pcie_init(struct hfi1_devdata *dd) * about that, it appears. If the original BAR was retained * in the kernel data structures, this may be OK. */ - dd_dev_err(dd, "pci enable failed: error %d\n", -ret); + dev_err(&pdev->dev, "pci enable failed: error %pe\n", + ERR_PTR(ret)); return ret; } ret = pci_request_regions(pdev, DRIVER_NAME); if (ret) { - dd_dev_err(dd, "pci_request_regions fails: err %d\n", -ret); + dev_err(&pdev->dev, "pci_request_regions fails: err %pe\n", + ERR_PTR(ret)); goto bail; } @@ -59,7 +60,8 @@ int hfi1_pcie_init(struct hfi1_devdata *dd) */ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { - dd_dev_err(dd, "Unable to set DMA mask: %d\n", ret); + dev_err(&pdev->dev, "Unable to set DMA mask: %pe\n", + ERR_PTR(ret)); goto bail; } } From 4ebd241071af81dd75db31a96b7bd0f79b5e0813 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:24 -0400 Subject: [PATCH 029/160] RDMA/hfi1: Drop device data from hfi1_validate_rcvhdrcnt() hfi1_validate_rcvhdrcnt() only needs hfi1_devdata to identify the adapter in error messages. Requiring the full device data prevents module parameter validation from running before hfi1_devdata is allocated. Pass pci_dev instead and use dev_err(), allowing validation to move earlier without losing the PCI BDF needed on multi-device systems. Use %u for the unsigned count while changing the messages. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-5-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/chip.c | 17 +++++++++-------- drivers/infiniband/hw/hfi1/chip.h | 2 +- drivers/infiniband/hw/hfi1/init.c | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 44c524e45396..a41dd67c50bf 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -11929,26 +11929,27 @@ u8 encode_rcv_header_entry_size(u8 size) /** * hfi1_validate_rcvhdrcnt - validate hdrcnt - * @dd: the device data + * @pdev: the PCI device * @thecnt: the header count */ -int hfi1_validate_rcvhdrcnt(struct hfi1_devdata *dd, uint thecnt) +int hfi1_validate_rcvhdrcnt(struct pci_dev *pdev, uint thecnt) { if (thecnt <= HFI1_MIN_HDRQ_EGRBUF_CNT) { - dd_dev_err(dd, "Receive header queue count too small\n"); + dev_err(&pdev->dev, "Receive header queue count too small\n"); return -EINVAL; } if (thecnt > HFI1_MAX_HDRQ_EGRBUF_CNT) { - dd_dev_err(dd, - "Receive header queue count cannot be greater than %u\n", - HFI1_MAX_HDRQ_EGRBUF_CNT); + dev_err(&pdev->dev, + "Receive header queue count cannot be greater than %u\n", + HFI1_MAX_HDRQ_EGRBUF_CNT); return -EINVAL; } if (thecnt % HDRQ_INCREMENT) { - dd_dev_err(dd, "Receive header queue count %d must be divisible by %lu\n", - thecnt, HDRQ_INCREMENT); + dev_err(&pdev->dev, + "Receive header queue count %u must be divisible by %lu\n", + thecnt, HDRQ_INCREMENT); return -EINVAL; } diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h index 56e03d486ace..bc1c9e1c3172 100644 --- a/drivers/infiniband/hw/hfi1/chip.h +++ b/drivers/infiniband/hw/hfi1/chip.h @@ -660,7 +660,7 @@ static inline u32 chip_rcv_array_count(struct hfi1_devdata *dd) } u8 encode_rcv_header_entry_size(u8 size); -int hfi1_validate_rcvhdrcnt(struct hfi1_devdata *dd, uint thecnt); +int hfi1_validate_rcvhdrcnt(struct pci_dev *pdev, uint thecnt); void set_hdrq_regs(struct hfi1_devdata *dd, u8 ctxt, u8 entsize, u16 hdrcnt); u64 create_pbc(struct hfi1_pportdata *ppd, u64 flags, int srate_mbs, u32 vl, diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index a37a875736f7..fd91a4b4812d 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1580,7 +1580,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Validate some global module parameters */ - ret = hfi1_validate_rcvhdrcnt(dd, rcvhdrcnt); + ret = hfi1_validate_rcvhdrcnt(pdev, rcvhdrcnt); if (ret) goto bail; From 0d5618c1b2fc9dd4fc086f0226acd8a077ab6c1b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:24 -0400 Subject: [PATCH 030/160] RDMA/hfi1: Create workqueues before device initialization create_workqueues() only needs fields set up by hfi1_alloc_devdata(). Call it before hfi1_init_dd() so a workqueue allocation failure happens before chip resources are initialized. To keep the reordered error paths safe, make init_one() own hfi1_devdata. hfi1_init_dd() unwinds its partial setup but leaves the allocation for the caller to free. If device initialization fails, destroy the workqueues before freeing the device data. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-6-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/chip.c | 4 +--- drivers/infiniband/hw/hfi1/hfi.h | 2 -- drivers/infiniband/hw/hfi1/init.c | 19 ++++++++++--------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index a41dd67c50bf..592e330e74bf 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -15008,7 +15008,7 @@ int hfi1_init_dd(struct hfi1_devdata *dd) */ ret = hfi1_pcie_ddinit(dd, pdev); if (ret < 0) - goto bail_free; + goto bail; /* Save PCI space registers to rewrite after device reset */ ret = save_pci_variables(dd); @@ -15263,8 +15263,6 @@ int hfi1_init_dd(struct hfi1_devdata *dd) bail_cleanup: hfi1_free_rx(dd); hfi1_pcie_ddcleanup(dd); -bail_free: - hfi1_free_devdata(dd); bail: return ret; } diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 95f86a002a3d..80d480c4fc6a 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -2024,9 +2024,7 @@ struct cc_state *get_cc_state_protected(struct hfi1_pportdata *ppd) /* waiting for an urgent packet to arrive */ #define HFI1_CTXT_WAITING_URG 4 -/* free up any allocated data at closes */ int hfi1_init_dd(struct hfi1_devdata *dd); -void hfi1_free_devdata(struct hfi1_devdata *dd); /* LED beaconing functions */ void hfi1_start_led_override(struct hfi1_pportdata *ppd, unsigned int timeon, diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index fd91a4b4812d..076ea9527b6e 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -629,8 +629,6 @@ void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd, ppd->sm_trap_qp = 0x0; ppd->sa_qp = 0x1; - ppd->hfi1_wq = NULL; - spin_lock_init(&ppd->cca_timer_lock); for (i = 0; i < OPA_MAX_SLS; i++) { @@ -1161,7 +1159,7 @@ static void finalize_asic_data(struct hfi1_devdata *dd, * It cleans up and frees all data structures set up by * by hfi1_alloc_devdata(). */ -void hfi1_free_devdata(struct hfi1_devdata *dd) +static void hfi1_free_devdata(struct hfi1_devdata *dd) { struct hfi1_asic_data *ad; unsigned long flags; @@ -1624,17 +1622,17 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto bail; + ret = create_workqueues(dd); + if (ret) + goto free_devdata; + /* * Do device-specific initialization, function table setup, dd * allocation, etc. */ ret = hfi1_init_dd(dd); if (ret) - goto clean_bail; /* error already printed */ - - ret = create_workqueues(dd); - if (ret) - goto clean_bail; + goto destroy_workqueues; /* error already printed */ /* do the generic initialization */ initfail = hfi1_init(dd, 0); @@ -1687,7 +1685,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; -clean_bail: +destroy_workqueues: + destroy_workqueues(dd); +free_devdata: + hfi1_free_devdata(dd); hfi1_pcie_cleanup(pdev); bail: return ret; From 8e17e101e04a3dc062e2719da57ff78c1c060632 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:24 -0400 Subject: [PATCH 031/160] RDMA/hfi1: Free RX data on late probe failure hfi1_init_dd() allocates the shared AIP/VNIC RX support before returning. If hfi1_init() or hfi1_register_ib_device() later fails, init_one() tears down the device data without calling hfi1_free_rx(). This leaks netdev_rx and its dummy netdev. Free the RX support after IB unregistration and before postinit_cleanup(), as done on normal device removal. Fixes: 4730f4a6c6b2 ("IB/hfi1: Activate the dummy netdev") Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-7-b9e9641268a5@nvidia.com Reviewed-by: Kalesh AP Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 076ea9527b6e..79e253ac61f3 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1675,6 +1675,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hfi1_device_remove(dd); if (!ret) hfi1_unregister_ib_device(dd); + hfi1_free_rx(dd); postinit_cleanup(dd); if (initfail) ret = initfail; From 9f674ba674a0d1d6c418752c237364e12620dd89 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 032/160] RDMA/hfi1: Allocate device data after PCI initialization After the preceding changes, module parameter validation and common PCI setup no longer need hfi1_devdata. init_one() nevertheless allocates it first, so failures in those early steps return without releasing it. Move the allocation after hfi1_pcie_init() and return directly when no resources are held. Use dev_err() and pci_info() for diagnostics emitted before allocation so they retain the adapter BDF. Once PCI setup succeeds, unwind allocation failures through hfi1_pcie_cleanup() to disable the device and release its regions. Some PCI error delivery paths are not serialized against probe. Keep their diagnostics based on pci_dev and skip resume while driver data is absent, preventing recovery from dereferencing a missing hfi1_devdata. Fixes: 57f97e96625f ("IB/hfi1: Get the hfi1_devdata structure as early as possible") Reported-by: Dawei Feng Closes: https://lore.kernel.org/all/20260627060159.2543686-1-dawei.feng@seu.edu.cn/ Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-8-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 42 ++++++++++++++----------------- drivers/infiniband/hw/hfi1/pcie.c | 20 +++++++-------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 79e253ac61f3..0545180b5a11 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1569,25 +1569,16 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* First, lock the non-writable module parameters */ HFI1_CAP_LOCK(); - /* Allocate the dd so we can get to work */ - dd = hfi1_alloc_devdata(pdev, NUM_IB_PORTS * - sizeof(struct hfi1_pportdata)); - if (IS_ERR(dd)) { - ret = PTR_ERR(dd); - goto bail; - } - /* Validate some global module parameters */ ret = hfi1_validate_rcvhdrcnt(pdev, rcvhdrcnt); if (ret) - goto bail; + return ret; /* use the encoding function as a sanitization check */ if (!encode_rcv_header_entry_size(hfi1_hdrq_entsize)) { - dd_dev_err(dd, "Invalid HdrQ Entry size %u\n", - hfi1_hdrq_entsize); - ret = -EINVAL; - goto bail; + dev_err(&pdev->dev, "Invalid HdrQ Entry size %u\n", + hfi1_hdrq_entsize); + return -EINVAL; } /* The receive eager buffer size must be set before the receive @@ -1607,12 +1598,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) clamp_val(eager_buffer_size, MIN_EAGER_BUFFER * 8, MAX_EAGER_BUFFER_TOTAL); - dd_dev_info(dd, "Eager buffer size %u\n", - eager_buffer_size); + pci_info(pdev, "Eager buffer size %u\n", eager_buffer_size); } else { - dd_dev_err(dd, "Invalid Eager buffer size of 0\n"); - ret = -EINVAL; - goto bail; + dev_err(&pdev->dev, "Invalid Eager buffer size of 0\n"); + return -EINVAL; } /* restrict value of hfi1_rcvarr_split */ @@ -1620,15 +1609,22 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ret = hfi1_pcie_init(pdev); if (ret) - goto bail; + return ret; + + /* Allocate the dd so we can get to work */ + dd = hfi1_alloc_devdata(pdev, NUM_IB_PORTS * + sizeof(struct hfi1_pportdata)); + if (IS_ERR(dd)) { + ret = PTR_ERR(dd); + goto clean_pcie; + } ret = create_workqueues(dd); if (ret) goto free_devdata; /* - * Do device-specific initialization, function table setup, dd - * allocation, etc. + * Do device-specific initialization, function table setup, etc. */ ret = hfi1_init_dd(dd); if (ret) @@ -1679,7 +1675,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) postinit_cleanup(dd); if (initfail) ret = initfail; - goto bail; /* everything already cleaned */ + return ret; /* everything already cleaned */ } sdma_start(dd); @@ -1690,8 +1686,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) destroy_workqueues(dd); free_devdata: hfi1_free_devdata(dd); +clean_pcie: hfi1_pcie_cleanup(pdev); -bail: return ret; } diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c index 7ca8f07ba43e..1154b8cc713c 100644 --- a/drivers/infiniband/hw/hfi1/pcie.c +++ b/drivers/infiniband/hw/hfi1/pcie.c @@ -514,29 +514,28 @@ pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) switch (state) { case pci_channel_io_normal: - dd_dev_info(dd, "State Normal, ignoring\n"); + dev_info(&pdev->dev, "State Normal, ignoring\n"); break; case pci_channel_io_frozen: - dd_dev_info(dd, "State Frozen, requesting reset\n"); + dev_info(&pdev->dev, "State Frozen, requesting reset\n"); pci_disable_device(pdev); ret = PCI_ERS_RESULT_NEED_RESET; break; case pci_channel_io_perm_failure: + dev_info(&pdev->dev, "State Permanent Failure, disabling\n"); if (dd) { - dd_dev_info(dd, "State Permanent Failure, disabling\n"); /* no more register accesses! */ dd->flags &= ~HFI1_PRESENT; hfi1_disable_after_error(dd); } - /* else early, or other problem */ ret = PCI_ERS_RESULT_DISCONNECT; break; default: /* shouldn't happen */ - dd_dev_info(dd, "HFI1 PCI errors detected (state %d)\n", - state); + dev_info(&pdev->dev, "HFI1 PCI errors detected (state %d)\n", + state); break; } return ret; @@ -563,9 +562,7 @@ pci_mmio_enabled(struct pci_dev *pdev) static pci_ers_result_t pci_slot_reset(struct pci_dev *pdev) { - struct hfi1_devdata *dd = pci_get_drvdata(pdev); - - dd_dev_info(dd, "HFI1 slot_reset function called, ignored\n"); + dev_info(&pdev->dev, "HFI1 slot_reset function called, ignored\n"); return PCI_ERS_RESULT_CAN_RECOVER; } @@ -574,7 +571,10 @@ pci_resume(struct pci_dev *pdev) { struct hfi1_devdata *dd = pci_get_drvdata(pdev); - dd_dev_info(dd, "HFI1 resume function called\n"); + dev_info(&pdev->dev, "HFI1 resume function called\n"); + if (!dd) + return; + /* * Running jobs will fail, since it's asynchronous * unlike sysfs-requested reset. Better than From 22113f3f55a1f41359f9e3c0502f35fef619df28 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 033/160] RDMA/hfi1: Remove redundant NULL checks in create_workqueues() create_workqueues() is called only from init_one(), immediately after hfi1_alloc_devdata() returns a zero-initialized hfi1_devdata. As a result, the per-port hfi1_wq and link_wq pointers are always NULL on entry, and nothing modifies them between allocation and this call. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-9-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 56 +++++++++++++------------------ 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 0545180b5a11..ae30f6dd944c 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -738,31 +738,27 @@ static int create_workqueues(struct hfi1_devdata *dd) for (pidx = 0; pidx < dd->num_pports; ++pidx) { ppd = dd->pport + pidx; - if (!ppd->hfi1_wq) { - ppd->hfi1_wq = - alloc_workqueue( - "hfi%d_%d", - WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | - WQ_PERCPU, - HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES, - dd->unit, pidx); - if (!ppd->hfi1_wq) - goto wq_error; - } - if (!ppd->link_wq) { - /* - * Make the link workqueue single-threaded to enforce - * serialization. - */ - ppd->link_wq = - alloc_workqueue( - "hfi_link_%d_%d", - WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, - 1, /* max_active */ - dd->unit, pidx); - if (!ppd->link_wq) - goto wq_error; - } + ppd->hfi1_wq = + alloc_workqueue( + "hfi%d_%d", + WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | + WQ_PERCPU, + HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES, + dd->unit, pidx); + if (!ppd->hfi1_wq) + goto wq_error; + /* + * Make the link workqueue single-threaded to enforce + * serialization. + */ + ppd->link_wq = + alloc_workqueue( + "hfi_link_%d_%d", + WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, + 1, /* max_active */ + dd->unit, pidx); + if (!ppd->link_wq) + goto wq_error; } return 0; wq_error: @@ -1658,14 +1654,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) for (pidx = 0; pidx < dd->num_pports; ++pidx) { hfi1_quiet_serdes(dd->pport + pidx); ppd = dd->pport + pidx; - if (ppd->hfi1_wq) { - destroy_workqueue(ppd->hfi1_wq); - ppd->hfi1_wq = NULL; - } - if (ppd->link_wq) { - destroy_workqueue(ppd->link_wq); - ppd->link_wq = NULL; - } + destroy_workqueue(ppd->hfi1_wq); + destroy_workqueue(ppd->link_wq); } if (!j) hfi1_device_remove(dd); From d43b1c17f9e1b9d34a0d742f569c00d84147ebc0 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 034/160] RDMA/hfi1: Stop flushing the global IB workqueue hfi1 does not queue work on ib_wq. QSFP and link work run on the per-port link_wq, while the remaining device work uses hfi1_wq or dedicated queues. The probe failure path destroys both per-port workqueues, and normal device removal flushes them in shutdown_device() before destroying them. Remove the flushes of the core-owned global workqueue. Waiting for unrelated core or other device work is not part of hfi1 teardown. Fixes: 71d47008ca1b ("IB/hfi1: Create workqueue for link events") Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-10-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index ae30f6dd944c..6c642b68ad86 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1650,7 +1650,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (initfail || ret) { msix_clean_up_interrupts(dd); stop_timers(dd); - flush_workqueue(ib_wq); for (pidx = 0; pidx < dd->num_pports; ++pidx) { hfi1_quiet_serdes(dd->pport + pidx); ppd = dd->pport + pidx; @@ -1721,9 +1720,6 @@ static void remove_one(struct pci_dev *pdev) stop_timers(dd); - /* wait until all of our (qsfp) queue_work() calls complete */ - flush_workqueue(ib_wq); - postinit_cleanup(dd); } From 9bab31776ae60a1172f4405d792046c93bb7e93a Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 035/160] RDMA/hfi1: Defer device creation until probe succeeds init_one() creates the character device before checking whether generic or IB initialization failed, only to remove it immediately while unwinding. Moreover, user_add() already calls user_remove() when device creation fails. Move hfi1_device_create() after the initialization failure path, immediately before starting SDMA. The failure path then has no character device to remove, and hfi1_device_create() continues to unwind its own failures. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-11-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 6c642b68ad86..d17f319ad9f2 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1558,7 +1558,7 @@ static void postinit_cleanup(struct hfi1_devdata *dd) static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - int ret = 0, j, pidx, initfail; + int ret = 0, pidx, initfail; struct hfi1_devdata *dd; struct hfi1_pportdata *ppd; @@ -1633,9 +1633,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* * Now ready for use. this should be cleared whenever we - * detect a reset, or initiate one. If earlier failure, - * we still create devices, so diags, etc. can be used - * to determine cause of problem. + * detect a reset, or initiate one. */ if (!initfail && !ret) { dd->flags |= HFI1_INITTED; @@ -1643,10 +1641,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hfi1_dbg_ibdev_init(&dd->verbs_dev); } - j = hfi1_device_create(dd); - if (j) - dd_dev_err(dd, "Failed to create /dev devices: %d\n", -j); - if (initfail || ret) { msix_clean_up_interrupts(dd); stop_timers(dd); @@ -1656,8 +1650,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) destroy_workqueue(ppd->hfi1_wq); destroy_workqueue(ppd->link_wq); } - if (!j) - hfi1_device_remove(dd); if (!ret) hfi1_unregister_ib_device(dd); hfi1_free_rx(dd); @@ -1667,6 +1659,11 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return ret; /* everything already cleaned */ } + ret = hfi1_device_create(dd); + if (ret) + dd_dev_err(dd, "Failed to create /dev devices: %pe\n", + ERR_PTR(ret)); + sdma_start(dd); return 0; From bb18740b302f6f222ce3d5a7e5c45a52a90df805 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 036/160] RDMA/hfi1: Initialize debugfs after probe completes Commit ed6f653fe430 ("staging/rdma/hfi1: Fix debugfs access race") moved debugfs creation after device initialization and IB registration so users cannot access the files before the driver is ready. However, init_one() still creates them before character device creation and SDMA startup finish. Move hfi1_dbg_ibdev_init() to the end of the successful probe path, matching hfi1_dbg_ibdev_exit() as the first action in remove_one(). Fixes: ed6f653fe430 ("staging/rdma/hfi1: Fix debugfs access race") Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-12-b9e9641268a5@nvidia.com Reviewed-by: Kalesh AP Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index d17f319ad9f2..9ed7c1ffc93c 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1635,11 +1635,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) * Now ready for use. this should be cleared whenever we * detect a reset, or initiate one. */ - if (!initfail && !ret) { + if (!initfail && !ret) dd->flags |= HFI1_INITTED; - /* create debufs files after init and ib register */ - hfi1_dbg_ibdev_init(&dd->verbs_dev); - } if (initfail || ret) { msix_clean_up_interrupts(dd); @@ -1665,6 +1662,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ERR_PTR(ret)); sdma_start(dd); + hfi1_dbg_ibdev_init(&dd->verbs_dev); return 0; From e26c48cf23a4940208c5d82fcb37d20df234092b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 07:41:25 -0400 Subject: [PATCH 037/160] RDMA/hfi1: Align probe error unwinding with device removal init_one() defers handling errors from hfi1_init() and hfi1_register_ib_device() to a combined block. This allows IB registration to run after device initialization has failed. Cleanup then depends on two unrelated error values. The late probe failure path also differs from the common teardown in remove_one(), even though both release the same initialized hardware and driver resources. Maintaining separate sequences obscures ownership and allows the paths to drift whenever initialization changes. Unwind at each failing stage and keep late probe teardown ordered like remove_one(), so partial initialization releases exactly the resources it owns and normal removal remains the reference cleanup flow. Link: https://patch.msgid.link/20260708-clean-init-one-hfi1-v1-13-b9e9641268a5@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/init.c | 45 +++++++++++-------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 9ed7c1ffc93c..f94d896e7212 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1549,18 +1549,14 @@ static void postinit_cleanup(struct hfi1_devdata *dd) hfi1_dev_affinity_clean_up(dd); hfi1_pcie_ddcleanup(dd); - hfi1_pcie_cleanup(dd->pcidev); cleanup_device_data(dd); - - hfi1_free_devdata(dd); } static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - int ret = 0, pidx, initfail; + int ret; struct hfi1_devdata *dd; - struct hfi1_pportdata *ppd; /* First, lock the non-writable module parameters */ HFI1_CAP_LOCK(); @@ -1627,34 +1623,19 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto destroy_workqueues; /* error already printed */ /* do the generic initialization */ - initfail = hfi1_init(dd, 0); + ret = hfi1_init(dd, 0); + if (ret) + goto free_rx; ret = hfi1_register_ib_device(dd); + if (ret) + goto free_rx; /* * Now ready for use. this should be cleared whenever we * detect a reset, or initiate one. */ - if (!initfail && !ret) - dd->flags |= HFI1_INITTED; - - if (initfail || ret) { - msix_clean_up_interrupts(dd); - stop_timers(dd); - for (pidx = 0; pidx < dd->num_pports; ++pidx) { - hfi1_quiet_serdes(dd->pport + pidx); - ppd = dd->pport + pidx; - destroy_workqueue(ppd->hfi1_wq); - destroy_workqueue(ppd->link_wq); - } - if (!ret) - hfi1_unregister_ib_device(dd); - hfi1_free_rx(dd); - postinit_cleanup(dd); - if (initfail) - ret = initfail; - return ret; /* everything already cleaned */ - } + dd->flags |= HFI1_INITTED; ret = hfi1_device_create(dd); if (ret) @@ -1666,6 +1647,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +free_rx: + hfi1_free_rx(dd); + shutdown_device(dd); + stop_timers(dd); + postinit_cleanup(dd); + destroy_workqueues: destroy_workqueues(dd); free_devdata: @@ -1711,11 +1698,11 @@ static void remove_one(struct pci_dev *pdev) * clear dma engines, etc. */ shutdown_device(dd); - destroy_workqueues(dd); - stop_timers(dd); - postinit_cleanup(dd); + destroy_workqueues(dd); + hfi1_free_devdata(dd); + hfi1_pcie_cleanup(pdev); } static void shutdown_one(struct pci_dev *pdev) From 4266547bdce7425b6736f11c092951d3a54656e7 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Sun, 12 Jul 2026 13:44:12 +0000 Subject: [PATCH 038/160] RDMA/efa: Extend page-shift field in MR registration Update device interface adding one more bit from reserved to enable >4GB page sizes that can be supported on 0xefa4 devices. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260712134413.19226-2-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_admin_cmds_defs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 826790ca9d83..3eb3a4de8912 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -367,10 +367,10 @@ struct efa_admin_reg_mr_cmd { /* * flags and page size - * 4:0 : phys_page_size_shift - page size is (1 << + * 5:0 : phys_page_size_shift - page size is (1 << * phys_page_size_shift). Page size is used for * building the Virtual to Physical address mapping - * 6:5 : reserved - MBZ + * 6 : reserved - MBZ * 7 : mem_addr_phy_mode_en - Enable bit for physical * memory registration (no translation), can be used * only by privileged clients. If set, PBL must @@ -1103,7 +1103,7 @@ struct efa_admin_host_info { #define EFA_ADMIN_MODIFY_QP_CMD_RNR_RETRY_MASK BIT(5) /* reg_mr_cmd */ -#define EFA_ADMIN_REG_MR_CMD_PHYS_PAGE_SIZE_SHIFT_MASK GENMASK(4, 0) +#define EFA_ADMIN_REG_MR_CMD_PHYS_PAGE_SIZE_SHIFT_MASK GENMASK(5, 0) #define EFA_ADMIN_REG_MR_CMD_MEM_ADDR_PHY_MODE_EN_MASK BIT(7) #define EFA_ADMIN_REG_MR_CMD_LOCAL_WRITE_ENABLE_MASK BIT(0) #define EFA_ADMIN_REG_MR_CMD_REMOTE_WRITE_ENABLE_MASK BIT(1) From 6a57af838b1a56405a9bde9030de16df901ae2ca Mon Sep 17 00:00:00 2001 From: Anas Mousa Date: Sun, 12 Jul 2026 13:44:13 +0000 Subject: [PATCH 039/160] RDMA/efa: Add EFA 0xefa4 PCI ID Add support for 0xefa4 devices. Reviewed-by: Michael Margolin Signed-off-by: Anas Mousa Link: https://patch.msgid.link/20260712134413.19226-3-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/hw/efa/efa_main.c b/drivers/infiniband/hw/efa/efa_main.c index 97da8e828e34..ee09dc1e0b43 100644 --- a/drivers/infiniband/hw/efa/efa_main.c +++ b/drivers/infiniband/hw/efa/efa_main.c @@ -17,12 +17,14 @@ #define PCI_DEV_ID_EFA1_VF 0xefa1 #define PCI_DEV_ID_EFA2_VF 0xefa2 #define PCI_DEV_ID_EFA3_VF 0xefa3 +#define PCI_DEV_ID_EFA4_VF 0xefa4 static const struct pci_device_id efa_pci_tbl[] = { { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA0_VF) }, { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA1_VF) }, { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA2_VF) }, { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA3_VF) }, + { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA4_VF) }, { } }; From eeb9697db6c16d9bb2ce7b7ddf95aa20305aa9f2 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 13 Jul 2026 11:10:35 +0300 Subject: [PATCH 040/160] RDMA: Remove redundant memset() from query_device callbacks The core always hands the driver's query_device() callback a zeroed ib_device_attr. There are only two callers of the op and both clear the structure before invoking it: setup_device() memsets &device->attrs, and ib_uverbs_ex_query_device() passes an on-stack structure initialized to {}. The open-coded memset(props, 0, sizeof(*props)) at the top of the driver callbacks is therefore redundant. Remove it from all drivers. Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 1 - drivers/infiniband/hw/efa/efa_verbs.c | 1 - drivers/infiniband/hw/erdma/erdma_verbs.c | 2 -- drivers/infiniband/hw/hns/hns_roce_main.c | 2 -- drivers/infiniband/hw/irdma/verbs.c | 1 - drivers/infiniband/hw/mana/main.c | 1 - drivers/infiniband/hw/mlx4/main.c | 2 -- drivers/infiniband/hw/mlx5/main.c | 1 - drivers/infiniband/hw/mthca/mthca_provider.c | 2 -- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 1 - drivers/infiniband/hw/qedr/verbs.c | 2 -- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 1 - drivers/infiniband/sw/siw/siw_verbs.c | 2 -- 13 files changed, 19 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 565762529007..90138d64adee 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -193,7 +193,6 @@ int bnxt_re_query_device(struct ib_device *ibdev, if (rc) return rc; - memset(ib_attr, 0, sizeof(*ib_attr)); memcpy(&ib_attr->fw_ver, dev_attr->fw_ver, min(sizeof(dev_attr->fw_ver), sizeof(ib_attr->fw_ver))); diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index ec124fbda637..6be8ec53dcb3 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -222,7 +222,6 @@ int efa_query_device(struct ib_device *ibdev, dev_attr = &dev->dev_attr; - memset(props, 0, sizeof(*props)); props->max_mr_size = dev_attr->max_mr_pages * PAGE_SIZE; props->page_size_cap = dev_attr->page_size_cap; props->vendor_id = dev->pdev->vendor; diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 74afe6eb18b0..9491cbab69b3 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -324,8 +324,6 @@ int erdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, if (err) return err; - memset(attr, 0, sizeof(*attr)); - attr->max_mr_size = dev->attrs.max_mr_size; attr->vendor_id = PCI_VENDOR_ID_ALIBABA; attr->vendor_part_id = dev->pdev->device; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index c6f633bd5a34..09c07de5f022 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -227,8 +227,6 @@ static int hns_roce_query_device(struct ib_device *ib_dev, if (ret) return ret; - memset(props, 0, sizeof(*props)); - props->fw_ver = hr_dev->caps.fw_ver; props->sys_image_guid = cpu_to_be64(hr_dev->sys_image_guid); props->max_mr_size = (u64)(~(0ULL)); diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 5c907ffce99b..c9b606cc67d4 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -22,7 +22,6 @@ static int irdma_query_device(struct ib_device *ibdev, if (err) return err; - memset(props, 0, sizeof(*props)); addrconf_addr_eui48((u8 *)&props->sys_image_guid, iwdev->netdev->dev_addr); props->fw_ver = (u64)irdma_fw_major_ver(&rf->sc_dev) << 32 | diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index efe2935bda29..a5b3606a1dd5 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -579,7 +579,6 @@ int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props, if (err) return err; - memset(props, 0, sizeof(*props)); props->vendor_id = pdev->vendor; props->vendor_part_id = dev->gdma_dev->dev_id.type; props->max_mr_size = MANA_IB_MAX_MR_SIZE; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 17073e8f105a..7266a6141944 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -471,8 +471,6 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, if (err) goto out; - memset(props, 0, sizeof *props); - have_ib_ports = num_ib_ports(dev->dev); props->fw_ver = dev->dev->caps.fw_ver; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 02809114fc79..e8bba5a76d4e 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -969,7 +969,6 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, if (err) return err; - memset(props, 0, sizeof(*props)); err = mlx5_query_system_image_guid(ibdev, &props->sys_image_guid); if (err) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index f90f67afc8fa..e933a53779a4 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -69,8 +69,6 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *pr goto out; } - memset(props, 0, sizeof *props); - props->fw_ver = mdev->fw_ver; ib_init_query_mad(in_mad); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 53ba32d168a1..de83dc0ea79c 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -74,7 +74,6 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, if (err) return err; - memset(attr, 0, sizeof *attr); memcpy(&attr->fw_ver, &dev->attr.fw_ver[0], min(sizeof(dev->attr.fw_ver), sizeof(attr->fw_ver))); addrconf_addr_eui48((u8 *)&attr->sys_image_guid, diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index d5416b161340..1cf502579ad1 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -118,8 +118,6 @@ int qedr_query_device(struct ib_device *ibdev, if (rc) return rc; - memset(attr, 0, sizeof(*attr)); - attr->fw_ver = qattr->fw_ver; attr->sys_image_guid = qattr->sys_image_guid; attr->max_mr_size = qattr->max_mr_size; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index dc355b00f61c..3402c84eb904 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -284,7 +284,6 @@ int usnic_ib_query_device(struct ib_device *ibdev, mutex_lock(&us_ibdev->usdev_lock); us_ibdev->netdev->ethtool_ops->get_drvinfo(us_ibdev->netdev, &info); - memset(props, 0, sizeof(*props)); usnic_mac_ip_to_gid(us_ibdev->ufdev->mac, us_ibdev->ufdev->inaddr, &gid.raw[0]); memcpy(&props->sys_image_guid, &gid.global.interface_id, diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index b74ac85c1b8b..e281517fc6f9 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -136,8 +136,6 @@ int siw_query_device(struct ib_device *base_dev, struct ib_device_attr *attr, if (rv) return rv; - memset(attr, 0, sizeof(*attr)); - /* Revisit atomic caps if RFC 7306 gets supported */ attr->atomic_cap = 0; attr->device_cap_flags = IB_DEVICE_MEM_MGT_EXTENSIONS; From 66073100a7b8576e5037cac117b2a3fd3d9946e7 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 13 Jul 2026 10:17:22 +0300 Subject: [PATCH 041/160] RDMA/umem: ib_umem_get(): use kmalloc() to allocate page array ib_umem_get() allocates an array of pointers to struct page for pin_user_pages_fast() calls during memory registration. This array can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of __get_free_page() with kmalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260713-b4-rdma-v2-1-65d2a1a5180c@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/umem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 73498723a5d5..81f44dadfa52 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -209,7 +209,8 @@ static struct ib_umem *__ib_umem_get_va(struct ib_device *device, mmgrab(mm); - page_list = (struct page **) __get_free_page(GFP_KERNEL); + /* TODO: switch to "fast and as large as possible" allocation helper */ + page_list = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!page_list) { ret = -ENOMEM; goto umem_kfree; @@ -269,7 +270,7 @@ static struct ib_umem *__ib_umem_get_va(struct ib_device *device, __ib_umem_release(device, umem, 0); atomic64_sub(ib_umem_num_pages(umem), &mm->pinned_vm); out: - free_page((unsigned long) page_list); + kfree(page_list); umem_kfree: if (ret) { mmdrop(umem->owning_mm); From e3d8c413e2e9e870b74a5a9448a5c49eb143218b Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 13 Jul 2026 10:17:23 +0300 Subject: [PATCH 042/160] RDMA/mlx5: replace __get_free_page() with kmalloc() mlx5_ib_mr_wqe_pfault_handler() allocates a scratch buffer for parsing work queue entries during page fault handling. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of __get_free_page() with kmalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260713-b4-rdma-v2-2-65d2a1a5180c@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/odp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 1badec9bf527..b8618610737a 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "mlx5_ib.h" #include "cmd.h" @@ -1414,7 +1415,8 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, goto resolve_page_fault; } - wqe_start = (void *)__get_free_page(GFP_KERNEL); + /* TODO: switch to "fast and as large as possible" allocation helper */ + wqe_start = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!wqe_start) { mlx5_ib_err(dev, "Error allocating memory for IO page fault handling.\n"); goto resolve_page_fault; @@ -1475,7 +1477,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, pfault->wqe.wq_num, resume_with_error, pfault->type); mlx5_core_res_put(res); - free_page((unsigned long)wqe_start); + kfree(wqe_start); } static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_dev *dev, From 5036553f0e39e298a761427e9b4dc30f951cb78d Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 13 Jul 2026 10:17:24 +0300 Subject: [PATCH 043/160] IB/mthca: mthca_reg_user_mr(): use kmalloc() to allocate addresses array mthca_reg_user_mr() allocates an array of DMA addresses during memory registration. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of __get_free_page() with kmalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260713-b4-rdma-v2-3-65d2a1a5180c@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mthca/mthca_provider.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index e933a53779a4..05cca36f6a4b 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -893,7 +893,8 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, goto err_umem; } - pages = (u64 *) __get_free_page(GFP_KERNEL); + /* TODO: switch to "fast and as large as possible" allocation helper */ + pages = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!pages) { err = -ENOMEM; goto err_mtt; @@ -922,7 +923,7 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (i) err = mthca_write_mtt(dev, mr->mtt, n, pages, i); mtt_done: - free_page((unsigned long) pages); + kfree(pages); if (err) goto err_mtt; From 45bf0b3da47d75b227c35e95bc0ee15857820fa9 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 13 Jul 2026 10:17:25 +0300 Subject: [PATCH 044/160] IB/mthca: allocate mthca_array memory with kzalloc() mthca_array is essentially a sparse array of pointers and there is no need to allocate its memory using page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of get_zeroed_page() with kzalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260713-b4-rdma-v2-4-65d2a1a5180c@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mthca/mthca_allocator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mthca/mthca_allocator.c b/drivers/infiniband/hw/mthca/mthca_allocator.c index dedc301235a0..117a070e784e 100644 --- a/drivers/infiniband/hw/mthca/mthca_allocator.c +++ b/drivers/infiniband/hw/mthca/mthca_allocator.c @@ -126,7 +126,7 @@ int mthca_array_set(struct mthca_array *array, int index, void *value) /* Allocate with GFP_ATOMIC because we'll be called with locks held. */ if (!array->page_list[p].page) - array->page_list[p].page = (void **) get_zeroed_page(GFP_ATOMIC); + array->page_list[p].page = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (!array->page_list[p].page) return -ENOMEM; @@ -142,7 +142,7 @@ void mthca_array_clear(struct mthca_array *array, int index) int p = (index * sizeof (void *)) >> PAGE_SHIFT; if (--array->page_list[p].used == 0) { - free_page((unsigned long) array->page_list[p].page); + kfree(array->page_list[p].page); array->page_list[p].page = NULL; } else array->page_list[p].page[index & MTHCA_ARRAY_MASK] = NULL; @@ -174,7 +174,7 @@ void mthca_array_cleanup(struct mthca_array *array, int nent) int i; for (i = 0; i < (nent * sizeof (void *) + PAGE_SIZE - 1) / PAGE_SIZE; ++i) - free_page((unsigned long) array->page_list[i].page); + kfree(array->page_list[i].page); kfree(array->page_list); } From f8d04b0c74e989c515e0fa17bf779b730077f63e Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 13 Jul 2026 10:17:26 +0300 Subject: [PATCH 045/160] IB/rdmavt: use kzalloc() to allocate QPN-map pages get_map_page() allocates bitmap pages using get_zeroed_page(). The bitmaps can be allocated with kmalloc() as there's nothing special about them to go directly to the page allocator. kmalloc() provides a better API that does not require ugly casts and kfree() does not need to know the size of the freed object. Performance difference between kmalloc() and __get_free_pages() is not measurable as both allocators take an object/page from a per-CPU list for fast path allocations. For the slow path the performance is anyway determined by the amount of reclaim involved rather than by what allocator is used. Replace use of get_zeroed_page() with kzalloc() and free_page() with kfree(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Signed-off-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260713-b4-rdma-v2-5-65d2a1a5180c@kernel.org Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rdmavt/qp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 70e7d08fdce6..c40cce69e945 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -263,7 +263,7 @@ static inline bool wss_exceeds_threshold(struct rvt_wss *wss) static void get_map_page(struct rvt_qpn_table *qpt, struct rvt_qpn_map *map) { - unsigned long page = get_zeroed_page(GFP_KERNEL); + void *page = kzalloc(PAGE_SIZE, GFP_KERNEL); /* * Free the page if someone raced with us installing it. @@ -271,9 +271,9 @@ static void get_map_page(struct rvt_qpn_table *qpt, spin_lock(&qpt->lock); if (map->page) - free_page(page); + kfree(page); else - map->page = (void *)page; + map->page = page; spin_unlock(&qpt->lock); } @@ -343,7 +343,7 @@ static void free_qpn_table(struct rvt_qpn_table *qpt) int i; for (i = 0; i < ARRAY_SIZE(qpt->map); i++) - free_page((unsigned long)qpt->map[i].page); + kfree(qpt->map[i].page); } /** From f5ca2c434efe46e3f4e5c810facab658f653e78d Mon Sep 17 00:00:00 2001 From: xiongweimin Date: Tue, 14 Jul 2026 10:44:23 +0800 Subject: [PATCH 046/160] RDMA/cma: fix spelling of guarantees in comment Correct "guarentees" to "guarantees" when describing handler teardown. Signed-off-by: xiongweimin Link: https://patch.msgid.link/20260714024423.188238-1-15927021679@163.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/cma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 9480d1a51c11..3fbbf8d2f960 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2104,7 +2104,7 @@ static void destroy_id_handler_unlock(struct rdma_id_private *id_priv) /* * Setting the state to destroyed under the handler mutex provides a * fence against calling handler callbacks. If this is invoked due to - * the failure of a handler callback then it guarentees that no future + * the failure of a handler callback then it guarantees that no future * handlers will be called. */ lockdep_assert_held(&id_priv->handler_mutex); From 21680554a8d1c5d3fec68a580bf462ba89bb913b Mon Sep 17 00:00:00 2001 From: xiongweimin Date: Tue, 14 Jul 2026 10:44:29 +0800 Subject: [PATCH 047/160] RDMA/addr: fix spelling of guarantees in comment Correct "guarentees" to "guarantees" when describing work cancel. Signed-off-by: xiongweimin Link: https://patch.msgid.link/20260714024429.188276-1-15927021679@163.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index e9fb7ad4c377..bdaa3eb83bcc 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -769,7 +769,7 @@ void rdma_addr_cancel(struct rdma_dev_addr *addr) /* * sync canceling the work after removing it from the req_list - * guarentees no work is running and none will be started. + * guarantees no work is running and none will be started. */ cancel_delayed_work_sync(&found->work); kfree(found); From 9eab9eb2225912665fbee5fa13925232fd13881a Mon Sep 17 00:00:00 2001 From: xiongweimin Date: Mon, 13 Jul 2026 09:04:39 +0800 Subject: [PATCH 048/160] RDMA/rxe: Reject unimplemented implicit ODP cleanly rxe advertises ODP but not IB_ODP_SUPPORT_IMPLICIT. The reg_user_mr path still contained a dead branch that checked the implicit capability and could never succeed. Return -EOPNOTSUPP for the implicit ODP address range up front so the intent is obvious and the unreachable code is gone. Signed-off-by: xiongweimin Cc: linux-rdma@vger.kernel.org Cc: Jason Gunthorpe Link: https://patch.msgid.link/20260713010439.331054-1-15927021679@163.com Reviewed-by: Zhu Yanjun Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_odp.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_odp.c b/drivers/infiniband/sw/rxe/rxe_odp.c index ff904d5e54a7..c189c033175d 100644 --- a/drivers/infiniband/sw/rxe/rxe_odp.c +++ b/drivers/infiniband/sw/rxe/rxe_odp.c @@ -87,14 +87,9 @@ int rxe_odp_mr_init_user(struct rxe_dev *rxe, u64 start, u64 length, rxe_mr_init(access_flags, mr); - if (!start && length == U64_MAX) { - if (iova != 0) - return -EINVAL; - if (!(rxe->attr.odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) - return -EINVAL; - - /* Never reach here, for implicit ODP is not implemented. */ - } + /* Implicit ODP (start=0, length=U64_MAX) is not implemented. */ + if (!start && length == U64_MAX) + return -EOPNOTSUPP; umem_odp = ib_umem_odp_get(&rxe->ib_dev, start, length, access_flags, &rxe_mn_ops); From 1069f1c8213b492e9b05801cc4a3148ea7d8427a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 14 Jul 2026 10:19:06 +0200 Subject: [PATCH 049/160] RDMA/mlx5: Constify struct ib_frmr_pool_ops and dma_buf_attach_ops 'struct ib_frmr_pool_ops' and 'struct dma_buf_attach_ops' are not modified in this driver. Constifying these structures moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. While at it, change a '1' into a 'true' into the mlx5_ib_dmabuf_attach_ops structure. The 'allow_peer2peer' field is a bool and other usages of 'struct dma_buf_attach_ops' prefer using true/false. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 77631 10392 320 88343 15917 drivers/infiniband/hw/mlx5/mr.o After: ===== text data bss dec hex filename 77759 10264 320 88343 15917 drivers/infiniband/hw/mlx5/mr.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/22f2263c04cc94e242cee712e6e6d82b86ac353d.1784017128.git.christophe.jaillet@wanadoo.fr Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/mr.c | 6 +++--- include/rdma/ib_umem.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index e6b74955d95d..00e13028762a 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -339,7 +339,7 @@ static int mlx5r_build_frmr_key(struct ib_device *device, return 0; } -static struct ib_frmr_pool_ops mlx5r_frmr_pool_ops = { +static const struct ib_frmr_pool_ops mlx5r_frmr_pool_ops = { .create_frmrs = mlx5r_create_mkeys, .destroy_frmrs = mlx5r_destroy_mkeys, .build_key = mlx5r_build_frmr_key, @@ -898,8 +898,8 @@ static void mlx5_ib_dmabuf_invalidate_cb(struct dma_buf_attachment *attach) ib_umem_dmabuf_unmap_pages(umem_dmabuf); } -static struct dma_buf_attach_ops mlx5_ib_dmabuf_attach_ops = { - .allow_peer2peer = 1, +static const struct dma_buf_attach_ops mlx5_ib_dmabuf_attach_ops = { + .allow_peer2peer = true, .invalidate_mappings = mlx5_ib_dmabuf_invalidate_cb, }; diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 31b3a86fe73a..1fe87fd1d769 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -250,7 +250,7 @@ struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, unsigned long offset, size_t size, int fd, int access, - struct dma_buf_attach_ops *ops) + const struct dma_buf_attach_ops *ops) { return ERR_PTR(-EOPNOTSUPP); } From 6af255a94f2aaab4dcf8642d28cdc6b37ff2ddd8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 14 Jul 2026 10:47:32 +0200 Subject: [PATCH 050/160] RDMA/umem: Constify struct dma_buf_attach_ops 'struct dma_buf_attach_ops' are not modified in this driver. Constifying these structures moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 10300 1216 0 11516 2cfc drivers/infiniband/core/umem_dmabuf.o After: ===== text data bss dec hex filename 10428 1088 0 11516 2cfc drivers/infiniband/core/umem_dmabuf.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/3ca4ace543a02ccfdcce1ba568895c994aad7abb.1784018825.git.christophe.jaillet@wanadoo.fr Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/umem_dmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index ad023c2d84d8..39b5564a4c35 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -181,7 +181,7 @@ struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, } EXPORT_SYMBOL(ib_umem_dmabuf_get); -static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_ops = { +static const struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_ops = { .allow_peer2peer = true, }; @@ -205,7 +205,7 @@ static void ib_umem_dmabuf_revoke_locked(struct dma_buf_attachment *attach) umem_dmabuf->revoked = 1; } -static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_revocable_ops = { +static const struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_revocable_ops = { .allow_peer2peer = true, .invalidate_mappings = ib_umem_dmabuf_revoke_locked, }; From 091c6162c022cbdfb64219708a71728cfd1d4600 Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Wed, 8 Jul 2026 16:22:52 +0000 Subject: [PATCH 051/160] RDMA/hfi1: Propagate sdma_txinit_ahg() errors set_txreq_header_ahg() ignores the return value of sdma_txinit_ahg(). If sdma_txinit_ahg() fails, it returns before initializing tx->txreq. However, set_txreq_header_ahg() ignores the error and returns the AHG change count, causing the caller to continue processing the request as though initialization had succeeded. Propagate sdma_txinit_ahg() failures to the caller and abort request processing when initialization fails. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: e3304b7cc4f1 ("IB/hfi1: Optimize cachelines for user SDMA request structure") Signed-off-by: Danila Chernetsov Link: https://patch.msgid.link/20260708162252.936634-1-listdansp@mail.ru Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/user_sdma.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 8ea5ed918a02..be6b82ba93af 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -1026,6 +1026,7 @@ static int set_txreq_header_ahg(struct user_sdma_request *req, struct user_sdma_txreq *tx, u32 datalen) { u32 ahg[AHG_KDETH_ARRAY_SIZE]; + int ret; int idx = 0; u8 omfactor; /* KDETH.OM */ struct hfi1_user_sdma_pkt_q *pq = req->pq; @@ -1130,11 +1131,13 @@ static int set_txreq_header_ahg(struct user_sdma_request *req, trace_hfi1_sdma_user_header_ahg(pq->dd, pq->ctxt, pq->subctxt, req->info.comp_idx, req->sde->this_idx, req->ahg_idx, ahg, idx, tidval); - sdma_txinit_ahg(&tx->txreq, - SDMA_TXREQ_F_USE_AHG, - datalen, req->ahg_idx, idx, - ahg, sizeof(req->hdr), - user_sdma_txreq_cb); + ret = sdma_txinit_ahg(&tx->txreq, + SDMA_TXREQ_F_USE_AHG, + datalen, req->ahg_idx, idx, + ahg, sizeof(req->hdr), + user_sdma_txreq_cb); + if (ret) + return ret; return idx; } From 6f7014237405e7f032b5c53a82d9eccf6161c291 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Sun, 12 Jul 2026 14:17:20 +0200 Subject: [PATCH 052/160] RDMA/rxe: Fix responder UAF on IB_QP_MAX_DEST_RD_ATOMIC modify_qp rxe_qp_from_attr() handles IB_QP_MAX_DEST_RD_ATOMIC outside the IB_QP_STATE path, so it holds no state_lock and runs while the responder task rxe_receiver() (recv_task on rxe_wq) is live. A modify_qp() setting only that attribute calls free_rd_atomic_resources() then alloc_rd_atomic_resources(), swapping qp->resp.resources[] while rxe_prepare_res()/find_resource() walk it; free_rd_atomic_resources() also leaves the cached pointer qp->resp.res dangling. A local unprivileged user can race the free/realloc into a use-after-free in rxe_receiver() (local DoS). Drain recv_task around the swap with rxe_disable_task()/rxe_enable_task(), as rxe_qp_reset() already does when tearing this array down, re-enabling only after alloc_rd_atomic_resources() succeeds so the responder never resumes against a NULL qp->resp.resources on the ENOMEM path. Also clear qp->resp.res in free_rd_atomic_resources(), like the rxe_resp.c completion paths. Reproduced under KASAN; the slab-use-after-free in rxe_receiver() is gone. Fixes: 8700e3e7c485 ("Soft RoCE driver") Reviewed-by: Zhu Yanjun Signed-off-by: Ibrahim Hashimov Link: https://patch.msgid.link/20260712121720.78001-1-security@auditcode.ai Assisted-by: AuditCode-AI:2026.07 Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_qp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index 7a0529a17992..ec2cfb12559a 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -172,6 +172,7 @@ static void free_rd_atomic_resources(struct rxe_qp *qp) } kfree(qp->resp.resources); qp->resp.resources = NULL; + qp->resp.res = NULL; } } @@ -709,11 +710,23 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, qp->attr.max_dest_rd_atomic = max_dest_rd_atomic; + /* + * Not gated by IB_QP_STATE, so the responder task is live. + * Quiesce recv_task like rxe_qp_reset() before swapping the + * rd_atomic array, so rxe_receiver() cannot race the free/ + * realloc. + */ + rxe_disable_task(&qp->recv_task); free_rd_atomic_resources(qp); - err = alloc_rd_atomic_resources(qp, max_dest_rd_atomic); + /* + * On ENOMEM leave recv_task quiesced: qp->resp.resources is + * NULL and rxe_prepare_res()/find_resource() would deref it. + * Re-enable only after a fresh array is installed. + */ if (err) return err; + rxe_enable_task(&qp->recv_task); } if (mask & IB_QP_EN_SQD_ASYNC_NOTIFY) From 126c757e4cd46f866ddc283143b58eb4d9bf52cd Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Sun, 12 Jul 2026 14:21:49 +0200 Subject: [PATCH 053/160] RDMA/rxe: Validate num_sge/cur_sge before indexing wqe->dma.sge[] For a user QP, qp->sq.queue is a ring the application writes directly, so rxe_post_send() takes the is_user branch and only schedules send_task without validating the WQE. rxe_requester() consumes it in place via req_next_wqe() and calls copy_data(), which indexes &wqe->dma.sge[cur_sge] with the attacker-controlled num_sge/cur_sge. Only the kernel path bounds num_sge (validate_send_wr()); the user WQE is never checked, so a local unprivileged user can post a WQE with an out-of-range cur_sge or oversized num_sge and force an out-of-bounds read of the per-WQE sge array in copy_data() (vmalloc OOB read, local DoS). Bound num_sge to qp->sq.max_sge in rxe_requester() before use, the way get_srq_wqe() already guards SRQ entries, and bound cur_sge only when the WQE carries payload (dma.resid): copy_data() returns early on a zero-length copy before touching dma->sge[], so a zero-payload WQE -- the only kind a max_sge == 0 QP can post -- stays valid. Reproduced under KASAN; the vmalloc-out-of-bounds in copy_data() is gone. Fixes: 8700e3e7c485 ("Soft RoCE driver") Reviewed-by: Zhu Yanjun Signed-off-by: Ibrahim Hashimov Link: https://patch.msgid.link/20260712122149.78142-1-security@auditcode.ai Assisted-by: AuditCode-AI:2026.07 Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_req.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 12d03f390b09..24f5c044363f 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -701,6 +701,21 @@ int rxe_requester(struct rxe_qp *qp) if (unlikely(!wqe)) goto exit; + /* + * Don't trust user space data: a user QP's WQE comes from an mmap'd + * ring, so num_sge/cur_sge are attacker-controlled. Bound num_sge like + * get_srq_wqe(); bound cur_sge only when payload exists (dma.resid), + * since copy_data() skips dma->sge[] on a zero-length copy (all a + * max_sge == 0 QP can post). + */ + if (unlikely(wqe->dma.num_sge > qp->sq.max_sge || + (wqe->dma.resid && + wqe->dma.cur_sge >= qp->sq.max_sge))) { + rxe_dbg_qp(qp, "invalid num_sge/cur_sge in send wqe\n"); + wqe->status = IB_WC_LOC_QP_OP_ERR; + goto err; + } + if (rxe_wqe_is_fenced(qp, wqe)) { qp->req.wait_fence = 1; goto exit; From fbf03a08679ea02764f25f26564252e879cf14a4 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 16 Jul 2026 04:20:30 -0400 Subject: [PATCH 054/160] RDMA: Use ib_no_udata_io() in query_device callbacks The query_device callbacks that neither accept driver-specific input nor return a driver-specific response open-code the empty udata handling as ib_is_udata_in_empty() on entry and ib_respond_empty_udata() on exit. ib_no_udata_io() already combines both steps, so replace the entry check with it and simply return 0 on success. Unlike the create and destroy flows, query_device owns no uobject or HW resource - the extended path fills a stack ib_device_attr that the core discards on error - so clearing the empty response buffer on entry rather than on exit is a mechanical change with no functional difference. Link: https://patch.msgid.link/20260714-convert-to-noio-udata-v1-1-f1f6b6c7c988@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/cxgb4/provider.c | 4 ++-- drivers/infiniband/hw/erdma/erdma_verbs.c | 4 ++-- drivers/infiniband/hw/hns/hns_roce_main.c | 4 ++-- drivers/infiniband/hw/ionic/ionic_ibdev.c | 4 ++-- drivers/infiniband/hw/irdma/verbs.c | 4 ++-- drivers/infiniband/hw/mana/main.c | 4 ++-- drivers/infiniband/hw/mthca/mthca_provider.c | 3 +-- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 4 ++-- drivers/infiniband/hw/qedr/verbs.c | 4 ++-- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 4 ++-- drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c | 4 ++-- drivers/infiniband/sw/rdmavt/vt.c | 4 ++-- drivers/infiniband/sw/rxe/rxe_verbs.c | 4 ++-- drivers/infiniband/sw/siw/siw_verbs.c | 4 ++-- 14 files changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index e1eec37ee822..ebe3170a641c 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -263,7 +263,7 @@ static int c4iw_query_device(struct ib_device *ibdev, struct ib_device_attr *pro pr_debug("ibdev %p\n", ibdev); - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -300,7 +300,7 @@ static int c4iw_query_device(struct ib_device *ibdev, struct ib_device_attr *pro props->max_fast_reg_page_list_len = t4_max_fr_depth(dev->rdev.lldi.ulptx_memwrite_dsgl && use_dsgl); - return ib_respond_empty_udata(uhw); + return 0; } static int c4iw_query_port(struct ib_device *ibdev, u32 port, diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 9491cbab69b3..71e3e8618a61 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -320,7 +320,7 @@ int erdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, struct erdma_dev *dev = to_edev(ibdev); int err; - err = ib_is_udata_in_empty(udata); + err = ib_no_udata_io(udata); if (err) return err; @@ -361,7 +361,7 @@ int erdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, addrconf_addr_eui48((u8 *)&attr->sys_image_guid, dev->netdev->dev_addr); - return ib_respond_empty_udata(udata); + return 0; } int erdma_query_gid(struct ib_device *ibdev, u32 port, int idx, diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 09c07de5f022..662959efcf31 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -223,7 +223,7 @@ static int hns_roce_query_device(struct ib_device *ib_dev, struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev); int ret; - ret = ib_is_udata_in_empty(uhw); + ret = ib_no_udata_io(uhw); if (ret) return ret; @@ -277,7 +277,7 @@ static int hns_roce_query_device(struct ib_device *ib_dev, if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_XRC) props->device_cap_flags |= IB_DEVICE_XRC; - return ib_respond_empty_udata(uhw); + return 0; } static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num, diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c index b0449c75f893..2b91bd03c73e 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.c +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c @@ -27,7 +27,7 @@ static int ionic_query_device(struct ib_device *ibdev, struct net_device *ndev; int err; - err = ib_is_udata_in_empty(udata); + err = ib_no_udata_io(udata); if (err) return err; @@ -74,7 +74,7 @@ static int ionic_query_device(struct ib_device *ibdev, attr->max_fast_reg_page_list_len = dev->lif_cfg.npts_per_lif / 2; attr->max_pkeys = IONIC_PKEY_TBL_LEN; - return ib_respond_empty_udata(udata); + return 0; } static int ionic_query_port(struct ib_device *ibdev, u32 port, diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index c9b606cc67d4..f48cc8b5299b 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -18,7 +18,7 @@ static int irdma_query_device(struct ib_device *ibdev, struct irdma_hw_attrs *hw_attrs = &rf->sc_dev.hw_attrs; int err; - err = ib_is_udata_in_empty(udata); + err = ib_no_udata_io(udata); if (err) return err; @@ -75,7 +75,7 @@ static int irdma_query_device(struct ib_device *ibdev, if (hw_attrs->uk_attrs.hw_rev >= IRDMA_GEN_3) props->device_cap_flags |= IB_DEVICE_MEM_WINDOW_TYPE_2B; - return ib_respond_empty_udata(udata); + return 0; } /** diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index a5b3606a1dd5..73f4bfb22b5e 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -575,7 +575,7 @@ int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props, struct pci_dev *pdev = to_pci_dev(mdev_to_gc(dev)->dev); int err; - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -604,7 +604,7 @@ int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props, if (!mana_ib_is_rnic(dev)) props->raw_packet_caps = IB_RAW_PACKET_CAP_IP_CSUM; - return ib_respond_empty_udata(uhw); + return 0; } int mana_ib_query_port(struct ib_device *ibdev, u32 port, diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 05cca36f6a4b..8989e572e32c 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -58,7 +58,7 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *pr int err; struct mthca_dev *mdev = to_mdev(ibdev); - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -112,7 +112,6 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *pr props->max_total_mcast_qp_attach = props->max_mcast_qp_attach * props->max_mcast_grp; - err = ib_respond_empty_udata(uhw); out: kfree(in_mad); kfree(out_mad); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index de83dc0ea79c..cfe3d19b73b3 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -70,7 +70,7 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, struct ocrdma_dev *dev = get_ocrdma_dev(ibdev); int err; - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -111,7 +111,7 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, attr->local_ca_ack_delay = dev->attr.local_ca_ack_delay; attr->max_fast_reg_page_list_len = dev->attr.max_pages_per_frmr; attr->max_pkeys = 1; - return ib_respond_empty_udata(uhw); + return 0; } static inline void get_link_speed_and_width(struct ocrdma_dev *dev, diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 1cf502579ad1..012a0ab98d6b 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -114,7 +114,7 @@ int qedr_query_device(struct ib_device *ibdev, return -EINVAL; } - rc = ib_is_udata_in_empty(udata); + rc = ib_no_udata_io(udata); if (rc) return rc; @@ -158,7 +158,7 @@ int qedr_query_device(struct ib_device *ibdev, attr->max_pkeys = qattr->max_pkey; attr->max_ah = qattr->max_ah; - return ib_respond_empty_udata(udata); + return 0; } static inline void get_link_speed_and_width(int speed, u16 *ib_speed, diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 3402c84eb904..1a1647d0e345 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -278,7 +278,7 @@ int usnic_ib_query_device(struct ib_device *ibdev, int err; usnic_dbg("\n"); - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -323,7 +323,7 @@ int usnic_ib_query_device(struct ib_device *ibdev, * max_qp_wr, max_sge, max_sge_rd, max_cqe */ mutex_unlock(&us_ibdev->usdev_lock); - return ib_respond_empty_udata(uhw); + return 0; } int usnic_ib_query_port(struct ib_device *ibdev, u32 port, diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index 1d29a535f76a..59c2a88c158a 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -69,7 +69,7 @@ int pvrdma_query_device(struct ib_device *ibdev, struct pvrdma_dev *dev = to_vdev(ibdev); int err; - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; @@ -116,7 +116,7 @@ int pvrdma_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_PORT_ACTIVE_EVENT | IB_DEVICE_RC_RNR_NAK_GEN; - return ib_respond_empty_udata(uhw); + return 0; } /** diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index f37d6d64adb9..1c112f4dc994 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -84,14 +84,14 @@ static int rvt_query_device(struct ib_device *ibdev, struct rvt_dev_info *rdi = ib_to_rvt(ibdev); int err; - err = ib_is_udata_in_empty(uhw); + err = ib_no_udata_io(uhw); if (err) return err; /* * Return rvt_dev_info.dparms.props contents */ *props = rdi->dparms.props; - return ib_respond_empty_udata(uhw); + return 0; } static int rvt_get_numa_node(struct ib_device *ibdev) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 1ec130fee8ea..c8562866e21f 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -22,13 +22,13 @@ static int rxe_query_device(struct ib_device *ibdev, struct rxe_dev *rxe = to_rdev(ibdev); int err; - err = ib_is_udata_in_empty(udata); + err = ib_no_udata_io(udata); if (err) return err; memcpy(attr, &rxe->attr, sizeof(*attr)); - return ib_respond_empty_udata(udata); + return 0; } static int rxe_query_port(struct ib_device *ibdev, diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index e281517fc6f9..4ba6d6d5afe9 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -132,7 +132,7 @@ int siw_query_device(struct ib_device *base_dev, struct ib_device_attr *attr, struct siw_device *sdev = to_siw_dev(base_dev); int rv; - rv = ib_is_udata_in_empty(udata); + rv = ib_no_udata_io(udata); if (rv) return rv; @@ -165,7 +165,7 @@ int siw_query_device(struct ib_device *base_dev, struct ib_device_attr *attr, addrconf_addr_eui48((u8 *)&attr->sys_image_guid, sdev->raw_gid); - return ib_respond_empty_udata(udata); + return 0; } int siw_query_port(struct ib_device *base_dev, u32 port, From fa09adf4231f6c19caa9f52c1a707e0da54cd94e Mon Sep 17 00:00:00 2001 From: Praveen Kumar Kannoju Date: Wed, 15 Jul 2026 08:07:38 +0000 Subject: [PATCH 055/160] IB/mlx4: Fix stale CM id_map entries when RTU is never received mlx4_ib_multiplex_cm_handler() allocates an id_map_entry for CM transactions, but the entry is normally released only on DREQ or REJ flows. In the duplicate REP handling scenario, cm_dup_rep_handler() may be invoked when the remote side receives a REP for which no matching cm_id_priv exists. In such cases the CM handshake never reaches RTU, and the sender side may never receive either DREQ or REJ cleanup events. As a result, the allocated id_map_entry remains indefinitely, resulting in a stale mapping leak. Fix this by arming an RTU-abandon cleanup timeout when the id_map_entry is allocated. The timeout uses the mlx4 CM workqueue and the existing schedule_delayed() path, so later DREQ/REJ cleanup can shorten the pending timeout with mod_delayed_work(). Track whether a pending cleanup timeout is still waiting for RTU. RTU cancels only that initial timeout; if DREQ/REJ has already converted it to normal teardown cleanup, a late or duplicate RTU does not cancel the teardown timer. If the RTU timeout callback has already started, leave the entry on the timeout path and make the RTU packet lose that race. Hold id_map_lock while looking up the entry, canceling the RTU timeout, scheduling teardown cleanup, and copying the id values needed by the CM handlers. The delayed-work callback rechecks scheduled_delete under the same lock before removing and freeing the entry, avoiding use-after-free when RTU races with timeout execution. Signed-off-by: Praveen Kumar Kannoju Link: https://patch.msgid.link/20260715080738.1357072-1-praveen.kannoju@oracle.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx4/cm.c | 111 +++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index 202fd5365e35..1e5e525fea73 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -40,6 +40,7 @@ #include "mlx4_ib.h" #define CM_CLEANUP_CACHE_TIMEOUT (30 * HZ) +#define CM_RTU_TIMEOUT (60 * HZ) struct id_map_entry { struct rb_node node; @@ -48,6 +49,7 @@ struct id_map_entry { u32 pv_cm_id; int slave_id; int scheduled_delete; + bool rtu_timeout; struct mlx4_ib_dev *dev; struct list_head list; @@ -184,6 +186,10 @@ static void id_map_ent_timeout(struct work_struct *work) struct rb_root *sl_id_map = &sriov->sl_id_map; spin_lock(&sriov->id_map_lock); + if (!ent->scheduled_delete) { + spin_unlock(&sriov->id_map_lock); + return; + } if (!xa_erase(&sriov->pv_id_table, ent->pv_cm_id)) goto out; found_ent = id_map_find_by_sl_id(&dev->ib_dev, ent->slave_id, ent->sl_cm_id); @@ -228,8 +234,12 @@ static void sl_id_map_add(struct ib_device *ibdev, struct id_map_entry *new) rb_insert_color(&new->node, sl_id_map); } +static void schedule_delayed(struct ib_device *ibdev, struct id_map_entry *id, + unsigned long timeout, bool rtu_timeout); + static struct id_map_entry * -id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) +id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id, + u32 *pv_cm_id) { int ret; struct id_map_entry *ent; @@ -242,6 +252,7 @@ id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) ent->sl_cm_id = sl_cm_id; ent->slave_id = slave_id; ent->scheduled_delete = 0; + ent->rtu_timeout = false; ent->dev = to_mdev(ibdev); INIT_DELAYED_WORK(&ent->timeout, id_map_ent_timeout); @@ -251,6 +262,8 @@ id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) spin_lock(&sriov->id_map_lock); sl_id_map_add(ibdev, ent); list_add_tail(&ent->list, &sriov->cm_list); + *pv_cm_id = ent->pv_cm_id; + schedule_delayed(ibdev, ent, CM_RTU_TIMEOUT, true); spin_unlock(&sriov->id_map_lock); return ent; } @@ -267,42 +280,41 @@ id_map_get(struct ib_device *ibdev, int *pv_cm_id, int slave_id, int sl_cm_id) struct id_map_entry *ent; struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov; - spin_lock(&sriov->id_map_lock); + lockdep_assert_held(&sriov->id_map_lock); if (*pv_cm_id == -1) { ent = id_map_find_by_sl_id(ibdev, slave_id, sl_cm_id); if (ent) *pv_cm_id = (int) ent->pv_cm_id; } else ent = xa_load(&sriov->pv_id_table, *pv_cm_id); - spin_unlock(&sriov->id_map_lock); return ent; } -static void schedule_delayed(struct ib_device *ibdev, struct id_map_entry *id) +static void schedule_delayed(struct ib_device *ibdev, struct id_map_entry *id, + unsigned long timeout, bool rtu_timeout) { struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov; unsigned long flags; - spin_lock(&sriov->id_map_lock); + lockdep_assert_held(&sriov->id_map_lock); spin_lock_irqsave(&sriov->going_down_lock, flags); /*make sure that there is no schedule inside the scheduled work.*/ - if (!sriov->is_going_down && !id->scheduled_delete) { + if (!sriov->is_going_down || id->scheduled_delete) { id->scheduled_delete = 1; - queue_delayed_work(cm_wq, &id->timeout, CM_CLEANUP_CACHE_TIMEOUT); - } else if (id->scheduled_delete) { - /* Adjust timeout if already scheduled */ - mod_delayed_work(cm_wq, &id->timeout, CM_CLEANUP_CACHE_TIMEOUT); + id->rtu_timeout = rtu_timeout; + mod_delayed_work(cm_wq, &id->timeout, timeout); } spin_unlock_irqrestore(&sriov->going_down_lock, flags); - spin_unlock(&sriov->id_map_lock); } #define REJ_REASON(m) be16_to_cpu(((struct cm_generic_msg *)(m))->rej_reason) int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id, struct ib_mad *mad) { + struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov; struct id_map_entry *id; + u32 pv_cm_id_to_set = 0; u32 sl_cm_id; int pv_cm_id = -1; @@ -312,12 +324,22 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID || (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID && REJ_REASON(mad) == IB_CM_REJ_TIMEOUT)) { sl_cm_id = get_local_comm_id(mad); + spin_lock(&sriov->id_map_lock); id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); + if (id) { + pv_cm_id_to_set = id->pv_cm_id; + if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) + schedule_delayed(ibdev, id, + CM_CLEANUP_CACHE_TIMEOUT, + false); + } + spin_unlock(&sriov->id_map_lock); if (id) goto cont; if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) return 0; - id = id_map_alloc(ibdev, slave_id, sl_cm_id); + id = id_map_alloc(ibdev, slave_id, sl_cm_id, + &pv_cm_id_to_set); if (IS_ERR(id)) { mlx4_ib_warn(ibdev, "%s: id{slave: %d, sl_cm_id: 0x%x} Failed to id_map_alloc\n", __func__, slave_id, sl_cm_id); @@ -325,14 +347,39 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id } } else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) { sl_cm_id = get_local_comm_id(mad); + spin_lock(&sriov->id_map_lock); id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); + if (id) { + pv_cm_id_to_set = id->pv_cm_id; + schedule_delayed(ibdev, id, CM_CLEANUP_CACHE_TIMEOUT, + false); + } + spin_unlock(&sriov->id_map_lock); if (!id) return 0; } else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) { return 0; } else { sl_cm_id = get_local_comm_id(mad); + spin_lock(&sriov->id_map_lock); id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); + if (id) { + if (mad->mad_hdr.attr_id == CM_RTU_ATTR_ID && + id->rtu_timeout) { + id->rtu_timeout = false; + if (cancel_delayed_work(&id->timeout)) + id->scheduled_delete = 0; + else + id = NULL; + } + if (id) + pv_cm_id_to_set = id->pv_cm_id; + if (id && mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID) + schedule_delayed(ibdev, id, + CM_CLEANUP_CACHE_TIMEOUT, + false); + } + spin_unlock(&sriov->id_map_lock); } if (!id) { @@ -342,11 +389,7 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id } cont: - set_local_comm_id(mad, id->pv_cm_id); - - if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID || - mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) - schedule_delayed(ibdev, id); + set_local_comm_id(mad, pv_cm_id_to_set); return 0; } @@ -436,7 +479,10 @@ int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave, struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov; u32 rem_pv_cm_id = get_local_comm_id(mad); u32 pv_cm_id; + u32 sl_cm_id = 0; struct id_map_entry *id; + int pv_cm_id_int; + int slave_id = 0; int sts; if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID || @@ -464,7 +510,28 @@ int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave, } pv_cm_id = get_remote_comm_id(mad); - id = id_map_get(ibdev, (int *)&pv_cm_id, -1, -1); + pv_cm_id_int = pv_cm_id; + spin_lock(&sriov->id_map_lock); + id = id_map_get(ibdev, &pv_cm_id_int, -1, -1); + if (id) { + if (mad->mad_hdr.attr_id == CM_RTU_ATTR_ID && + id->rtu_timeout) { + id->rtu_timeout = false; + if (cancel_delayed_work(&id->timeout)) + id->scheduled_delete = 0; + else + id = NULL; + } + if (id && slave) + slave_id = id->slave_id; + if (id) + sl_cm_id = id->sl_cm_id; + if (id && (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID || + mad->mad_hdr.attr_id == CM_REJ_ATTR_ID)) + schedule_delayed(ibdev, id, + CM_CLEANUP_CACHE_TIMEOUT, false); + } + spin_unlock(&sriov->id_map_lock); if (!id) { if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID && @@ -479,12 +546,8 @@ int mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave, } if (slave) - *slave = id->slave_id; - set_remote_comm_id(mad, id->sl_cm_id); - - if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID || - mad->mad_hdr.attr_id == CM_REJ_ATTR_ID) - schedule_delayed(ibdev, id); + *slave = slave_id; + set_remote_comm_id(mad, sl_cm_id); return 0; } From d38c835925d4a3bfdf0a85ff2829ee90c709c561 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 14 Jul 2026 13:50:00 +0300 Subject: [PATCH 056/160] RDMA/bnxt_re: Validate udata before executing commands The destroy callbacks currently zero the udata output after tearing down driver resources. If the userspace access fails, uverbs preserves the uobject and allows the destroy callback to run again, even though the driver resource has already been freed. Call ib_no_udata_io() before teardown so udata failures are detected while the resource is still intact, then return success after teardown completes. As part of this change, move ib_respond_empty_udata() to the start of the create and modify flows. While this is not strictly required for general create flows, as the core layer unwinds uobjects on failure, it is necessary for create AH. In _rdma_create_ah(), the HW object is otherwise leaked. Fixes: bed686d8dcd4 ("RDMA/bnxt_re: Use ib_respond_empty_udata()") Signed-off-by: Leon Romanovsky Link: https://patch.msgid.link/20260714-fix-destroy-no-udata-v2-1-734fdcf667d5@kernel.org Acked-by: Selvin Xavier Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 65 +++++++++++------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 90138d64adee..adc693736769 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -694,7 +694,7 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) struct bnxt_re_dev *rdev = pd->rdev; int ret; - ret = ib_is_udata_in_empty(udata); + ret = ib_no_udata_io(udata); if (ret) return ret; @@ -711,7 +711,7 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) &pd->qplib_pd)) atomic_dec(&rdev->stats.res.pd_count); } - return ib_respond_empty_udata(udata); + return 0; } int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) @@ -843,7 +843,7 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, u8 nw_type; int rc; - rc = ib_is_udata_in_empty(udata); + rc = ib_no_udata_io(udata); if (rc) return rc; @@ -900,7 +900,7 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, if (active_ahs > rdev->stats.res.ah_watermark) rdev->stats.res.ah_watermark = active_ahs; - return ib_respond_empty_udata(udata); + return 0; } int bnxt_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr) @@ -1014,7 +1014,7 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) unsigned int flags; int rc; - rc = ib_is_udata_in_empty(udata); + rc = ib_no_udata_io(udata); if (rc) return rc; @@ -1063,7 +1063,7 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) if (scq_nq != rcq_nq) bnxt_re_synchronize_nq(rcq_nq); - return ib_respond_empty_udata(udata); + return 0; } static u8 __from_ib_qp_type(enum ib_qp_type type) @@ -2147,7 +2147,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) struct bnxt_qplib_srq *qplib_srq = &srq->qplib_srq; int ret; - ret = ib_is_udata_in_empty(udata); + ret = ib_no_udata_io(udata); if (ret) return ret; @@ -2158,7 +2158,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) free_page((unsigned long)srq->uctx_srq_page); ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); - return ib_respond_empty_udata(udata); + return 0; } static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev, @@ -2296,34 +2296,25 @@ int bnxt_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr, { struct bnxt_re_srq *srq = container_of(ib_srq, struct bnxt_re_srq, ib_srq); - struct bnxt_re_dev *rdev = srq->rdev; int ret; - ret = ib_is_udata_in_empty(udata); + ret = ib_no_udata_io(udata); if (ret) return ret; - switch (srq_attr_mask) { - case IB_SRQ_MAX_WR: - /* SRQ resize is not supported */ + if (srq_attr_mask != IB_SRQ_LIMIT) return -EINVAL; - case IB_SRQ_LIMIT: - /* Change the SRQ threshold */ - if (srq_attr->srq_limit > srq->qplib_srq.max_wqe) - return -EINVAL; - srq->qplib_srq.threshold = srq_attr->srq_limit; - bnxt_qplib_srq_arm_db(&srq->qplib_srq.dbinfo, srq->qplib_srq.threshold); - - /* On success, update the shadow */ - srq->srq_limit = srq_attr->srq_limit; - /* No need to Build and send response back to udata */ - return ib_respond_empty_udata(udata); - default: - ibdev_err(&rdev->ibdev, - "Unsupported srq_attr_mask 0x%x", srq_attr_mask); + if (srq_attr->srq_limit > srq->qplib_srq.max_wqe) return -EINVAL; - } + + srq->qplib_srq.threshold = srq_attr->srq_limit; + bnxt_qplib_srq_arm_db(&srq->qplib_srq.dbinfo, srq->qplib_srq.threshold); + + /* On success, update the shadow */ + srq->srq_limit = srq_attr->srq_limit; + /* No need to Build and send response back to udata */ + return 0; } int bnxt_re_query_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr) @@ -2436,7 +2427,7 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, unsigned int flags; u8 nw_type; - rc = ib_is_udata_in_empty(udata); + rc = ib_no_udata_io(udata); if (rc) return rc; @@ -2688,7 +2679,7 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, if (rc) return rc; } - return ib_respond_empty_udata(udata); + return 0; } int bnxt_re_query_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, @@ -3470,7 +3461,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) nq = cq->qplib_cq.nq; cctx = rdev->chip_ctx; - ret = ib_is_udata_in_empty(udata); + ret = ib_no_udata_io(udata); if (ret) return ret; @@ -3485,7 +3476,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) atomic_dec(&rdev->stats.res.cq_count); kfree(cq->cql); ib_umem_release(cq->umem); - return ib_respond_empty_udata(udata); + return 0; } int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, @@ -3687,6 +3678,10 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, if (rc) goto fail; + rc = ib_respond_empty_udata(udata); + if (rc) + goto fail; + cq->resize_umem = ib_umem_get_va(&rdev->ibdev, req.cq_va, entries * sizeof(struct cq_base), IB_ACCESS_LOCAL_WRITE); @@ -3716,7 +3711,7 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, cq->ib_cq.cqe = cq->resize_cqe; atomic_inc(&rdev->stats.res.resize_count); - return ib_respond_empty_udata(udata); + return 0; fail: if (cq->resize_umem) { @@ -4448,7 +4443,7 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) struct bnxt_re_dev *rdev = mr->rdev; int rc; - rc = ib_is_udata_in_empty(udata); + rc = ib_no_udata_io(udata); if (rc) return rc; @@ -4471,7 +4466,7 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) atomic_dec(&rdev->stats.res.mr_count); if (rc) return rc; - return ib_respond_empty_udata(udata); + return 0; } static int bnxt_re_set_page(struct ib_mr *ib_mr, u64 addr) From b38f98e176050850f41bb6415f3a71400056623e Mon Sep 17 00:00:00 2001 From: TanZheng Date: Wed, 15 Jul 2026 18:15:50 +0800 Subject: [PATCH 057/160] RDMA/srpt: Fix srpt_alloc_rw_ctxs() unwind counters When srpt_alloc_rw_ctxs() fails partway through a multi-buffer indirect descriptor, the unwind path destroys RDMA contexts but leaves stale n_rw_ctx and n_rdma values (and a dangling rw_ctxs pointer). Later sq_wr_avail accounting in srpt_queue_response() or srpt_write_pending() can then subtract the wrong number of send queue credits. Reset the counters and clear rw_ctxs after freeing the heap allocation before returning an error. Fixes: b99f8e4d7bcd ("IB/srpt: convert to the generic RDMA READ/WRITE API") Signed-off-by: TanZheng Link: https://patch.msgid.link/20260715101550.45345-1-kensanya@163.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/srpt/ib_srpt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index a760b4fbee90..a22ee4fc1e4c 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -959,6 +959,7 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx, struct srpt_rdma_ch *ch = ioctx->ch; struct scatterlist *prev = NULL; unsigned prev_nents; + u8 n_rdma, n_rw_ctx; int ret, i; if (nbufs == 1) { @@ -969,6 +970,9 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx, return -ENOMEM; } + n_rw_ctx = ioctx->n_rw_ctx; + n_rdma = ioctx->n_rdma; + for (i = ioctx->n_rw_ctx; i < nbufs; i++, db++) { struct srpt_rw_ctx *ctx = &ioctx->rw_ctxs[i]; u64 remote_addr = be64_to_cpu(db->va); @@ -1015,6 +1019,9 @@ static int srpt_alloc_rw_ctxs(struct srpt_send_ioctx *ioctx, } if (ioctx->rw_ctxs != &ioctx->s_rw_ctx) kfree(ioctx->rw_ctxs); + ioctx->rw_ctxs = NULL; + ioctx->n_rw_ctx = n_rw_ctx; + ioctx->n_rdma = n_rdma; return ret; } From d32fe5bb418832adef25e70f9d6ed50140b9f05f Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 16:40:13 +0000 Subject: [PATCH 058/160] RDMA/irdma: Prevent user-triggered null deref on QP create Previously, the user QP creation path would only attempt to populate iwqp->iwpbl if the user-provided req.user_wqe_bufs field was non-zero. The problem is that iwqp->iwpbl is unconditionally dereferenced later on in irdma_setup_virt_qp. While there was a check for iwqp->iwpbl != NULL, this check would only occur if req.user_wqe_bufs was non-zero. The end result is that a user could send a zero user_wqe_bufs value and trigger a null ptr deref. Fix this by unconditionally calling irdma_get_pbl and bailing if it fails, similar to the CQ and SRQ paths. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Signed-off-by: Jacob Moroni Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index f48cc8b5299b..a5aa1cd8ab7c 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -635,17 +635,16 @@ static int irdma_setup_umode_qp(struct ib_udata *udata, iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; iwqp->user_mode = 1; - if (req.user_wqe_bufs) { - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, - &ucontext->qp_reg_mem_list); - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); - if (!iwqp->iwpbl) { - ret = -ENODATA; - ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); - return ret; - } + spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); + iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + &ucontext->qp_reg_mem_list); + spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + + if (!iwqp->iwpbl) { + ret = -ENODATA; + ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); + return ret; } if (!ucontext->use_raw_attrs) { From 9b5261a23389ee6e224da6306658aac8abfda281 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:52 +0000 Subject: [PATCH 059/160] RDMA/irdma: Add checks for no udata Several methods do not accept udata input and do not provide a udata response. Use the ib_no_udata_io helper to check that the input buffers are empty and to zero fill any user response buffers. For methods that do provide a response, enforce the input buffer is empty using ib_is_udata_in_empty. The irdma rdma-core provider as well as the legacy i40iw provider were both checked to ensure they never passed any udata to these ops. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-2-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 66 +++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index a5aa1cd8ab7c..fadd4bff572c 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -406,6 +406,10 @@ static int irdma_alloc_pd(struct ib_pd *pd, struct ib_udata *udata) u32 pd_id = 0; int err; + err = ib_is_udata_in_empty(udata); + if (err) + return err; + if (udata && udata->outlen < IRDMA_ALLOC_PD_MIN_RESP_LEN) return -EINVAL; @@ -444,6 +448,11 @@ static int irdma_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct irdma_pd *iwpd = to_iwpd(ibpd); struct irdma_device *iwdev = to_iwdev(ibpd->device); + int ret; + + ret = ib_no_udata_io(udata); + if (ret) + return ret; irdma_free_rsrc(iwdev->rf, iwdev->rf->allocated_pds, iwpd->sc_pd.pd_id); @@ -536,11 +545,10 @@ static int irdma_setup_push_mmap_entries(struct irdma_ucontext *ucontext, } /** - * irdma_destroy_qp - destroy qp + * _irdma_destroy_qp - destroy qp * @ibqp: qp's ib pointer also to get to device's qp address - * @udata: user data */ -static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) +static void _irdma_destroy_qp(struct ib_qp *ibqp) { struct irdma_qp *iwqp = to_iwqp(ibqp); struct irdma_device *iwdev = iwqp->iwdev; @@ -572,6 +580,22 @@ static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (iwqp->sc_qp.qp_uk.qp_id == 1) iwdev->rf->hwqp1_rsvd = false; irdma_free_qp_rsrc(iwqp); +} + +/** + * irdma_destroy_qp - destroy qp + * @ibqp: qp's ib pointer also to get to device's qp address + * @udata: user data + */ +static int irdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) +{ + int ret; + + ret = ib_no_udata_io(udata); + if (ret) + return ret; + + _irdma_destroy_qp(ibqp); return 0; } @@ -1126,7 +1150,7 @@ static int irdma_create_qp(struct ib_qp *ibqp, err_code = ib_respond_udata(udata, uresp); if (err_code) { - irdma_destroy_qp(&iwqp->ibqp, udata); + _irdma_destroy_qp(&iwqp->ibqp); return err_code; } } @@ -1980,6 +2004,11 @@ static int irdma_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata) struct irdma_device *iwdev = to_iwdev(ibsrq->device); struct irdma_srq *iwsrq = to_iwsrq(ibsrq); struct irdma_sc_srq *srq = &iwsrq->sc_srq; + int ret; + + ret = ib_no_udata_io(udata); + if (ret) + return ret; irdma_srq_wq_destroy(iwdev->rf, srq); irdma_srq_free_rsrc(iwdev->rf, iwsrq); @@ -2000,6 +2029,11 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) struct irdma_sc_ceq *ceq = dev->ceq[cq->ceq_id]; struct irdma_ceq *iwceq = container_of(ceq, struct irdma_ceq, sc_ceq); unsigned long flags; + int ret; + + ret = ib_no_udata_io(udata); + if (ret) + return ret; spin_lock_irqsave(&iwcq->lock, flags); if (!list_empty(&iwcq->cmpl_generated)) @@ -2234,6 +2268,10 @@ static int irdma_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, struct cqp_cmds_info *cqp_info; int status; + status = ib_no_udata_io(udata); + if (status) + return status; + if (attr_mask & IB_SRQ_MAX_WR) return -EINVAL; @@ -3079,6 +3117,10 @@ static int irdma_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) int err_code; u32 stag; + err_code = ib_no_udata_io(udata); + if (err_code) + return err_code; + stag = irdma_create_stag(iwdev); if (!stag) return -ENOMEM; @@ -3830,6 +3872,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, struct ib_umem_dmabuf *umem_dmabuf; int ret; + ret = ib_no_udata_io(udata); + if (ret) + return ERR_PTR(ret); + if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size) return ERR_PTR(-EINVAL); @@ -4022,6 +4068,10 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) bool dmabuf_revocable = iwmr->region && iwmr->region->is_dmabuf; int ret; + ret = ib_no_udata_io(udata); + if (ret) + return ret; + if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) { if (iwmr->region) { struct irdma_ucontext *ucontext; @@ -5343,6 +5393,10 @@ static int irdma_create_user_ah(struct ib_ah *ibah, struct irdma_ah *parent_ah; int err; + err = ib_is_udata_in_empty(udata); + if (err) + return err; + if (udata->outlen < IRDMA_CREATE_AH_MIN_RESP_LEN) return -EINVAL; @@ -5394,6 +5448,10 @@ static int irdma_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr, struct irdma_device *iwdev = to_iwdev(ibah->pd->device); int err; + err = ib_no_udata_io(udata); + if (err) + return err; + err = irdma_setup_ah(ibah, attr); if (err) return err; From 91a74a238631ef7b7c6bda17328014ab7160c888 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:53 +0000 Subject: [PATCH 060/160] RDMA/irdma: Clear udata response buffers where necessary Methods that may accept udata input but do not provide a udata response should use the ib_respond_empty_udata() helper to ensure that user response buffers are cleared. Since the ib_respond_empty_udata() call itself can fail if the user intentionally provides a bogus output buffer, it is called at the beginning of the method to fail early before mutating any state that would be difficult to unwind. Additionally, add missing bounds validation for udata->outlen in irdma_create_srq() to ensure it is large enough to hold the response struct as per its original (and so far, only) definition. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-3-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index fadd4bff572c..e3ae8ea36fba 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -1315,6 +1315,11 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, u8 issue_modify_qp = 0; int ret = 0; + /* Clear the response buffer (if any). It may be updated again later. */ + ret = ib_respond_empty_udata(udata); + if (ret) + return ret; + ctx_info = &iwqp->ctx_info; roce_info = &iwqp->roce_info; udp_info = &iwqp->udp_info; @@ -1675,6 +1680,10 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, int err; unsigned long flags; + err = ib_respond_empty_udata(udata); + if (err) + return err; + if (udata) { /* udata inlen/outlen can be 0 when supporting legacy libi40iw */ if ((udata->inlen && udata->inlen < IRDMA_MODIFY_QP_MIN_REQ_LEN) || @@ -2094,6 +2103,10 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, if (entries > rf->max_cqe) return -EINVAL; + ret = ib_respond_empty_udata(udata); + if (ret) + return ret; + if (!iwcq->user_mode) { entries += 2; @@ -2394,6 +2407,7 @@ static int irdma_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *initattrs, struct ib_udata *udata) { +#define IRDMA_CREATE_SRQ_MIN_RESP_LEN offsetofend(struct irdma_create_srq_resp, srq_size) struct irdma_device *iwdev = to_iwdev(ibsrq->device); struct ib_srq_attr *attr = &initattrs->attr; struct irdma_pd *iwpd = to_iwpd(ibsrq->pd); @@ -2414,6 +2428,9 @@ static int irdma_create_srq(struct ib_srq *ibsrq, if (initattrs->srq_type != IB_SRQT_BASIC) return -EOPNOTSUPP; + if (udata && udata->outlen < IRDMA_CREATE_SRQ_MIN_RESP_LEN) + return -EINVAL; + if (!(uk_attrs->feature_flags & IRDMA_FEATURE_SRQ) || attr->max_sge > uk_attrs->max_hw_wq_frags) return -EINVAL; @@ -3606,6 +3623,10 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, if (dmah) return ERR_PTR(-EOPNOTSUPP); + err = ib_respond_empty_udata(udata); + if (err) + return ERR_PTR(err); + if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size) return ERR_PTR(-EINVAL); From aa5967095c7d53f1405a8bed995bf355efc1ffae Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:54 +0000 Subject: [PATCH 061/160] RDMA/irdma: Use robust input copy helpers Replace the use of ib_copy_from_udata() with ib_copy_validate_udata_in() where applicable. For each modified call site, the last argument of ib_copy_validate_udata_in() was determined by taking the last member of the ABI struct as per its original definition (i.e., when it was first committed). Some methods like irdma_create_cq required special care because the last member of the current ABI def is beyond that of the legacy i40iw's ABI def which we need to remain compatible with. In some other cases like modify_qp, the legacy i40iw provider never provided any udata at all so the validation is only performed if inlen > 0. irdma_create_qp is more challenging because the legacy ABI was actually larger but the additional fields were never used, and even worse, never initialized in the provider. This will be handled in a followup commit. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-4-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 79 +++++++++++++---------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index e3ae8ea36fba..d32d651530bb 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -1299,7 +1299,6 @@ static int irdma_wait_for_suspend(struct irdma_qp *iwqp) int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata) { -#define IRDMA_MODIFY_QP_MIN_REQ_LEN offsetofend(struct irdma_modify_qp_req, rq_flush) #define IRDMA_MODIFY_QP_MIN_RESP_LEN offsetofend(struct irdma_modify_qp_resp, push_valid) struct irdma_pd *iwpd = to_iwpd(ibqp->pd); struct irdma_qp *iwqp = to_iwqp(ibqp); @@ -1326,9 +1325,15 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (udata) { /* udata inlen/outlen can be 0 when supporting legacy libi40iw */ - if ((udata->inlen && udata->inlen < IRDMA_MODIFY_QP_MIN_REQ_LEN) || - (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN)) + if (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN) return -EINVAL; + + /* For current irdma, validate against ABI def. */ + if (udata->inlen) { + ret = ib_copy_validate_udata_in(udata, ureq, rsvd); + if (ret) + return ret; + } } if (attr_mask & ~IB_QP_ATTR_STANDARD_BITS) @@ -1571,10 +1576,6 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, iwqp->ibqp_state = attr->qp_state; spin_unlock_irqrestore(&iwqp->lock, flags); if (udata && udata->inlen) { - if (ib_copy_from_udata(&ureq, udata, - min(sizeof(ureq), udata->inlen))) - return -EINVAL; - irdma_flush_wqes(iwqp, (ureq.sq_flush ? IRDMA_FLUSH_SQ : 0) | (ureq.rq_flush ? IRDMA_FLUSH_RQ : 0) | @@ -1664,7 +1665,6 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata) { -#define IRDMA_MODIFY_QP_MIN_REQ_LEN offsetofend(struct irdma_modify_qp_req, rq_flush) #define IRDMA_MODIFY_QP_MIN_RESP_LEN offsetofend(struct irdma_modify_qp_resp, push_valid) struct irdma_qp *iwqp = to_iwqp(ibqp); struct irdma_device *iwdev = iwqp->iwdev; @@ -1686,9 +1686,14 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, if (udata) { /* udata inlen/outlen can be 0 when supporting legacy libi40iw */ - if ((udata->inlen && udata->inlen < IRDMA_MODIFY_QP_MIN_REQ_LEN) || - (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN)) + if (udata->outlen && udata->outlen < IRDMA_MODIFY_QP_MIN_RESP_LEN) return -EINVAL; + + if (udata->inlen) { + err = ib_copy_validate_udata_in(udata, ureq, rsvd); + if (err) + return err; + } } if (attr_mask & ~IB_QP_ATTR_STANDARD_BITS) @@ -1778,10 +1783,6 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, iwqp->ibqp_state = attr->qp_state; spin_unlock_irqrestore(&iwqp->lock, flags); if (udata && udata->inlen) { - if (ib_copy_from_udata(&ureq, udata, - min(sizeof(ureq), udata->inlen))) - return -EINVAL; - irdma_flush_wqes(iwqp, (ureq.sq_flush ? IRDMA_FLUSH_SQ : 0) | (ureq.rq_flush ? IRDMA_FLUSH_RQ : 0) | @@ -2073,7 +2074,6 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, struct ib_udata *udata) { -#define IRDMA_RESIZE_CQ_MIN_REQ_LEN offsetofend(struct irdma_resize_cq_req, user_cq_buffer) struct irdma_cq *iwcq = to_iwcq(ibcq); struct irdma_sc_dev *dev = iwcq->sc_cq.dev; struct irdma_cqp_request *cqp_request; @@ -2097,9 +2097,6 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, IRDMA_FEATURE_CQ_RESIZE)) return -EOPNOTSUPP; - if (udata && udata->inlen < IRDMA_RESIZE_CQ_MIN_REQ_LEN) - return -EINVAL; - if (entries > rf->max_cqe) return -EINVAL; @@ -2133,9 +2130,9 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - if (ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen))) - return -EINVAL; + ret = ib_copy_validate_udata_in(udata, req, user_cq_buffer); + if (ret) + return ret; spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); iwpbl_buf = irdma_get_pbl((unsigned long)req.user_cq_buffer, @@ -2327,24 +2324,20 @@ static int irdma_setup_umode_srq(struct irdma_device *iwdev, struct irdma_srq_init_info *info, struct ib_udata *udata) { -#define IRDMA_CREATE_SRQ_MIN_REQ_LEN \ - offsetofend(struct irdma_create_srq_req, user_shadow_area) struct irdma_create_srq_req req = {}; struct irdma_ucontext *ucontext; struct irdma_srq_mr *srqmr; struct irdma_pbl *iwpbl; unsigned long flags; + int ret; iwsrq->user_mode = true; ucontext = rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - if (udata->inlen < IRDMA_CREATE_SRQ_MIN_REQ_LEN) - return -EINVAL; - - if (ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen))) - return -EFAULT; + ret = ib_copy_validate_udata_in(udata, req, user_shadow_area); + if (ret) + return ret; spin_lock_irqsave(&ucontext->srq_reg_mem_list_lock, flags); iwpbl = irdma_get_pbl((unsigned long)req.user_srq_buf, @@ -2558,7 +2551,6 @@ static int irdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs) { -#define IRDMA_CREATE_CQ_MIN_REQ_LEN offsetofend(struct irdma_create_cq_req, user_cq_buf) #define IRDMA_CREATE_CQ_MIN_RESP_LEN offsetofend(struct irdma_create_cq_resp, cq_size) struct ib_udata *udata = &attrs->driver_udata; struct ib_device *ibdev = ibcq->device; @@ -2582,8 +2574,7 @@ static int irdma_create_cq(struct ib_cq *ibcq, if (err_code) return err_code; - if (udata && (udata->inlen < IRDMA_CREATE_CQ_MIN_REQ_LEN || - udata->outlen < IRDMA_CREATE_CQ_MIN_RESP_LEN)) + if (udata && udata->outlen < IRDMA_CREATE_CQ_MIN_RESP_LEN) return -EINVAL; err_code = irdma_alloc_rsrc(rf, rf->allocated_cqs, rf->max_cq, &cq_num, @@ -2625,11 +2616,14 @@ static int irdma_create_cq(struct ib_cq *ibcq, ucontext = rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - if (ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen))) { - err_code = -EFAULT; + /* Even though the last member of struct irdma_create_cq_req + * was always user_shadow_area, we need backwards compat with + * the legacy i40iw struct i40iw_ucreate_cq which stopped + * at user_cq_buffer. + */ + err_code = ib_copy_validate_udata_in(udata, req, user_cq_buf); + if (err_code) goto cq_free_rsrc; - } spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); iwcq->iwpbl = irdma_get_pbl((unsigned long)req.user_cq_buf, @@ -3613,7 +3607,6 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, struct ib_dmah *dmah, struct ib_udata *udata) { -#define IRDMA_MEM_REG_MIN_REQ_LEN offsetofend(struct irdma_mem_reg_req, sq_pages) struct irdma_device *iwdev = to_iwdev(pd->device); struct irdma_mem_reg_req req = {}; struct ib_umem *region = NULL; @@ -3623,6 +3616,10 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, if (dmah) return ERR_PTR(-EOPNOTSUPP); + err = ib_copy_validate_udata_in(udata, req, sq_pages); + if (err) + return ERR_PTR(err); + err = ib_respond_empty_udata(udata); if (err) return ERR_PTR(err); @@ -3630,9 +3627,6 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size) return ERR_PTR(-EINVAL); - if (udata->inlen < IRDMA_MEM_REG_MIN_REQ_LEN) - return ERR_PTR(-EINVAL); - region = ib_umem_get_va(pd->device, start, len, access); if (IS_ERR(region)) { @@ -3641,11 +3635,6 @@ static struct ib_mr *irdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, return (struct ib_mr *)region; } - if (ib_copy_from_udata(&req, udata, min(sizeof(req), udata->inlen))) { - ib_umem_release(region); - return ERR_PTR(-EFAULT); - } - iwmr = irdma_alloc_iwmr(region, pd, virt, req.reg_type); if (IS_ERR(iwmr)) { ib_umem_release(region); From 173fc2b8dcd1537b481eb67e775f49c7b7f73268 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:55 +0000 Subject: [PATCH 062/160] RDMA/irdma: Use robust udata helper for QP creation Replace the manual udata input copy and validation during QP creation with the robust helper. The irdma driver is backwards compatible with the legacy i40iw userspace provider. The current create_qp ABI contains two 8 byte fields. The legacy i40iw ABI was the same but also contained two additional fields which were never actually used. Furthermore, the i40iw userspace provider never explicitly zero-initialized those extra fields, so there is a chance that existing binaries are passing non-zero garbage values down to the kernel. Previously, the irdma driver only copied out the first 16 bytes and did not have any check for the rest of the buffer being zero, so that additional garbage didn't matter. By switching to ib_copy_validate_udata_in(), we will now be checking to ensure that data beyond the kernel's definition of the request is all zero. In order to avoid breaking legacy binaries, we therefore need to increase the request structure size to cover those garbage fields. - Legacy binaries will continue to pass down a 32 byte request, with the driver copying the entire 32 bytes out but ignoring the second 16 bytes, just as before. - Newer binaries will pass down the normal 16 byte request. The ib_copy_validate_udata_in() call will allow this to succeed because we use user_compl_ctx as our minimum length (16 bytes). - If the request is ever extended, the new fields would be added after the "don't use" fields and would work as per the normal uAPI mechanism. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-5-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 41 ++++++++++++++++------------- include/uapi/rdma/irdma-abi.h | 1 + 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index d32d651530bb..1dd4a48c268d 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -631,37 +631,29 @@ static void irdma_setup_virt_qp(struct irdma_device *iwdev, /** * irdma_setup_umode_qp - setup sq and rq size in user mode qp - * @udata: udata + * @ucontext: user context + * @req: user request pointer * @iwdev: iwarp device * @iwqp: qp ptr (user or kernel) * @info: initialize info to return * @init_attr: Initial QP create attributes */ -static int irdma_setup_umode_qp(struct ib_udata *udata, +static int irdma_setup_umode_qp(struct irdma_ucontext *ucontext, + struct irdma_create_qp_req *req, struct irdma_device *iwdev, struct irdma_qp *iwqp, struct irdma_qp_init_info *info, struct ib_qp_init_attr *init_attr) { - struct irdma_ucontext *ucontext = rdma_udata_to_drv_context(udata, - struct irdma_ucontext, ibucontext); struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info; - struct irdma_create_qp_req req; unsigned long flags; int ret; - ret = ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen)); - if (ret) { - ibdev_dbg(&iwdev->ibdev, "VERBS: ib_copy_from_data fail\n"); - return ret; - } - - iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; + iwqp->ctx_info.qp_compl_ctx = req->user_compl_ctx; iwqp->user_mode = 1; spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + iwqp->iwpbl = irdma_get_pbl((unsigned long)req->user_wqe_bufs, &ucontext->qp_reg_mem_list); spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); @@ -988,6 +980,7 @@ static int irdma_create_qp(struct ib_qp *ibqp, struct irdma_uk_attrs *uk_attrs = &dev->hw_attrs.uk_attrs; struct irdma_qp_init_info init_info = {}; struct irdma_qp_host_ctx_info *ctx_info; + struct irdma_create_qp_req ureq = {}; struct irdma_srq *iwsrq; bool srq_valid = false; u32 srq_id = 0; @@ -1005,9 +998,14 @@ static int irdma_create_qp(struct ib_qp *ibqp, if (err_code) return err_code; - if (udata && (udata->inlen < IRDMA_CREATE_QP_MIN_REQ_LEN || - udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN)) - return -EINVAL; + if (udata) { + if (udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN) + return -EINVAL; + + err_code = ib_copy_validate_udata_in(udata, ureq, user_compl_ctx); + if (err_code) + return err_code; + } init_info.vsi = &iwdev->vsi; init_info.qp_uk_init_info.uk_attrs = uk_attrs; @@ -1066,9 +1064,14 @@ static int irdma_create_qp(struct ib_qp *ibqp, init_waitqueue_head(&iwqp->mod_qp_waitq); if (udata) { + struct irdma_ucontext *ucontext = + rdma_udata_to_drv_context(udata, + struct irdma_ucontext, + ibucontext); + init_info.qp_uk_init_info.abi_ver = iwpd->sc_pd.abi_ver; - err_code = irdma_setup_umode_qp(udata, iwdev, iwqp, &init_info, - init_attr); + err_code = irdma_setup_umode_qp(ucontext, &ureq, iwdev, iwqp, + &init_info, init_attr); } else { INIT_DELAYED_WORK(&iwqp->dwork_flush, irdma_flush_worker); init_info.qp_uk_init_info.abi_ver = IRDMA_ABI_VER; diff --git a/include/uapi/rdma/irdma-abi.h b/include/uapi/rdma/irdma-abi.h index 36f20802bcc8..38155affc8b4 100644 --- a/include/uapi/rdma/irdma-abi.h +++ b/include/uapi/rdma/irdma-abi.h @@ -88,6 +88,7 @@ struct irdma_create_srq_resp { struct irdma_create_qp_req { __aligned_u64 user_wqe_bufs; __aligned_u64 user_compl_ctx; + __aligned_u64 legacy_dontuse[2]; }; struct irdma_mem_reg_req { From 628f33362e80953ca80c457ca53e4c8458381a7b Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:56 +0000 Subject: [PATCH 063/160] RDMA/irdma: Fix legacy i40iw compat check in create_qp The irdma driver maintains backward compatibility with the legacy i40iw userspace provider by checking the length of the user response buffer in irdma_create_qp. Previously, the check relied on udata->outlen < sizeof(uresp). That is technically okay since there have only ever been two sizes for the resp struct (legacy and current). However, it would be a problem if the resp struct is ever expanded in the future because it would end up triggering the legacy fallback path for non-legacy irdma providers that just haven't moved over to the newer expanded struct yet. Fix this by explicitly checking for the exact legacy resp size. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-6-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 1dd4a48c268d..81593bb1917e 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -1138,8 +1138,12 @@ static int irdma_create_qp(struct ib_qp *ibqp, init_completion(&iwqp->free_qp); if (udata) { - /* GEN_1 legacy support with libi40iw does not have expanded uresp struct */ - if (udata->outlen < sizeof(uresp)) { + /* GEN_1 legacy support with libi40iw does not have expanded + * uresp struct. Check for the exact legacy size (20 bytes) to + * ensure that newer expanded uresp structs don't accidentally + * trigger the legacy fallback. + */ + if (udata->outlen == IRDMA_CREATE_QP_MIN_RESP_LEN) { uresp.lsmm = 1; uresp.push_idx = IRDMA_INVALID_PUSH_PAGE_INDEX_GEN_1; } else { From ba3156e68ca7818ee0c42967c36d21a09623f750 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Mon, 13 Jul 2026 17:12:57 +0000 Subject: [PATCH 064/160] RDMA/irdma: Enable uverbs_robust_udata compliance flag The irdma driver has been audited to confirm that: 1. Methods which do not accept udata input perform an explicit check for no (or zero value) input. 2. Methods which do accept input perform the correct validation to ensure that additional udata beyond the kernel's current ABI definition is zero, and to enforce the required minimum length. 3. Methods which do not return udata responses use the proper helper. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260713171257.3131493-7-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 81593bb1917e..16c80367270f 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -5551,6 +5551,7 @@ static const struct ib_device_ops irdma_dev_ops = { .owner = THIS_MODULE, .driver_id = RDMA_DRIVER_IRDMA, .uverbs_abi_ver = IRDMA_ABI_VER, + .uverbs_robust_udata = true, .alloc_hw_port_stats = irdma_alloc_hw_port_stats, .alloc_mr = irdma_alloc_mr, From 1318e3c33fb06d677988137710d0a9421ba7e2e4 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 16 Jul 2026 05:03:31 -0400 Subject: [PATCH 065/160] RDMA/cxgb4: use kmalloc() for the PBL address array c4iw_reg_user_mr() allocates a page-sized temporary array of DMA addresses while programming a PBL. The array has no page-specific requirements, so allocate it with kmalloc() and release it with kfree(). This avoids the casts required by the page allocator and lets the free operation derive the allocation size from the object. Link: https://patch.msgid.link/20260715-get_pages-to-kmalloc-v1-1-b0b7fce288be@nvidia.com Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/cxgb4/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index cd1b01014198..49498c75f38f 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -541,7 +541,7 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (err) goto err_umem_release; - pages = (__be64 *) __get_free_page(GFP_KERNEL); + pages = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!pages) { err = -ENOMEM; goto err_pbl_free; @@ -568,7 +568,7 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mhp->wr_waitp); pbl_done: - free_page((unsigned long) pages); + kfree(pages); if (err) goto err_pbl_free; From ce5322367c4bd00e51560ffdc07c6c619952967a Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 16 Jul 2026 05:03:31 -0400 Subject: [PATCH 066/160] RDMA/mlx4: use kzalloc() for the fast registration page list mlx4_alloc_priv_pages() allocates a zeroed, page-sized buffer for a DMA-to-device page list. kmalloc() provides the required physical contiguity, and a PAGE_SIZE allocation retains the alignment needed to keep the list within one page. Use kzalloc() for the buffer and kfree() on the error and teardown paths. Link: https://patch.msgid.link/20260715-get_pages-to-kmalloc-v1-2-b0b7fce288be@nvidia.com Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx4/mr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 761e2c05dd0f..a6b4abce1bfe 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -313,7 +313,7 @@ mlx4_alloc_priv_pages(struct ib_device *device, MLX4_MR_PAGES_ALIGN); /* Prevent cross page boundary allocation. */ - mr->pages = (__be64 *)get_zeroed_page(GFP_KERNEL); + mr->pages = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!mr->pages) return -ENOMEM; @@ -328,7 +328,7 @@ mlx4_alloc_priv_pages(struct ib_device *device, return 0; err: - free_page((unsigned long)mr->pages); + kfree(mr->pages); return ret; } @@ -340,7 +340,7 @@ mlx4_free_priv_pages(struct mlx4_ib_mr *mr) dma_unmap_single(device->dev.parent, mr->page_map, mr->page_map_size, DMA_TO_DEVICE); - free_page((unsigned long)mr->pages); + kfree(mr->pages); mr->pages = NULL; } } From ed3760b376384248b2977f67e0f803bad1657f8e Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 16 Jul 2026 05:03:31 -0400 Subject: [PATCH 067/160] RDMA/usnic: use kmalloc() for the page pointer array usnic_uiom_get_pages() uses a page-sized array of struct page pointers as temporary storage for pin_user_pages(). Nothing requires the array to come directly from the page allocator. Use kmalloc() for the array and kfree() after the pinning loop. Link: https://patch.msgid.link/20260715-get_pages-to-kmalloc-v1-3-b0b7fce288be@nvidia.com Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/usnic/usnic_uiom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index 691c64a73516..201c35039a74 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -114,7 +114,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, INIT_LIST_HEAD(chunk_list); - page_list = (struct page **) __get_free_page(GFP_KERNEL); + page_list = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!page_list) return -ENOMEM; @@ -182,7 +182,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, mmgrab(uiomr->owning_mm); mmap_read_unlock(mm); - free_page((unsigned long) page_list); + kfree(page_list); return ret; } From b2022068dea0fa838f63591085015c02c459479e Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 16 Jul 2026 05:03:32 -0400 Subject: [PATCH 068/160] RDMA/mlx5: use kmalloc() for UMR translation buffers mlx5r_umr_alloc_xlt() allocates physically contiguous scratch buffers that are DMA mapped only in the DMA_TO_DEVICE direction. kmalloc() provides the required contiguity and alignment for these sizes while preserving the existing GFP allocation policy. The emergency translation buffer has the same requirements. Convert all of these UMR buffers to kmalloc() and release them with kfree(), which no longer requires the caller to supply the allocation order. Link: https://patch.msgid.link/20260715-get_pages-to-kmalloc-v1-4-b0b7fce288be@nvidia.com Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 8 ++++---- drivers/infiniband/hw/mlx5/umr.c | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e8bba5a76d4e..d65ebdef2823 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -5500,13 +5500,13 @@ static int __init mlx5_ib_init(void) { int ret; - xlt_emergency_page = (void *)__get_free_page(GFP_KERNEL); + xlt_emergency_page = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!xlt_emergency_page) return -ENOMEM; mlx5_ib_event_wq = alloc_ordered_workqueue("mlx5_ib_event_wq", 0); if (!mlx5_ib_event_wq) { - free_page((unsigned long)xlt_emergency_page); + kfree(xlt_emergency_page); return -ENOMEM; } @@ -5540,7 +5540,7 @@ static int __init mlx5_ib_init(void) mlx5_ib_qp_event_cleanup(); qp_event_err: destroy_workqueue(mlx5_ib_event_wq); - free_page((unsigned long)xlt_emergency_page); + kfree(xlt_emergency_page); return ret; } @@ -5553,7 +5553,7 @@ static void __exit mlx5_ib_cleanup(void) mlx5_ib_qp_event_cleanup(); destroy_workqueue(mlx5_ib_event_wq); - free_page((unsigned long)xlt_emergency_page); + kfree(xlt_emergency_page); } module_init(mlx5_ib_init); diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index c595b85b428c..80d0d190b26c 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */ +#include #include #include #include "mlx5_ib.h" @@ -517,22 +518,20 @@ static void *mlx5r_umr_alloc_xlt(size_t *nents, size_t ent_size, gfp_t gfp_mask) size = min_t(size_t, ent_size * ALIGN(*nents, xlt_chunk_align), MLX5_MAX_UMR_CHUNK); *nents = size / ent_size; - res = (void *)__get_free_pages(gfp_mask | __GFP_NOWARN, - get_order(size)); + res = kmalloc(size, gfp_mask | __GFP_NOWARN); if (res) return res; if (size > MLX5_SPARE_UMR_CHUNK) { size = MLX5_SPARE_UMR_CHUNK; *nents = size / ent_size; - res = (void *)__get_free_pages(gfp_mask | __GFP_NOWARN, - get_order(size)); + res = kmalloc(size, gfp_mask | __GFP_NOWARN); if (res) return res; } *nents = PAGE_SIZE / ent_size; - res = (void *)__get_free_page(gfp_mask); + res = kmalloc(PAGE_SIZE, gfp_mask); if (res) return res; @@ -548,7 +547,7 @@ static void mlx5r_umr_free_xlt(void *xlt, size_t length) return; } - free_pages((unsigned long)xlt, get_order(length)); + kfree(xlt); } static void mlx5r_umr_unmap_free_xlt(struct mlx5_ib_dev *dev, void *xlt, From eb70d83a86456c91c7e6ab81bdd418868306a80b Mon Sep 17 00:00:00 2001 From: Konstantin Taranov Date: Tue, 14 Jul 2026 05:31:13 -0700 Subject: [PATCH 069/160] RDMA/mana_ib: Adopt robust udata Enable the uverbs robust udata interface in mana_ib by setting uverbs_robust_udata and converting the driver to the new udata handling model. Signed-off-by: Konstantin Taranov Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mana/cq.c | 8 +++++- drivers/infiniband/hw/mana/device.c | 1 + drivers/infiniband/hw/mana/main.c | 25 ++++++++++++++----- drivers/infiniband/hw/mana/mr.c | 13 ++++++++++ drivers/infiniband/hw/mana/qp.c | 38 +++++++++++++++++++++++------ drivers/infiniband/hw/mana/wq.c | 12 +++++++++ include/uapi/rdma/mana-abi.h | 2 +- 7 files changed, 84 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/hw/mana/cq.c b/drivers/infiniband/hw/mana/cq.c index f2547989f422..d4e5e3f91268 100644 --- a/drivers/infiniband/hw/mana/cq.c +++ b/drivers/infiniband/hw/mana/cq.c @@ -27,7 +27,8 @@ int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, is_rnic_cq = mana_ib_is_rnic(mdev); if (udata) { - err = ib_copy_validate_udata_in(udata, ucmd, buf_addr); + err = ib_copy_validate_udata_in_cm(udata, ucmd, buf_addr, + MANA_IB_CREATE_RNIC_CQ); if (err) return err; @@ -105,6 +106,11 @@ int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq); struct ib_device *ibdev = ibcq->device; struct mana_ib_dev *mdev; + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); diff --git a/drivers/infiniband/hw/mana/device.c b/drivers/infiniband/hw/mana/device.c index 9811570ab8f8..a8d19586a78a 100644 --- a/drivers/infiniband/hw/mana/device.c +++ b/drivers/infiniband/hw/mana/device.c @@ -15,6 +15,7 @@ static const struct ib_device_ops mana_ib_dev_ops = { .owner = THIS_MODULE, .driver_id = RDMA_DRIVER_MANA, .uverbs_abi_ver = MANA_IB_UVERBS_ABI_VERSION, + .uverbs_robust_udata = true, .add_gid = mana_ib_gd_add_gid, .alloc_mw = mana_ib_alloc_mw, diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index 73f4bfb22b5e..0bfb1883a186 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -101,6 +101,10 @@ int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) struct gdma_context *gc; int err; + err = ib_no_udata_io(udata); + if (err) + return err; + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); gc = mdev_to_gc(dev); @@ -133,6 +137,11 @@ int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) struct gdma_destroy_pd_req req = {}; struct mana_ib_dev *dev; struct gdma_context *gc; + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; dev = container_of(ibdev, struct mana_ib_dev, ib_dev); gc = mdev_to_gc(dev); @@ -142,7 +151,11 @@ int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) req.pd_handle = pd->pd_handle; - return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err) + return err; + + return 0; } static int mana_gd_destroy_doorbell_page(struct gdma_context *gc, @@ -198,17 +211,17 @@ int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext, int doorbell_page; int ret; + ret = ib_no_udata_io(udata); + if (ret) + return ret; + mdev = container_of(ibdev, struct mana_ib_dev, ib_dev); gc = mdev_to_gc(mdev); /* Allocate a doorbell page index */ ret = mana_gd_allocate_doorbell_page(gc, &doorbell_page); - if (ret) { - ibdev_dbg(ibdev, "Failed to allocate doorbell page %d\n", ret); + if (ret) return ret; - } - - ibdev_dbg(ibdev, "Doorbell page allocated %d\n", doorbell_page); ucontext->doorbell = doorbell_page; diff --git a/drivers/infiniband/hw/mana/mr.c b/drivers/infiniband/hw/mana/mr.c index 030bfdcfff3c..1233685b4b89 100644 --- a/drivers/infiniband/hw/mana/mr.c +++ b/drivers/infiniband/hw/mana/mr.c @@ -113,6 +113,10 @@ struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length, if (dmah) return ERR_PTR(-EOPNOTSUPP); + err = ib_no_udata_io(udata); + if (err) + return ERR_PTR(err); + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); ibdev_dbg(ibdev, @@ -327,6 +331,11 @@ int mana_ib_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) { struct mana_ib_dev *mdev = container_of(ibmw->device, struct mana_ib_dev, ib_dev); struct mana_ib_pd *pd = container_of(ibmw->pd, struct mana_ib_pd, ibpd); + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; return mana_ib_gd_create_mw(mdev, pd, ibmw); } @@ -346,6 +355,10 @@ int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) struct mana_ib_dev *dev; int err; + err = ib_no_udata_io(udata); + if (err) + return err; + dev = container_of(ibdev, struct mana_ib_dev, ib_dev); err = mana_ib_gd_destroy_mr(dev, mr->mr_handle); diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index 60926f39ab9d..b5ff07e34eb7 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -651,10 +651,8 @@ static int mana_ib_create_ud_qp(struct ib_qp *ibqp, struct ib_pd *ibpd, u32 doorbell, queue_size; int i, err; - if (udata) { - ibdev_dbg(&mdev->ib_dev, "User-level UD QPs are not supported\n"); + if (udata) return -EOPNOTSUPP; - } for (i = 0; i < MANA_UD_QUEUE_TYPE_MAX; ++i) { queue_size = mana_ib_queue_size(attr, i); @@ -745,6 +743,11 @@ static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, struct gdma_context *gc = mdev_to_gc(mdev); struct mana_port_context *mpc; struct net_device *ndev; + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; mana_gd_init_req_hdr(&req.hdr, MANA_IB_SET_QP_STATE, sizeof(req), sizeof(resp)); @@ -797,7 +800,11 @@ static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, req.ah_attr.flow_label = attr->ah_attr.grh.flow_label; } - return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err) + return err; + + return 0; } int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, @@ -826,7 +833,11 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, struct mana_ib_pd *pd; struct mana_ib_wq *wq; struct ib_wq *ibwq; - int i; + int i, err; + + err = ib_no_udata_io(udata); + if (err) + return err; ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port); mpc = netdev_priv(ndev); @@ -872,6 +883,11 @@ static int mana_ib_destroy_qp_raw(struct mana_ib_qp *qp, struct ib_udata *udata) struct mana_port_context *mpc; struct net_device *ndev; struct mana_ib_pd *pd; + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port); mpc = netdev_priv(ndev); @@ -890,7 +906,11 @@ static int mana_ib_destroy_rc_qp(struct mana_ib_qp *qp, struct ib_udata *udata) { struct mana_ib_dev *mdev = container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); - int i; + int i, err; + + err = ib_no_udata_io(udata); + if (err) + return err; mana_table_remove_qp(mdev, qp); @@ -908,7 +928,11 @@ static int mana_ib_destroy_ud_qp(struct mana_ib_qp *qp, struct ib_udata *udata) { struct mana_ib_dev *mdev = container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); - int i; + int i, err; + + err = ib_no_udata_io(udata); + if (err) + return err; mana_remove_qp_from_cqs(qp); mana_table_remove_qp(mdev, qp); diff --git a/drivers/infiniband/hw/mana/wq.c b/drivers/infiniband/hw/mana/wq.c index 5c2134a0b1a1..6b066d605dcb 100644 --- a/drivers/infiniband/hw/mana/wq.c +++ b/drivers/infiniband/hw/mana/wq.c @@ -55,6 +55,11 @@ int mana_ib_destroy_wq(struct ib_wq *ibwq, struct ib_udata *udata) struct mana_ib_wq *wq = container_of(ibwq, struct mana_ib_wq, ibwq); struct ib_device *ib_dev = ibwq->device; struct mana_ib_dev *mdev; + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; mdev = container_of(ib_dev, struct mana_ib_dev, ib_dev); @@ -69,10 +74,17 @@ int mana_ib_create_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_table, struct ib_rwq_ind_table_init_attr *init_attr, struct ib_udata *udata) { + int err; + + err = ib_no_udata_io(udata); + if (err) + return err; + /* * There is no additional data in ind_table to be maintained by this * driver, do nothing */ + return 0; } diff --git a/include/uapi/rdma/mana-abi.h b/include/uapi/rdma/mana-abi.h index a75bf32b8cfb..8336bf51b7c5 100644 --- a/include/uapi/rdma/mana-abi.h +++ b/include/uapi/rdma/mana-abi.h @@ -25,7 +25,7 @@ enum mana_ib_create_cq_flags { struct mana_ib_create_cq { __aligned_u64 buf_addr; - __u16 flags; + __u16 comp_mask; __u16 reserved0; __u32 reserved1; }; From 28a06de7318fad1d0dec9bf54fe04243cc0635ff Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Fri, 17 Jul 2026 14:53:45 +0530 Subject: [PATCH 070/160] RDMA/ionic: Add robust udata compatibility checks to all uapi verbs Enable the robust udata contract by setting uverbs_robust_udata and adding proper input validation and output handling to all verbs that accept struct ib_udata. For verbs with no driver request or response struct, add ib_no_udata_io(). For create_ah, which already responds with ionic_ah_resp, add the missing input validation via ib_is_udata_in_empty(). Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260717092345.2533564-1-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- .../infiniband/hw/ionic/ionic_controlpath.c | 38 +++++++++++++++++++ drivers/infiniband/hw/ionic/ionic_ibdev.c | 1 + 2 files changed, 39 insertions(+) diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c index 9d91f7667d4f..42b02ee643ad 100644 --- a/drivers/infiniband/hw/ionic/ionic_controlpath.c +++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c @@ -487,6 +487,11 @@ int ionic_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct ionic_ibdev *dev = to_ionic_ibdev(ibpd->device); struct ionic_pd *pd = to_ionic_pd(ibpd); + int rc; + + rc = ib_no_udata_io(udata); + if (rc) + return rc; return ionic_get_pdid(dev, &pd->pdid); } @@ -495,6 +500,11 @@ int ionic_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct ionic_ibdev *dev = to_ionic_ibdev(ibpd->device); struct ionic_pd *pd = to_ionic_pd(ibpd); + int rc; + + rc = ib_no_udata_io(udata); + if (rc) + return rc; ionic_put_pdid(dev, pd->pdid); @@ -741,6 +751,10 @@ int ionic_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr, u32 flags = init_attr->flags; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + rc = ionic_get_ahid(dev, &ah->ahid); if (rc) return rc; @@ -877,6 +891,10 @@ struct ib_mr *ionic_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length, unsigned long pg_sz; int rc; + rc = ib_no_udata_io(udata); + if (rc) + return ERR_PTR(rc); + if (dmah) return ERR_PTR(-EOPNOTSUPP); @@ -1008,6 +1026,10 @@ int ionic_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) struct ionic_mr *mr = to_ionic_mr(ibmr); int rc; + rc = ib_no_udata_io(udata); + if (rc) + return rc; + if (!mr->ibmr.lkey) goto out; @@ -1120,6 +1142,10 @@ int ionic_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) struct ionic_mr *mr = to_ionic_mw(ibmw); int rc; + rc = ib_no_udata_io(udata); + if (rc) + return rc; + rc = ionic_get_mrid(dev, &mr->mrid); if (rc) return rc; @@ -1292,6 +1318,10 @@ int ionic_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) struct ionic_vcq *vcq = to_ionic_vcq(ibcq); int udma_idx, rc_tmp, rc = 0; + rc = ib_no_udata_io(udata); + if (rc) + return rc; + for (udma_idx = dev->lif_cfg.udma_count; udma_idx; ) { --udma_idx; @@ -2585,6 +2615,10 @@ int ionic_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask, struct ionic_qp *qp = to_ionic_qp(ibqp); int rc; + rc = ib_no_udata_io(udata); + if (rc) + return rc; + rc = ionic_check_modify_qp(qp, attr, mask); if (rc) return rc; @@ -2658,6 +2692,10 @@ int ionic_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) struct ionic_cq *cq; int rc; + rc = ib_no_udata_io(udata); + if (rc) + return rc; + rc = ionic_destroy_qp_cmd(dev, qp->qpid); if (rc) return rc; diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c index 2b91bd03c73e..cba7809ec3d9 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.c +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c @@ -216,6 +216,7 @@ static const struct ib_device_ops ionic_dev_ops = { .owner = THIS_MODULE, .driver_id = RDMA_DRIVER_IONIC, .uverbs_abi_ver = IONIC_ABI_VERSION, + .uverbs_robust_udata = true, .alloc_ucontext = ionic_alloc_ucontext, .dealloc_ucontext = ionic_dealloc_ucontext, From 74f49255492a62658f36bf2578d7916f1c6ffad1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:49:18 +0800 Subject: [PATCH 071/160] RDMA/nldev: validate dynamic counter attribute length RDMA_NLDEV_ATTR_STAT_HWCOUNTERS is a nested attribute whose children are consumed directly with nla_get_u32(). The top-level policy validates only the container, so it does not establish the fixed shape of each child. Require every child payload to be exactly one u32 before reading it. Fixes: 3c3c1f141639 ("RDMA/nldev: Allow optional-counter status configuration through RDMA netlink") Reviewed-by: Zhu Yanjun Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260720114918.70323-1-pengpeng@iscas.ac.cn Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/nldev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index aae4f3f6bcba..b1a6c1670091 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -2157,6 +2157,11 @@ static int nldev_stat_set_counter_dynamic_doit(struct nlattr *tb[], nla_for_each_nested(entry_attr, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], rem) { + if (nla_len(entry_attr) != sizeof(u32)) { + ret = -EINVAL; + goto out; + } + index = nla_get_u32(entry_attr); if ((index >= stats->num_counters) || !(stats->descs[index].flags & IB_STAT_FLAG_OPTIONAL)) { From ef63cc441703412628a517dda354f3e51fe2dc92 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 21 Jul 2026 12:10:56 +0300 Subject: [PATCH 072/160] RDMA/srpt: Pass the mapped task attribute to target_init_cmd() srpt_handle_cmd() maps the initiator-supplied srp_cmd->task_attr into cmd->sam_task_attr, but then hands a hardcoded TCM_SIMPLE_TAG to target_init_cmd(). Pass the already mapped cmd->sam_task_attr instead, so target core sees the attribute the initiator requested. Fixes: 9474b043132f ("ib_srpt: Convert I/O path to target_submit_cmd + drop legacy ioctx->kref") Link: https://patch.msgid.link/20260721-b4-scsi-ordering-violation-due-to-hardc-v1-1-07205aab71bb@nvidia.com Reviewed-by: Bart Van Assche Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/srpt/ib_srpt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index a22ee4fc1e4c..7197d95f2216 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1602,7 +1602,7 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch, rc = target_init_cmd(cmd, ch->sess, &send_ioctx->sense_data[0], scsilun_to_int(&srp_cmd->lun), data_len, - TCM_SIMPLE_TAG, dir, TARGET_SCF_ACK_KREF); + cmd->sam_task_attr, dir, TARGET_SCF_ACK_KREF); if (rc != 0) { pr_debug("target_submit_cmd() returned %d for tag %#llx\n", rc, srp_cmd->tag); From 0ca79979384f031d710c4b3bae065dcb5d95aca3 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 21 Jul 2026 17:25:45 +0900 Subject: [PATCH 073/160] RDMA/erdma: Fix CEQ tasklet use-after-free on removal Each CEQ interrupt handler only schedules eqc->tasklet. The tasklet calls erdma_ceq_completion_handler(), which reads the DMA-coherent EQ ring through get_next_valid_eqe() and updates eq->dbrec through notify_eq(). erdma_ceqs_uninit() frees each CEQ IRQ and then destroys its EQ. free_irq() prevents another hard IRQ and waits for an in-flight handler, but it does not drain a tasklet that the handler already scheduled. The tasklet can therefore access eq->qbuf or eq->dbrec after erdma_eq_destroy() frees them. Clearing ceq_cb->ready does not synchronize with a tasklet that already passed the check at the start of erdma_ceq_completion_handler(). Kill the tasklet after free_irq(), when no handler can schedule it again, and before erdma_ceq_uninit_one() releases the EQ buffers. Fixes: f2a0a630b953 ("RDMA/erdma: Add event queue implementation") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260721082545.47395-1-mhun512@gmail.com Acked-by: Cheng Xu Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/erdma/erdma_eq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c index d5b9d19882b2..a8784e07acd6 100644 --- a/drivers/infiniband/hw/erdma/erdma_eq.c +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -220,6 +220,7 @@ static void erdma_free_ceq_irq(struct erdma_dev *dev, u16 ceqn) irq_set_affinity_hint(eqc->irq.msix_vector, NULL); free_irq(eqc->irq.msix_vector, eqc); + tasklet_kill(&eqc->tasklet); } static int create_eq_cmd(struct erdma_dev *dev, u32 eqn, struct erdma_eq *eq) From 8aae47a046439bad72cd6eaa5e81d695cc22e8a9 Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 10 Jun 2026 21:12:12 +0530 Subject: [PATCH 074/160] net: ionic: register PHC for rdma timestamping Currently, the driver only registers the PTP Hardware Clock (PHC) if Ethernet hardware timestamping is supported. Update the registration logic to register the PHC if the device supports either Ethernet hardware timestamping or RDMA completion timestamping. Co-developed-by: Allen Hubbe Signed-off-by: Allen Hubbe Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260610154216.712374-2-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- .../ethernet/pensando/ionic/ionic_ethtool.c | 12 +++++++---- .../net/ethernet/pensando/ionic/ionic_if.h | 1 + .../net/ethernet/pensando/ionic/ionic_lif.c | 5 ++++- .../net/ethernet/pensando/ionic/ionic_phc.c | 20 ++++++++++++------- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index c4ab4b5caa0a..fc8c50e8f365 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -1041,10 +1041,14 @@ static int ionic_get_ts_info(struct net_device *netdev, info->phc_index = ptp_clock_index(lif->phc->ptp); - info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_TX_HARDWARE | - SOF_TIMESTAMPING_RX_HARDWARE | - SOF_TIMESTAMPING_RAW_HARDWARE; + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE; + + if (!(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) + return 0; + + info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; /* tx modes */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 0a201422d0c5..6afc1fc3c7ed 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -1181,6 +1181,7 @@ enum ionic_eth_hw_features { IONIC_ETH_HW_TX_CSUM_GENEVE = BIT(18), IONIC_ETH_HW_TSO_GENEVE = BIT(19), IONIC_ETH_HW_TIMESTAMP = BIT(20), + IONIC_ETH_HW_RDMA_TIMESTAMP = BIT(21), }; /** diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index fd3ee9820531..af4aadd96cb9 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1500,7 +1500,8 @@ static int ionic_set_nic_features(struct ionic_lif *lif, ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features); if (lif->phc) - ctx.cmd.lif_setattr.features |= cpu_to_le64(IONIC_ETH_HW_TIMESTAMP); + ctx.cmd.lif_setattr.features |= lif->ionic->ident.lif.eth.config.features & + cpu_to_le64(IONIC_ETH_HW_TIMESTAMP | IONIC_ETH_HW_RDMA_TIMESTAMP); err = ionic_adminq_post_wait(lif, &ctx); if (err) @@ -1551,6 +1552,8 @@ static int ionic_set_nic_features(struct ionic_lif *lif, dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n"); if (lif->hw_features & IONIC_ETH_HW_TIMESTAMP) dev_dbg(dev, "feature ETH_HW_TIMESTAMP\n"); + if (lif->hw_features & IONIC_ETH_HW_RDMA_TIMESTAMP) + dev_dbg(dev, "feature ETH_HW_RDMA_TIMESTAMP\n"); return 0; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_phc.c b/drivers/net/ethernet/pensando/ionic/ionic_phc.c index 05b44fc482f8..116408099974 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_phc.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_phc.c @@ -77,7 +77,8 @@ static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif, bool rx_all; __le64 mask; - if (!lif->phc || !lif->phc->ptp) + if (!lif->phc || !lif->phc->ptp || + !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); @@ -210,7 +211,8 @@ int ionic_hwstamp_set(struct net_device *netdev, struct ionic_lif *lif = netdev_priv(netdev); int err; - if (!lif->phc || !lif->phc->ptp) + if (!lif->phc || !lif->phc->ptp || + !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->queue_lock); @@ -228,7 +230,8 @@ void ionic_lif_hwstamp_replay(struct ionic_lif *lif) { int err; - if (!lif->phc || !lif->phc->ptp) + if (!lif->phc || !lif->phc->ptp || + !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->queue_lock); @@ -242,7 +245,8 @@ void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif) { int err; - if (!lif->phc || !lif->phc->ptp) + if (!lif->phc || !lif->phc->ptp || + !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->phc->config_lock); @@ -267,7 +271,8 @@ int ionic_hwstamp_get(struct net_device *netdev, { struct ionic_lif *lif = netdev_priv(netdev); - if (!lif->phc || !lif->phc->ptp) + if (!lif->phc || !lif->phc->ptp || + !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); @@ -506,7 +511,8 @@ static const struct ptp_clock_info ionic_ptp_info = { void ionic_lif_register_phc(struct ionic_lif *lif) { - if (!lif->phc || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) + if (!lif->phc || + !(lif->hw_features & (IONIC_ETH_HW_TIMESTAMP | IONIC_ETH_HW_RDMA_TIMESTAMP))) return; lif->phc->ptp = ptp_clock_register(&lif->phc->ptp_info, lif->ionic->dev); @@ -545,7 +551,7 @@ void ionic_lif_alloc_phc(struct ionic_lif *lif) return; features = le64_to_cpu(ionic->ident.lif.eth.config.features); - if (!(features & IONIC_ETH_HW_TIMESTAMP)) + if (!(features & (IONIC_ETH_HW_TIMESTAMP | IONIC_ETH_HW_RDMA_TIMESTAMP))) return; phc = devm_kzalloc(ionic->dev, sizeof(*phc), GFP_KERNEL); From 02e643e22a4403fd170db0c7118582b1c273827b Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 10 Jun 2026 21:12:13 +0530 Subject: [PATCH 075/160] net: ionic: Add PHC state page for user space access Add a page associated with the PHC that can be mapped to user space, allowing applications to access hardware timestamp information. In order to synchronize between kernel and user space, a sequence number is incremented at the beginning and end of each update. An odd number means the data is being updated while an even number means the update is complete. To guarantee that the data structure was accessed atomically, user space will: repeat: seq1 = goto if odd seq2 = if seq1 != seq2 goto repeat This mechanism acts as a guard against reading invalid state during concurrent updates. Co-developed-by: Allen Hubbe Signed-off-by: Allen Hubbe Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260610154216.712374-3-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- .../net/ethernet/pensando/ionic/ionic_lif.h | 3 +- .../net/ethernet/pensando/ionic/ionic_phc.c | 43 +++++++++++++++++++ include/uapi/rdma/ib_user_verbs.h | 33 ++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index d34692462036..e790f6b9441e 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -248,7 +248,7 @@ struct ionic_lif { }; struct ionic_phc { - spinlock_t lock; /* lock for cc and tc */ + spinlock_t lock; /* lock for state_page, cc and tc */ struct cyclecounter cc; struct timecounter tc; @@ -261,6 +261,7 @@ struct ionic_phc { long aux_work_delay; struct ptp_clock_info ptp_info; + struct ib_uverbs_clock_info *state_page; struct ptp_clock *ptp; struct ionic_lif *lif; }; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_phc.c b/drivers/net/ethernet/pensando/ionic/ionic_phc.c index 116408099974..4b8d80a55b72 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_phc.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_phc.c @@ -3,6 +3,7 @@ #include #include +#include #include "ionic.h" #include "ionic_bus.h" @@ -334,6 +335,26 @@ static int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx) return ionic_adminq_post(phc->lif, ctx); } +static void ionic_phc_state_page_update(struct ionic_phc *phc) +{ + struct ib_uverbs_clock_info *state = phc->state_page; + u32 sign; + + /* read current sign */ + sign = smp_load_acquire(&state->sign) & ~1; + + /* make sign odd for updating */ + smp_store_mb(state->sign, sign | 1); + + state->cycles = phc->tc.cycle_last; + state->nsec = phc->tc.nsec; + state->frac = phc->tc.frac; + state->mult = phc->cc.mult; + + /* make sign the next even number for update completed */ + smp_store_release(&state->sign, sign + 2); +} + static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); @@ -361,6 +382,8 @@ static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm) timecounter_read(&phc->tc); phc->cc.mult = adj; + ionic_phc_state_page_update(phc); + /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ @@ -386,6 +409,8 @@ static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta) timecounter_adjtime(&phc->tc, delta); + ionic_phc_state_page_update(phc); + /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ @@ -415,6 +440,8 @@ static int ionic_phc_settime64(struct ptp_clock_info *info, timecounter_init(&phc->tc, &phc->cc, ns); + ionic_phc_state_page_update(phc); + /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ @@ -472,6 +499,8 @@ static long ionic_phc_aux_work(struct ptp_clock_info *info) /* update point-in-time basis to now */ timecounter_read(&phc->tc); + ionic_phc_state_page_update(phc); + /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ @@ -558,6 +587,12 @@ void ionic_lif_alloc_phc(struct ionic_lif *lif) if (!phc) return; + phc->state_page = (void *)get_zeroed_page(GFP_KERNEL); + if (!phc->state_page) { + devm_kfree(ionic->dev, phc); + return; + } + phc->lif = lif; phc->cc.read = ionic_cc_read; @@ -569,6 +604,7 @@ void ionic_lif_alloc_phc(struct ionic_lif *lif) dev_err(lif->ionic->dev, "Invalid device PHC mask multiplier %u, disabling HW timestamp support\n", phc->cc.mult); + free_page((unsigned long)phc->state_page); devm_kfree(lif->ionic->dev, phc); lif->phc = NULL; return; @@ -652,6 +688,12 @@ void ionic_lif_alloc_phc(struct ionic_lif *lif) */ phc->ptp_info.max_adj = NORMAL_PPB; + phc->state_page->mask = phc->cc.mask; + phc->state_page->shift = phc->cc.shift; + phc->state_page->overflow_period = delay; + + ionic_phc_state_page_update(phc); + lif->phc = phc; } @@ -662,6 +704,7 @@ void ionic_lif_free_phc(struct ionic_lif *lif) mutex_destroy(&lif->phc->config_lock); + free_page((unsigned long)lif->phc->state_page); devm_kfree(lif->ionic->dev, lif->phc); lif->phc = NULL; } diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h index d2aeadb6d2f9..565301594634 100644 --- a/include/uapi/rdma/ib_user_verbs.h +++ b/include/uapi/rdma/ib_user_verbs.h @@ -1379,4 +1379,37 @@ enum ib_uverbs_raw_packet_caps { IB_UVERBS_RAW_PACKET_CAP_DELAY_DROP = 1 << 3, }; +/* + * struct ib_uverbs_clock_info - timecounter state shared with userspace + * + * Drivers that use a software timecounter over a free-running hardware + * cycle counter can map this page read-only into userspace, allowing + * conversion of hardware timestamps to system time without a syscall. + * + * Synchronization uses a sequence counter (@sign): the kernel sets bit 0 + * before updating, then advances by 2 after. Userspace must retry the read + * if @sign is odd or changed during the read. + * + * @sign: Sequence counter (bit 0 = update in progress) + * @resv: Reserved + * @nsec: Nanoseconds at last update + * @cycles: Cycle counter value at last update + * @frac: Fractional nanoseconds at last update + * @mult: Cycle-to-nanosecond multiplier + * @shift: Cycle-to-nanosecond shift + * @mask: Cycle counter bitmask + * @overflow_period: Max interval (nsec) between reads before counter wraps + */ +struct ib_uverbs_clock_info { + __u32 sign; + __u32 resv; + __aligned_u64 nsec; + __aligned_u64 cycles; + __aligned_u64 frac; + __u32 mult; + __u32 shift; + __aligned_u64 mask; + __aligned_u64 overflow_period; +}; + #endif /* IB_USER_VERBS_H */ From 7462d18889bf5d1e5b2269da5e3ef694b1c5ca59 Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 10 Jun 2026 21:12:14 +0530 Subject: [PATCH 076/160] RDMA/ionic: map PHC state into user space Enable user space applications to access the PHC state page when firmware RDMA completion timestamp is supported. This mapping allows user space to convert RDMA completion timestamps to system wall time without kernel transitions, minimizing latency overhead. Applications can directly read the PHC state through mmap, enabling efficient timestamp correlation for precision timing applications. Co-developed-by: Allen Hubbe Signed-off-by: Allen Hubbe Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260610154216.712374-4-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- .../infiniband/hw/ionic/ionic_controlpath.c | 34 +++++++++++++++++++ drivers/infiniband/hw/ionic/ionic_ibdev.h | 2 ++ drivers/infiniband/hw/ionic/ionic_lif_cfg.c | 2 ++ drivers/infiniband/hw/ionic/ionic_lif_cfg.h | 1 + include/uapi/rdma/ionic-abi.h | 1 + 5 files changed, 40 insertions(+) diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c index 42b02ee643ad..a70ef59a8064 100644 --- a/drivers/infiniband/hw/ionic/ionic_controlpath.c +++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c @@ -391,6 +391,16 @@ int ionic_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata) goto err_mmap_dbell; } + if (dev->lif_cfg.phc_state) { + ctx->mmap_phc = ionic_mmap_entry_insert(ctx, PAGE_SIZE, 0, + IONIC_MMAP_PHC, + &resp.phc_offset); + if (!ctx->mmap_phc) { + rc = -ENOMEM; + goto err_mmap_phc; + } + } + resp.page_shift = PAGE_SHIFT; resp.dbell_offset = db_phys & ~PAGE_MASK; @@ -421,6 +431,8 @@ int ionic_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata) return 0; err_resp: + rdma_user_mmap_entry_remove(ctx->mmap_phc); +err_mmap_phc: rdma_user_mmap_entry_remove(ctx->mmap_dbell); err_mmap_dbell: ionic_put_dbid(dev, ctx->dbid); @@ -433,10 +445,26 @@ void ionic_dealloc_ucontext(struct ib_ucontext *ibctx) struct ionic_ibdev *dev = to_ionic_ibdev(ibctx->device); struct ionic_ctx *ctx = to_ionic_ctx(ibctx); + rdma_user_mmap_entry_remove(ctx->mmap_phc); rdma_user_mmap_entry_remove(ctx->mmap_dbell); ionic_put_dbid(dev, ctx->dbid); } +static int ionic_mmap_phc_state(struct ionic_ibdev *dev, + struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (vma->vm_flags & (VM_WRITE | VM_EXEC)) + return -EPERM; + + vm_flags_clear(vma, VM_MAYWRITE); + + return vm_insert_page(vma, vma->vm_start, + virt_to_page(dev->lif_cfg.phc_state)); +} + int ionic_mmap(struct ib_ucontext *ibctx, struct vm_area_struct *vma) { struct ionic_ibdev *dev = to_ionic_ibdev(ibctx->device); @@ -455,6 +483,12 @@ int ionic_mmap(struct ib_ucontext *ibctx, struct vm_area_struct *vma) ionic_entry = container_of(rdma_entry, struct ionic_mmap_entry, rdma_entry); + if (ionic_entry->mmap_flags & IONIC_MMAP_PHC) { + rc = ionic_mmap_phc_state(dev, vma); + rdma_user_mmap_entry_put(rdma_entry); + return rc; + } + ibdev_dbg(&dev->ibdev, "writecombine? %d\n", ionic_entry->mmap_flags & IONIC_MMAP_WC); if (ionic_entry->mmap_flags & IONIC_MMAP_WC) diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index 53dab2d54844..d7adaa161e6f 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -71,6 +71,7 @@ enum ionic_admin_flags { enum ionic_mmap_flag { IONIC_MMAP_WC = BIT(0), + IONIC_MMAP_PHC = BIT(1), }; struct ionic_mmap_entry { @@ -172,6 +173,7 @@ struct ionic_ctx { struct ib_ucontext ibctx; u32 dbid; struct rdma_user_mmap_entry *mmap_dbell; + struct rdma_user_mmap_entry *mmap_phc; }; struct ionic_tbl_buf { diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c index f3cd281c3a2f..f827dce59973 100644 --- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c +++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c @@ -40,6 +40,8 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg) cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif); cfg->dbpage = lif->kern_dbpage; cfg->intr_ctrl = lif->ionic->idev.intr_ctrl; + if (lif->phc) + cfg->phc_state = lif->phc->state_page; cfg->db_phys = lif->ionic->bars[IONIC_PCI_BAR_DBELL].bus_addr; diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h index 20853429f623..2b29e646c193 100644 --- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h +++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h @@ -23,6 +23,7 @@ struct ionic_lif_cfg { u64 __iomem *dbpage; struct ionic_intr __iomem *intr_ctrl; phys_addr_t db_phys; + void *phc_state; u64 page_size_supported; u32 npts_per_lif; diff --git a/include/uapi/rdma/ionic-abi.h b/include/uapi/rdma/ionic-abi.h index 7b589d3e9728..2c70ac149c4f 100644 --- a/include/uapi/rdma/ionic-abi.h +++ b/include/uapi/rdma/ionic-abi.h @@ -48,6 +48,7 @@ struct ionic_ctx_resp { __u8 expdb_qtypes; __u8 rsvd2[3]; + __aligned_u64 phc_offset; }; struct ionic_qdesc { From 8ba049504671fe52eeba29f70b022ee4b302507f Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 10 Jun 2026 21:12:15 +0530 Subject: [PATCH 077/160] RDMA/ionic: add completion timestamp to CQE format Update the CQE structure to include hardware timestamp. When firmware supports RDMA completion timestamps, the hardware populates the timestamp field. Co-developed-by: Allen Hubbe Signed-off-by: Allen Hubbe Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260610154216.712374-5-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/ionic/ionic_datapath.c | 43 ++++++++++---------- drivers/infiniband/hw/ionic/ionic_fw.h | 12 ++++-- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/hw/ionic/ionic_datapath.c b/drivers/infiniband/hw/ionic/ionic_datapath.c index aa2944887f23..3e2300f7ea10 100644 --- a/drivers/infiniband/hw/ionic/ionic_datapath.c +++ b/drivers/infiniband/hw/ionic/ionic_datapath.c @@ -32,6 +32,7 @@ static int ionic_flush_recv(struct ionic_qp *qp, struct ib_wc *wc) { struct ionic_rq_meta *meta; struct ionic_v1_wqe *wqe; + u64 wqe_idx; if (!qp->rq_flush) return 0; @@ -40,21 +41,22 @@ static int ionic_flush_recv(struct ionic_qp *qp, struct ib_wc *wc) return 0; wqe = ionic_queue_at_cons(&qp->rq); + wqe_idx = le64_to_cpu(wqe->base.wqe_idx); - /* wqe_id must be a valid queue index */ - if (unlikely(wqe->base.wqe_id >> qp->rq.depth_log2)) { + /* wqe_idx must be a valid queue index */ + if (unlikely(wqe_idx >> qp->rq.depth_log2)) { ibdev_warn(qp->ibqp.device, "flush qp %u recv index %llu invalid\n", - qp->qpid, (unsigned long long)wqe->base.wqe_id); + qp->qpid, (unsigned long long)wqe_idx); return -EIO; } - /* wqe_id must indicate a request that is outstanding */ - meta = &qp->rq_meta[wqe->base.wqe_id]; + /* wqe_idx must indicate a request that is outstanding */ + meta = &qp->rq_meta[wqe_idx]; if (unlikely(meta->next != IONIC_META_POSTED)) { ibdev_warn(qp->ibqp.device, "flush qp %u recv index %llu not posted\n", - qp->qpid, (unsigned long long)wqe->base.wqe_id); + qp->qpid, (unsigned long long)wqe_idx); return -EIO; } @@ -133,8 +135,8 @@ static int ionic_poll_recv(struct ionic_ibdev *dev, struct ionic_cq *cq, { struct ionic_qp *qp = NULL; struct ionic_rq_meta *meta; + u16 vlan_tag, wqe_idx; u32 src_qpn, st_len; - u16 vlan_tag; u8 op; if (cqe_qp->rq_flush) @@ -144,7 +146,7 @@ static int ionic_poll_recv(struct ionic_ibdev *dev, struct ionic_cq *cq, st_len = be32_to_cpu(cqe->status_length); - /* ignore wqe_id in case of flush error */ + /* ignore wqe_idx in case of flush error */ if (ionic_v1_cqe_error(cqe) && st_len == IONIC_STS_WQE_FLUSHED_ERR) { cqe_qp->rq_flush = true; cq->flush = true; @@ -160,20 +162,19 @@ static int ionic_poll_recv(struct ionic_ibdev *dev, struct ionic_cq *cq, return -EIO; } - /* wqe_id must be a valid queue index */ - if (unlikely(cqe->recv.wqe_id >> qp->rq.depth_log2)) { + wqe_idx = le64_to_cpu(cqe->recv.wqe_idx_timestamp) & IONIC_V1_CQE_WQE_IDX_MASK; + /* wqe_idx must be a valid queue index */ + if (unlikely(wqe_idx >> qp->rq.depth_log2)) { ibdev_warn(&dev->ibdev, - "qp %u recv index %llu invalid\n", - qp->qpid, (unsigned long long)cqe->recv.wqe_id); + "qp %u recv index %u invalid\n", qp->qpid, wqe_idx); return -EIO; } - /* wqe_id must indicate a request that is outstanding */ - meta = &qp->rq_meta[cqe->recv.wqe_id]; + /* wqe_idx must indicate a request that is outstanding */ + meta = &qp->rq_meta[wqe_idx]; if (unlikely(meta->next != IONIC_META_POSTED)) { ibdev_warn(&dev->ibdev, - "qp %u recv index %llu not posted\n", - qp->qpid, (unsigned long long)cqe->recv.wqe_id); + "qp %u recv index %u not posted\n", qp->qpid, wqe_idx); return -EIO; } @@ -408,7 +409,7 @@ static int ionic_comp_msn(struct ionic_qp *qp, struct ionic_v1_cqe *cqe) static int ionic_comp_npg(struct ionic_qp *qp, struct ionic_v1_cqe *cqe) { struct ionic_sq_meta *meta; - u16 cqe_idx; + u16 wqe_idx; u32 st_len; if (qp->sq_flush) @@ -430,8 +431,8 @@ static int ionic_comp_npg(struct ionic_qp *qp, struct ionic_v1_cqe *cqe) return 0; } - cqe_idx = cqe->send.npg_wqe_id & qp->sq.mask; - meta = &qp->sq_meta[cqe_idx]; + wqe_idx = le64_to_cpu(cqe->send.npg_wqe_idx_timestamp) & qp->sq.mask; + meta = &qp->sq_meta[wqe_idx]; meta->local_comp = true; if (ionic_v1_cqe_error(cqe)) { @@ -811,7 +812,7 @@ static void ionic_prep_base(struct ionic_qp *qp, meta->signal = false; meta->local_comp = false; - wqe->base.wqe_id = qp->sq.prod; + wqe->base.wqe_idx = cpu_to_le64(qp->sq.prod); if (wr->send_flags & IB_SEND_FENCE) wqe->base.flags |= cpu_to_be16(IONIC_V1_FLAG_FENCE); @@ -1205,7 +1206,7 @@ static int ionic_prep_recv(struct ionic_qp *qp, meta->wrid = wr->wr_id; - wqe->base.wqe_id = meta - qp->rq_meta; + wqe->base.wqe_idx = cpu_to_le64(meta - qp->rq_meta); wqe->base.num_sge_key = wr->num_sge; /* total length for recv goes in base imm_data_key */ diff --git a/drivers/infiniband/hw/ionic/ionic_fw.h b/drivers/infiniband/hw/ionic/ionic_fw.h index adfbb89d856c..ee23062a1762 100644 --- a/drivers/infiniband/hw/ionic/ionic_fw.h +++ b/drivers/infiniband/hw/ionic/ionic_fw.h @@ -332,7 +332,7 @@ struct ionic_v1_cqe { __le16 old_rq_cq_cindex; } admin; struct { - __u64 wqe_id; + __le64 wqe_idx_timestamp; __be32 src_qpn_op; __u8 src_mac[6]; __be16 vlan_tag; @@ -342,13 +342,19 @@ struct ionic_v1_cqe { __u8 rsvd[4]; __be32 msg_msn; __u8 rsvd2[8]; - __u64 npg_wqe_id; + __le64 npg_wqe_idx_timestamp; } send; }; __be32 status_length; __be32 qid_type_flags; }; +/* bits for cqe wqe_idx and timestamp */ +enum ionic_v1_cqe_wqe_idx_timestamp_bits { + IONIC_V1_CQE_WQE_IDX_MASK = 0xffff, + IONIC_V1_CQE_TIMESTAMP_SHIFT = 16, +}; + /* bits for cqe recv */ enum ionic_v1_cqe_src_qpn_bits { IONIC_V1_CQE_RECV_QPN_MASK = 0xffffff, @@ -423,7 +429,7 @@ static inline u32 ionic_v1_cqe_qtf_qid(u32 qtf) /* v1 base wqe header */ struct ionic_v1_base_hdr { - __u64 wqe_id; + __le64 wqe_idx; __u8 op; __u8 num_sge_key; __be16 flags; From 63d6c2d10c15ba3a41cee15e3cc50fda95c35984 Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 10 Jun 2026 21:12:16 +0530 Subject: [PATCH 078/160] RDMA/mlx5: move mlx5 clock info to common struct ib_uverbs_clock_info Use struct ib_uverbs_clock_info from ib_user_verbs.h for clock info. Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260610154216.712374-6-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- include/uapi/rdma/mlx5-abi.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index 8a6ad6c6841c..a39226cd62dc 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -472,17 +472,10 @@ struct mlx5_ib_modify_wq { __u32 reserved; }; -struct mlx5_ib_clock_info { - __u32 sign; - __u32 resv; - __aligned_u64 nsec; - __aligned_u64 cycles; - __aligned_u64 frac; - __u32 mult; - __u32 shift; - __aligned_u64 mask; - __aligned_u64 overflow_period; -}; +/* + * deprecated, see struct ib_uverbs_clock_info from ib_user_verbs.h + */ +#define mlx5_ib_clock_info ib_uverbs_clock_info enum mlx5_ib_mmap_cmd { MLX5_IB_MMAP_REGULAR_PAGE = 0, From 97f7c2262c28ebcae64fc957ee978646684a5ed9 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 21 Jul 2026 18:00:01 +0300 Subject: [PATCH 079/160] RDMA/mana_ib: drain QP references after partial table insertion mana_table_store_ud_qp() publishes a QP at its send-queue id before inserting the receive-queue id, dropping the XArray lock between the two xa_insert_irq() calls. A concurrent completion handler can look up the QP and take a transient reference. When the second insertion fails, the rollback erased only the send-queue entry and returned, leaving both the initial table reference and the transient reference outstanding while RDMA core frees the QP, causing a use-after-free. Drain the reference as normal destruction does: drop the initial reference and wait for qp->free, releasing the QP only after every concurrent lookup returns its reference. Fixes: 8001e9257eca ("RDMA/mana_ib: extend mana QP table") Link: https://patch.msgid.link/20260721-if-mana-table-store-qp-qids-partiall-v1-1-8fb3d2d2b559@nvidia.com Reviewed-by: Konstantin Taranov Reviewed-by: Long Li Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mana/qp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index b5ff07e34eb7..c52f3ec14032 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -472,6 +472,12 @@ static void mana_table_remove_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp xa_erase_irq(&mdev->qp_table_wq, qp->ibqp.qp_num); } +static void mana_table_drain_qp_ref(struct mana_ib_qp *qp) +{ + mana_put_qp_ref(qp); + wait_for_completion(&qp->free); +} + static int mana_table_store_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) { u32 qids = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].id | MANA_SENDQ_MASK; @@ -490,6 +496,7 @@ static int mana_table_store_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *q remove_sq: xa_erase_irq(&mdev->qp_table_wq, qids); + mana_table_drain_qp_ref(qp); return err; } @@ -537,8 +544,7 @@ static void mana_table_remove_qp(struct mana_ib_dev *mdev, qp->ibqp.qp_type); return; } - mana_put_qp_ref(qp); - wait_for_completion(&qp->free); + mana_table_drain_qp_ref(qp); } static int mana_ib_create_rc_qp(struct ib_qp *ibqp, struct ib_pd *ibpd, From 8d186210677c0322db886973bcec9aa4d21b51cd Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:00 +0300 Subject: [PATCH 080/160] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Add rdma_restrack_abort_del(), rdma_restrack_begin_del() and rdma_restrack_commit_del() functions to allow deleting a resource from the xarray to effectively prevent future access to it and wait for all current users to finish while preserving its index in the xarray to allow to re-insert it if needed with guaranteed success. This is a preparatory change for subsequent patches in the series which will use these functions to fix the cleanup flow. Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-1-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/restrack.c | 165 +++++++++++++++++++++++------ drivers/infiniband/core/restrack.h | 3 + 2 files changed, 135 insertions(+), 33 deletions(-) diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index 1b2f9df49e28..ff228d6fb977 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -129,6 +129,46 @@ static void rdma_restrack_attach_task(struct rdma_restrack_entry *res, res->user = true; } +static struct rdma_restrack_root *res_to_rt(struct rdma_restrack_entry *res) +{ + struct ib_device *dev = res_to_dev(res); + + if (WARN_ON(!dev)) + return NULL; + + return &dev->res[res->type]; +} + +static void restrack_drain_res(struct rdma_restrack_root *rt, + struct rdma_restrack_entry *res) +{ + if (rt) { + struct rdma_restrack_entry *old; + + old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, + GFP_KERNEL); + WARN_ON(old != res); + } + + rdma_restrack_put(res); + wait_for_completion(&res->comp); +} + +static void restrack_restore_res(struct rdma_restrack_root *rt, + struct rdma_restrack_entry *res) +{ + reinit_completion(&res->comp); + kref_init(&res->kref); + + if (rt) { + struct rdma_restrack_entry *old; + + old = xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, + GFP_KERNEL); + WARN_ON(old); + } +} + /** * rdma_restrack_set_name() - set the task for this resource * @res: resource entry @@ -177,22 +217,23 @@ void rdma_restrack_new(struct rdma_restrack_entry *res, EXPORT_SYMBOL(rdma_restrack_new); /** - * rdma_restrack_add() - add object to the resource tracking database + * rdma_restrack_add() - add object to the resource tracking database. + * If this resource reuses an ID of a resource that was already destroyed + * after calling rdma_restrack_begin() but didn't yet call + * rdma_restrack_commit_del() it can result in an untracked QP. * @res: resource entry */ void rdma_restrack_add(struct rdma_restrack_entry *res) { - struct ib_device *dev = res_to_dev(res); struct rdma_restrack_root *rt; int ret = 0; - if (!dev) - return; - if (res->no_track) goto out; - rt = &dev->res[res->type]; + rt = res_to_rt(res); + if (!rt) + return; if (res->type == RDMA_RESTRACK_QP) { /* Special case to ensure that LQPN points to right QP */ @@ -229,6 +270,28 @@ void rdma_restrack_add(struct rdma_restrack_entry *res) } EXPORT_SYMBOL(rdma_restrack_add); +/** + * rdma_restrack_abort_del() - re-add object to the resource tracking database + * it can only be used after rdma_restrack_begin_del(). + * @res: resource entry + */ +void rdma_restrack_abort_del(struct rdma_restrack_entry *res) +{ + struct rdma_restrack_root *rt = NULL; + + if (!res->valid) + return; + + if (!res->no_track) { + rt = res_to_rt(res); + if (!rt) + return; + } + + restrack_restore_res(rt, res); +} +EXPORT_SYMBOL(rdma_restrack_abort_del); + int __must_check rdma_restrack_get(struct rdma_restrack_entry *res) { return kref_get_unless_zero(&res->kref); @@ -265,7 +328,7 @@ static void restrack_release(struct kref *kref) struct rdma_restrack_entry *res; res = container_of(kref, struct rdma_restrack_entry, kref); - if (res->task) { + if (res->task && !res->valid) { put_task_struct(res->task); res->task = NULL; } @@ -291,37 +354,20 @@ EXPORT_SYMBOL(rdma_restrack_put); */ void rdma_restrack_sync(struct rdma_restrack_entry *res) { - struct rdma_restrack_entry *old; struct rdma_restrack_root *rt; - struct task_struct *task; - struct ib_device *dev; if (!res->valid || res->no_track) return; - dev = res_to_dev(res); - if (WARN_ON(!dev)) + rt = res_to_rt(res); + if (!rt) return; - rt = &dev->res[res->type]; if (WARN_ON(xa_get_mark(&rt->xa, res->id, RESTRACK_DD))) return; - old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL); - if (WARN_ON(old != res)) - return; - - task = res->task; - if (task) - get_task_struct(task); - rdma_restrack_put(res); - wait_for_completion(&res->comp); - reinit_completion(&res->comp); - if (task) - res->task = task; - kref_init(&res->kref); - - xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, GFP_KERNEL); + restrack_drain_res(rt, res); + restrack_restore_res(rt, res); } EXPORT_SYMBOL(rdma_restrack_sync); @@ -333,7 +379,6 @@ void rdma_restrack_del(struct rdma_restrack_entry *res) { struct rdma_restrack_entry *old; struct rdma_restrack_root *rt; - struct ib_device *dev; if (!res->valid) { if (res->task) { @@ -346,12 +391,10 @@ void rdma_restrack_del(struct rdma_restrack_entry *res) if (res->no_track) goto out; - dev = res_to_dev(res); - if (WARN_ON(!dev)) + rt = res_to_rt(res); + if (!rt) return; - rt = &dev->res[res->type]; - old = xa_erase(&rt->xa, res->id); WARN_ON(old != res); @@ -359,5 +402,61 @@ void rdma_restrack_del(struct rdma_restrack_entry *res) res->valid = false; rdma_restrack_put(res); wait_for_completion(&res->comp); + if (res->task) { + put_task_struct(res->task); + res->task = NULL; + } } EXPORT_SYMBOL(rdma_restrack_del); + +/** + * rdma_restrack_begin_del() - invalidate the object from the resource tracking + * database but preserve its index in the array. + * Since this preserves the index in the array until rdma_restrack_commit_del() + * is called, if rdma_restrack_add() is called in between with an old QP ID it + * can result in an untracked QP. + * @res: resource entry + */ +void rdma_restrack_begin_del(struct rdma_restrack_entry *res) +{ + struct rdma_restrack_root *rt = NULL; + + if (!res->valid) + return; + + if (!res->no_track) { + rt = res_to_rt(res); + if (!rt) + return; + } + + restrack_drain_res(rt, res); +} +EXPORT_SYMBOL(rdma_restrack_begin_del); + +/** + * rdma_restrack_commit_del() - delete object from the resource tracking + * database and free the task. + * @res: resource entry + */ +void rdma_restrack_commit_del(struct rdma_restrack_entry *res) +{ + struct rdma_restrack_root *rt; + + if (!res->valid || res->no_track) + goto out; + + rt = res_to_rt(res); + if (!rt) + return; + + xa_erase(&rt->xa, res->id); + +out: + res->valid = false; + if (res->task) { + put_task_struct(res->task); + res->task = NULL; + } +} +EXPORT_SYMBOL(rdma_restrack_commit_del); diff --git a/drivers/infiniband/core/restrack.h b/drivers/infiniband/core/restrack.h index 75b8d1005a98..2df78e084e10 100644 --- a/drivers/infiniband/core/restrack.h +++ b/drivers/infiniband/core/restrack.h @@ -26,8 +26,11 @@ struct rdma_restrack_root { int rdma_restrack_init(struct ib_device *dev); void rdma_restrack_clean(struct ib_device *dev); void rdma_restrack_add(struct rdma_restrack_entry *res); +void rdma_restrack_abort_del(struct rdma_restrack_entry *res); void rdma_restrack_del(struct rdma_restrack_entry *res); void rdma_restrack_sync(struct rdma_restrack_entry *res); +void rdma_restrack_begin_del(struct rdma_restrack_entry *res); +void rdma_restrack_commit_del(struct rdma_restrack_entry *res); void rdma_restrack_new(struct rdma_restrack_entry *res, enum rdma_restrack_type type); void rdma_restrack_set_name(struct rdma_restrack_entry *res, From 709ba0e5311bd034eb4d9c1c00cc4e1109d6dc3e Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:01 +0300 Subject: [PATCH 081/160] RDMA/core: Fix use after free in ib_query_qp() When querying a QP via the netlink flow the only synchronization mechanism for the said QP is rdma_restrack_get(), meanwhile during the QP destroy path rdma_restrack_del() is called at the end of the ib_destroy_qp_user() function which is too late, since by then the vendor specific resources for said QP would already be destroyed, and till the rdma_restrack_del() is called this QP can still be accessed, which could cause the use after free below. Fix this by moving the rdma_restrack_begin_del() to the start of the ib_destroy_qp_user(), which in turn waits for all usages of the QP to be done then removes it from the database to prevent access to it while it is being destroyed. RIP: 0010:ib_query_qp+0x15/0x50 [ib_core] Code: 48 83 05 5d 8e b9 ff 01 eb b5 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 c7 46 40 00 00 00 00 48 c7 46 78 00 00 00 00 <48> 8b 07 48 8b 80 88 01 00 00 48 85 c0 74 1a 48 83 05 54 91 b9 ff RSP: 0018:ff11000108a8f2f0 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ff11000108a8f370 RCX: ff11000108a8f370 RDX: 0000000000000000 RSI: ff11000108a8f3d8 RDI: 0000000000000000 RBP: ff1100010de5a000 R08: 0000000000000e80 R09: 0000000000000004 R10: ff110001057a604c R11: 0000000000000000 R12: ff11000108a8f370 R13: ff110001090e8000 R14: 0000000000000000 R15: ff110001057a602c FS: 00007f2ffd8db6c0(0000) GS:ff110008dc90b000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000010b9a7004 CR4: 0000000000373eb0 Call Trace: mlx5_ib_gsi_query_qp+0x21/0x50 [mlx5_ib] mlx5_ib_query_qp+0x689/0x9d0 [mlx5_ib] ib_query_qp+0x35/0x50 [ib_core] fill_res_qp_entry_query.isra.0+0x47/0x280 [ib_core] ? __wake_up+0x40/0x50 ? netlink_broadcast_filtered+0x15a/0x550 ? kobject_uevent_env+0x562/0x710 ? ep_poll_callback+0x242/0x270 ? __nla_put+0xc/0x20 ? nla_put+0x28/0x40 ? nla_put_string+0x2e/0x40 [ib_core] fill_res_qp_entry+0x138/0x190 [ib_core] res_get_common_dumpit+0x4a5/0x800 [ib_core] ? fill_res_qp_entry_query.isra.0+0x280/0x280 [ib_core] nldev_res_get_qp_dumpit+0x1e/0x30 [ib_core] netlink_dump+0x16f/0x450 __netlink_dump_start+0x1ce/0x2e0 rdma_nl_rcv_msg+0x1d3/0x330 [ib_core] ? nldev_res_get_qp_raw_dumpit+0x30/0x30 [ib_core] rdma_nl_rcv_skb.constprop.0.isra.0+0x108/0x180 [ib_core] rdma_nl_rcv+0x12/0x20 [ib_core] netlink_unicast+0x255/0x380 ? __alloc_skb+0xfa/0x1e0 netlink_sendmsg+0x1f3/0x420 __sock_sendmsg+0x38/0x60 ____sys_sendmsg+0x1e8/0x230 ? copy_msghdr_from_user+0xea/0x170 ___sys_sendmsg+0x7c/0xb0 ? __futex_wait+0x95/0xf0 ? __futex_wake_mark+0x40/0x40 ? futex_wait+0x67/0x100 ? futex_wake+0xac/0x1b0 __sys_sendmsg+0x5f/0xb0 do_syscall_64+0x55/0xb90 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Fixes: 514aee660df4 ("RDMA: Globally allocate and release QP memory") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-2-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/verbs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 3b613b57e269..7abb89a4e6a0 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -2154,6 +2154,8 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata) if (qp->real_qp != qp) return __ib_destroy_shared_qp(qp); + rdma_restrack_begin_del(&qp->res); + sec = qp->qp_sec; if (sec) ib_destroy_qp_security_begin(sec); @@ -2166,6 +2168,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata) if (ret) { if (sec) ib_destroy_qp_security_abort(sec); + rdma_restrack_abort_del(&qp->res); return ret; } @@ -2178,7 +2181,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata) if (sec) ib_destroy_qp_security_end(sec); - rdma_restrack_del(&qp->res); + rdma_restrack_commit_del(&qp->res); kfree(qp); return ret; } From 3481bec4dfc4aee24ffea5a547ee95b70b67d9d5 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:02 +0300 Subject: [PATCH 082/160] RDMA/core: Fix potential use after free in ib_destroy_cq_user() When accessing a CQ via the netlink path the only synchronization mechanism for the said CQ is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of ib_destroy_cq_user(), which is too late, since by that point vendor-specific resources associated with the CQ might already be freed. This can leave a short window where the CQ remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_begin_del() call to the start of ib_destroy_cq_user(), ensuring that the CQ is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a CQ that is in the process of destruction. In addition, this change preserves the intended inverted order between create and destroy routines: resources are added to restrack at the end of successful creation, and hence shall be removed from the restrack first thing during the destruction flow, which keeps the lifecycle management consistent and predictable. Fixes: 08f294a1524b ("RDMA/core: Add resource tracking for create and destroy CQs") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-3-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/verbs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 7abb89a4e6a0..568cb71da726 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -2247,11 +2247,15 @@ int ib_destroy_cq_user(struct ib_cq *cq, struct ib_udata *udata) if (atomic_read(&cq->usecnt)) return -EBUSY; - ret = cq->device->ops.destroy_cq(cq, udata); - if (ret) - return ret; + rdma_restrack_begin_del(&cq->res); - rdma_restrack_del(&cq->res); + ret = cq->device->ops.destroy_cq(cq, udata); + if (ret) { + rdma_restrack_abort_del(&cq->res); + return ret; + } + + rdma_restrack_commit_del(&cq->res); kfree(cq); return ret; } From 88244ecc71cc0b3ed200f5ef7ddea6686adfd730 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:03 +0300 Subject: [PATCH 083/160] RDMA/core: Fix potential use after free in ib_destroy_srq_user() When accessing a SRQ via the netlink path the only synchronization mechanism for the said SRQ is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of ib_destroy_srq_user(), which is too late, since by that point vendor-specific resources associated with the SRQ might already be freed. This can leave a short window where the SRQ remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_begin_del() call to the start of ib_destroy_srq_user(), ensuring that the SRQ is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a SRQ that is in the process of destruction. In addition, this change preserves the intended inverted order between create and destroy routines: resources are added to restrack at the end of successful creation, and hence shall be removed from the restrack first thing during the destruction flow, which keeps the lifecycle management consistent and predictable. Fixes: 48f8a70e899f ("RDMA/restrack: Add support to get resource tracking for SRQ") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-4-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/verbs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 568cb71da726..bfbc25dee95d 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1140,16 +1140,20 @@ int ib_destroy_srq_user(struct ib_srq *srq, struct ib_udata *udata) if (atomic_read(&srq->usecnt)) return -EBUSY; + rdma_restrack_begin_del(&srq->res); + ret = srq->device->ops.destroy_srq(srq, udata); - if (ret) + if (ret) { + rdma_restrack_abort_del(&srq->res); return ret; + } atomic_dec(&srq->pd->usecnt); if (srq->srq_type == IB_SRQT_XRC && srq->ext.xrc.xrcd) atomic_dec(&srq->ext.xrc.xrcd->usecnt); if (ib_srq_has_cq(srq->srq_type)) atomic_dec(&srq->ext.cq->usecnt); - rdma_restrack_del(&srq->res); + rdma_restrack_commit_del(&srq->res); kfree(srq); return ret; From 235ef2d0e750885c29340b0fc40620a7a4f52e12 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:04 +0300 Subject: [PATCH 084/160] RDMA/core: Fix potential use after free in counter_release() When accessing a counter via the netlink path the only synchronization mechanism for the said counter is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of counter_release(), which is too late, since by that point vendor-specific resources associated with the counter might already be freed. This can leave a short window where the counter remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_del() call to be before the freeing of the vendor-specific resources, ensuring that the counter is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a counter that is in the process of destruction. Fixes: 99fa331dc862 ("RDMA/counter: Add "auto" configuration mode support") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-5-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/counters.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c index a9e189194c13..a2c85840c501 100644 --- a/drivers/infiniband/core/counters.c +++ b/drivers/infiniband/core/counters.c @@ -234,7 +234,6 @@ static void rdma_counter_free(struct rdma_counter *counter) mutex_unlock(&port_counter->lock); - rdma_restrack_del(&counter->res); rdma_free_hw_stats_struct(counter->stats); kfree(counter); } @@ -329,6 +328,7 @@ static void counter_release(struct kref *kref) counter = container_of(kref, struct rdma_counter, kref); counter_history_stat_update(counter); + rdma_restrack_del(&counter->res); counter->device->ops.counter_dealloc(counter); rdma_counter_free(counter); } @@ -490,7 +490,8 @@ static struct rdma_counter *rdma_get_counter_by_id(struct ib_device *dev, return NULL; counter = container_of(res, struct rdma_counter, res); - kref_get(&counter->kref); + if (!kref_get_unless_zero(&counter->kref)) + counter = NULL; rdma_restrack_put(res); return counter; From 29dc2f8e1c97372c2871a70088707933515fbd5b Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:05 +0300 Subject: [PATCH 085/160] RDMA/core: Fix potential use after free in ib_free_cq() When accessing a CQ via the netlink path the only synchronization mechanism for the said CQ is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of ib_free_cq(), which is too late, since by that point vendor-specific resources associated with the CQ might already be freed. This can leave a short window where the CQ remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_del() call to be before the freeing of the vendor-specific resources ensuring that the CQ is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a CQ that is in the process of destruction. Fixes: 43d781b9fa56 ("RDMA: Allow fail of destroy CQ") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-6-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/cq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index ee98188e57fb..12304c9a9403 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -327,6 +327,7 @@ void ib_free_cq(struct ib_cq *cq) if (WARN_ON_ONCE(cq->cqe_used)) return; + rdma_restrack_del(&cq->res); if (cq->device->ops.pre_destroy_cq) { ret = cq->device->ops.pre_destroy_cq(cq); WARN_ONCE(ret, "Disable of kernel CQ shouldn't fail"); @@ -353,7 +354,6 @@ void ib_free_cq(struct ib_cq *cq) else ret = cq->device->ops.destroy_cq(cq, NULL); WARN_ONCE(ret, "Destroy of kernel CQ shouldn't fail"); - rdma_restrack_del(&cq->res); kfree(cq->wc); kfree(cq); } From 2696626a0be5877f445fb647c25ef43930c777e6 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:06 +0300 Subject: [PATCH 086/160] RDMA/core: Fix potential use after free in uverbs_free_dmah() When accessing a dmah via the netlink path the only synchronization mechanism for the said dmah is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of uverbs_free_dmah(), which is too late, since by that point vendor-specific resources associated with the dmah might already be freed. This can leave a short window where the dmah remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_begin_del() call to the start of uverbs_free_dmah(), ensuring that the dmah is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a dmah that is in the process of destruction. In addition, this change preserves the intended inverted order between create and destroy routines: resources are added to restrack at the end of successful creation, and hence shall be removed from the restrack first thing during the destruction flow, which keeps the lifecycle management consistent and predictable. Fixes: d83edab562a4 ("RDMA/core: Introduce a DMAH object and its alloc/free APIs") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-7-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/uverbs_std_types_dmah.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c index 97101e093826..9873ab49a601 100644 --- a/drivers/infiniband/core/uverbs_std_types_dmah.c +++ b/drivers/infiniband/core/uverbs_std_types_dmah.c @@ -18,11 +18,14 @@ static int uverbs_free_dmah(struct ib_uobject *uobject, if (atomic_read(&dmah->usecnt)) return -EBUSY; + rdma_restrack_begin_del(&dmah->res); ret = dmah->device->ops.dealloc_dmah(dmah, attrs); - if (ret) + if (ret) { + rdma_restrack_abort_del(&dmah->res); return ret; + } - rdma_restrack_del(&dmah->res); + rdma_restrack_commit_del(&dmah->res); kfree(dmah); return 0; } From 8b90e701342275f414e36e7421c502237df241ad Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Mon, 13 Jul 2026 18:38:07 +0300 Subject: [PATCH 087/160] RDMA/core: Fix potential use after free in ib_dealloc_pd_user() When accessing a PD via the netlink path the only synchronization mechanism for the said PD is rdma_restrack_get(). Currently, rdma_restrack_del() is invoked at the end of ib_dealloc_pd_user(), which is too late, since by that point vendor-specific resources associated with the PD might already be freed. This can leave a short window where the PD remains accessible through restrack, leading to a potential use-after-free. Fix this by moving the rdma_restrack_begin_del() call to the start of ib_dealloc_pd_user(), ensuring that the PD is removed from restrack before its internal resources are released. This guarantees that no new users hold references to a PD that is in the process of destruction. In addition, this change preserves the intended inverted order between create and destroy routines: resources are added to restrack at the end of successful creation, and hence shall be removed from the restrack first thing during the destruction flow, which keeps the lifecycle management consistent and predictable. Fixes: 91a7c58fce06 ("RDMA: Restore ability to fail on PD deallocate") Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260713-restrack-uaf-fix-resub-v2-8-bbe8bb270d51@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/verbs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index bfbc25dee95d..f86e6f30b1df 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -392,6 +392,7 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata) { int ret; + rdma_restrack_begin_del(&pd->res); if (pd->__internal_mr) { ret = pd->device->ops.dereg_mr(pd->__internal_mr, NULL); WARN_ON(ret); @@ -399,10 +400,12 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata) } ret = pd->device->ops.dealloc_pd(pd, udata); - if (ret) + if (ret) { + rdma_restrack_abort_del(&pd->res); return ret; + } - rdma_restrack_del(&pd->res); + rdma_restrack_commit_del(&pd->res); kfree(pd); return ret; } From b3818ea4ad1e2eff8472e8280daba0d2aabfe3c0 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:35:58 +0000 Subject: [PATCH 088/160] RDMA/core: Add Completion Counters support Add core infrastructure for Completion Counters, a light-weight alternative to polling CQ for tracking operation completions. Define the UVERBS_OBJECT_COMP_CNTR ioctl object with create, destroy, modify and read methods for both success and error counters. Add a QP attach method on the QP object to associate a completion counter with a queue pair. Add ib_comp_cntr struct, ib_comp_cntr_attach_attr, device ops, and DECLARE_RDMA_OBJ_SIZE for driver object allocation. Only userspace Completion Counters are supported at this stage. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-2-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/Makefile | 1 + drivers/infiniband/core/device.c | 6 + drivers/infiniband/core/rdma_core.h | 1 + .../core/uverbs_std_types_comp_cntr.c | 159 ++++++++++++++++++ drivers/infiniband/core/uverbs_std_types_qp.c | 51 +++++- drivers/infiniband/core/uverbs_uapi.c | 1 + include/rdma/ib_verbs.h | 37 ++++ include/uapi/rdma/ib_user_ioctl_cmds.h | 36 ++++ include/uapi/rdma/ib_user_ioctl_verbs.h | 19 +++ 9 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 drivers/infiniband/core/uverbs_std_types_comp_cntr.c diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index ab7a2197bc86..47ef6b0afd29 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -38,6 +38,7 @@ ib_umad-y := user_mad.o ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \ uverbs_std_types.o uverbs_ioctl.o \ uverbs_std_types_cq.o \ + uverbs_std_types_comp_cntr.o \ uverbs_std_types_dmabuf.o \ uverbs_std_types_dmah.o \ uverbs_std_types_flow_action.o uverbs_std_types_dm.o \ diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index b8193e077a74..a4c57279f19d 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2742,6 +2742,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, create_ah); SET_DEVICE_OP(dev_ops, create_counters); SET_DEVICE_OP(dev_ops, create_cq); + SET_DEVICE_OP(dev_ops, create_comp_cntr); SET_DEVICE_OP(dev_ops, create_user_cq); SET_DEVICE_OP(dev_ops, create_flow); SET_DEVICE_OP(dev_ops, create_qp); @@ -2762,6 +2763,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, destroy_ah); SET_DEVICE_OP(dev_ops, destroy_counters); SET_DEVICE_OP(dev_ops, destroy_cq); + SET_DEVICE_OP(dev_ops, destroy_comp_cntr); SET_DEVICE_OP(dev_ops, destroy_flow); SET_DEVICE_OP(dev_ops, destroy_flow_action); SET_DEVICE_OP(dev_ops, destroy_qp); @@ -2813,6 +2815,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, modify_hw_stat); SET_DEVICE_OP(dev_ops, modify_port); SET_DEVICE_OP(dev_ops, modify_qp); + SET_DEVICE_OP(dev_ops, qp_attach_comp_cntr); SET_DEVICE_OP(dev_ops, modify_srq); SET_DEVICE_OP(dev_ops, modify_wq); SET_DEVICE_OP(dev_ops, peek_cq); @@ -2836,12 +2839,14 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, query_ucontext); SET_DEVICE_OP(dev_ops, rdma_netdev_get_params); SET_DEVICE_OP(dev_ops, read_counters); + SET_DEVICE_OP(dev_ops, read_comp_cntr); SET_DEVICE_OP(dev_ops, reg_dm_mr); SET_DEVICE_OP(dev_ops, reg_user_mr); SET_DEVICE_OP(dev_ops, reg_user_mr_dmabuf); SET_DEVICE_OP(dev_ops, req_notify_cq); SET_DEVICE_OP(dev_ops, rereg_user_mr); SET_DEVICE_OP(dev_ops, resize_user_cq); + SET_DEVICE_OP(dev_ops, modify_comp_cntr); SET_DEVICE_OP(dev_ops, set_vf_guid); SET_DEVICE_OP(dev_ops, set_vf_link_state); SET_DEVICE_OP(dev_ops, ufile_hw_cleanup); @@ -2850,6 +2855,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_OBJ_SIZE(dev_ops, ib_ah); SET_OBJ_SIZE(dev_ops, ib_counters); SET_OBJ_SIZE(dev_ops, ib_cq); + SET_OBJ_SIZE(dev_ops, ib_comp_cntr); SET_OBJ_SIZE(dev_ops, ib_dmah); SET_OBJ_SIZE(dev_ops, ib_mw); SET_OBJ_SIZE(dev_ops, ib_pd); diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h index 56121103e9f4..2b91e8527287 100644 --- a/drivers/infiniband/core/rdma_core.h +++ b/drivers/infiniband/core/rdma_core.h @@ -159,6 +159,7 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile); extern const struct uapi_definition uverbs_def_obj_async_fd[]; extern const struct uapi_definition uverbs_def_obj_counters[]; +extern const struct uapi_definition uverbs_def_obj_comp_cntr[]; extern const struct uapi_definition uverbs_def_obj_cq[]; extern const struct uapi_definition uverbs_def_obj_device[]; extern const struct uapi_definition uverbs_def_obj_dm[]; diff --git a/drivers/infiniband/core/uverbs_std_types_comp_cntr.c b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c new file mode 100644 index 000000000000..7db3feace2a9 --- /dev/null +++ b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. + */ + +#include +#include "rdma_core.h" +#include "uverbs.h" + +static int uverbs_free_comp_cntr(struct ib_uobject *uobject, enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct ib_comp_cntr *cc = uobject->object; + int ret; + + ret = cc->device->ops.destroy_comp_cntr(cc); + if (ret) + return ret; + + kfree(cc); + return 0; +} + +static int UVERBS_HANDLER(UVERBS_METHOD_COMP_CNTR_CREATE)(struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, + UVERBS_ATTR_CREATE_COMP_CNTR_HANDLE); + struct ib_device *ib_dev = attrs->context->device; + struct ib_comp_cntr *cc; + int ret; + + if (!ib_dev->ops.create_comp_cntr || + !ib_dev->ops.destroy_comp_cntr || + !ib_dev->ops.qp_attach_comp_cntr) + return -EOPNOTSUPP; + + cc = rdma_zalloc_drv_obj(ib_dev, ib_comp_cntr); + if (!cc) + return -ENOMEM; + + cc->device = ib_dev; + cc->uobject = uobj; + + ret = ib_dev->ops.create_comp_cntr(cc, attrs); + if (ret) + goto err_free; + + uobj->object = cc; + uverbs_finalize_uobj_create(attrs, UVERBS_ATTR_CREATE_COMP_CNTR_HANDLE); + return 0; + +err_free: + kfree(cc); + return ret; +} + +static int UVERBS_HANDLER(UVERBS_METHOD_COMP_CNTR_MODIFY)(struct uverbs_attr_bundle *attrs) +{ + struct ib_comp_cntr *cc = uverbs_attr_get_obj(attrs, UVERBS_ATTR_MODIFY_COMP_CNTR_HANDLE); + enum ib_comp_cntr_modify_op op; + enum ib_comp_cntr_entry entry; + u64 value; + int ret; + + if (!cc->device->ops.modify_comp_cntr) + return -EOPNOTSUPP; + + ret = uverbs_get_const(&entry, attrs, UVERBS_ATTR_MODIFY_COMP_CNTR_ENTRY); + if (ret) + return ret; + + ret = uverbs_get_const(&op, attrs, UVERBS_ATTR_MODIFY_COMP_CNTR_OP); + if (ret) + return ret; + + ret = uverbs_copy_from(&value, attrs, UVERBS_ATTR_MODIFY_COMP_CNTR_VALUE); + if (ret) + return ret; + + return cc->device->ops.modify_comp_cntr(cc, entry, op, value); +} + +static int UVERBS_HANDLER(UVERBS_METHOD_COMP_CNTR_READ)(struct uverbs_attr_bundle *attrs) +{ + struct ib_comp_cntr *cc = uverbs_attr_get_obj(attrs, UVERBS_ATTR_READ_COMP_CNTR_HANDLE); + enum ib_comp_cntr_entry entry; + u64 value = 0; + int ret; + + if (!cc->device->ops.read_comp_cntr) + return -EOPNOTSUPP; + + ret = uverbs_get_const(&entry, attrs, UVERBS_ATTR_READ_COMP_CNTR_ENTRY); + if (ret) + return ret; + + ret = cc->device->ops.read_comp_cntr(cc, entry, &value); + if (ret) + return ret; + + return uverbs_copy_to(attrs, UVERBS_ATTR_READ_COMP_CNTR_RESP_VALUE, &value, sizeof(value)); +} + +DECLARE_UVERBS_NAMED_METHOD( + UVERBS_METHOD_COMP_CNTR_CREATE, + UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_COMP_CNTR_HANDLE, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_ACCESS_NEW, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY( + UVERBS_METHOD_COMP_CNTR_DESTROY, + UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_COMP_CNTR_HANDLE, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD( + UVERBS_METHOD_COMP_CNTR_MODIFY, + UVERBS_ATTR_IDR(UVERBS_ATTR_MODIFY_COMP_CNTR_HANDLE, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_ACCESS_WRITE, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(UVERBS_ATTR_MODIFY_COMP_CNTR_ENTRY, + enum ib_uverbs_comp_cntr_entry, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(UVERBS_ATTR_MODIFY_COMP_CNTR_OP, + enum ib_uverbs_comp_cntr_modify_op, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(UVERBS_ATTR_MODIFY_COMP_CNTR_VALUE, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD( + UVERBS_METHOD_COMP_CNTR_READ, + UVERBS_ATTR_IDR(UVERBS_ATTR_READ_COMP_CNTR_HANDLE, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_ACCESS_READ, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(UVERBS_ATTR_READ_COMP_CNTR_ENTRY, + enum ib_uverbs_comp_cntr_entry, + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_READ_COMP_CNTR_RESP_VALUE, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT( + UVERBS_OBJECT_COMP_CNTR, + UVERBS_TYPE_ALLOC_IDR(uverbs_free_comp_cntr), + &UVERBS_METHOD(UVERBS_METHOD_COMP_CNTR_CREATE), + &UVERBS_METHOD(UVERBS_METHOD_COMP_CNTR_DESTROY), + &UVERBS_METHOD(UVERBS_METHOD_COMP_CNTR_MODIFY), + &UVERBS_METHOD(UVERBS_METHOD_COMP_CNTR_READ)); + +const struct uapi_definition uverbs_def_obj_comp_cntr[] = { + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_COMP_CNTR, + UAPI_DEF_OBJ_NEEDS_FN(destroy_comp_cntr)), + {} +}; diff --git a/drivers/infiniband/core/uverbs_std_types_qp.c b/drivers/infiniband/core/uverbs_std_types_qp.c index 5767607dd420..1a32a902bdba 100644 --- a/drivers/infiniband/core/uverbs_std_types_qp.c +++ b/drivers/infiniband/core/uverbs_std_types_qp.c @@ -372,11 +372,60 @@ DECLARE_UVERBS_NAMED_METHOD( UVERBS_ATTR_TYPE(struct ib_uverbs_destroy_qp_resp), UA_MANDATORY)); +static int UVERBS_HANDLER(UVERBS_METHOD_QP_ATTACH_COMP_CNTR)( + struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *qp_uobj = uverbs_attr_get_uobject( + attrs, UVERBS_ATTR_QP_ATTACH_COMP_CNTR_HANDLE); + struct ib_comp_cntr *cc = uverbs_attr_get_obj( + attrs, UVERBS_ATTR_QP_ATTACH_COMP_CNTR_CNTR_HANDLE); + struct ib_qp_attach_comp_cntr_attr attr = {}; + struct ib_qp *qp = qp_uobj->object; + int ret; + + if (!cc->device->ops.qp_attach_comp_cntr) + return -EOPNOTSUPP; + + if (qp->real_qp != qp) + return -EINVAL; + + ret = uverbs_get_flags32(&attr.op_mask, attrs, + UVERBS_ATTR_QP_ATTACH_COMP_CNTR_OP_MASK, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_SEND | + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RECV | + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_READ | + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ | + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE | + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE); + if (ret) + return ret; + + if (!attr.op_mask) + return -EINVAL; + + return qp->device->ops.qp_attach_comp_cntr(qp, cc, &attr); +} + +DECLARE_UVERBS_NAMED_METHOD( + UVERBS_METHOD_QP_ATTACH_COMP_CNTR, + UVERBS_ATTR_IDR(UVERBS_ATTR_QP_ATTACH_COMP_CNTR_HANDLE, + UVERBS_OBJECT_QP, + UVERBS_ACCESS_WRITE, + UA_MANDATORY), + UVERBS_ATTR_IDR(UVERBS_ATTR_QP_ATTACH_COMP_CNTR_CNTR_HANDLE, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_ACCESS_READ, + UA_MANDATORY), + UVERBS_ATTR_FLAGS_IN(UVERBS_ATTR_QP_ATTACH_COMP_CNTR_OP_MASK, + enum ib_uverbs_qp_attach_comp_cntr_op, + UA_MANDATORY)); + DECLARE_UVERBS_NAMED_OBJECT( UVERBS_OBJECT_QP, UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), uverbs_free_qp), &UVERBS_METHOD(UVERBS_METHOD_QP_CREATE), - &UVERBS_METHOD(UVERBS_METHOD_QP_DESTROY)); + &UVERBS_METHOD(UVERBS_METHOD_QP_DESTROY), + &UVERBS_METHOD(UVERBS_METHOD_QP_ATTACH_COMP_CNTR)); const struct uapi_definition uverbs_def_obj_qp[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_QP, diff --git a/drivers/infiniband/core/uverbs_uapi.c b/drivers/infiniband/core/uverbs_uapi.c index 4e2e556c8119..d150099b99d2 100644 --- a/drivers/infiniband/core/uverbs_uapi.c +++ b/drivers/infiniband/core/uverbs_uapi.c @@ -628,6 +628,7 @@ void uverbs_destroy_api(struct uverbs_api *uapi) static const struct uapi_definition uverbs_core_api[] = { UAPI_DEF_CHAIN(uverbs_def_obj_async_fd), UAPI_DEF_CHAIN(uverbs_def_obj_counters), + UAPI_DEF_CHAIN(uverbs_def_obj_comp_cntr), UAPI_DEF_CHAIN(uverbs_def_obj_cq), UAPI_DEF_CHAIN(uverbs_def_obj_device), UAPI_DEF_CHAIN(uverbs_def_obj_dm), diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 8d82d303b723..9a952750f068 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1746,6 +1746,34 @@ struct ib_cq { struct rdma_restrack_entry res; }; +enum ib_qp_attach_comp_cntr_op { + IB_QP_ATTACH_COMP_CNTR_OP_SEND = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_SEND, + IB_QP_ATTACH_COMP_CNTR_OP_RECV = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RECV, + IB_QP_ATTACH_COMP_CNTR_OP_RDMA_READ = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_READ, + IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ, + IB_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE, + IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE, +}; + +struct ib_comp_cntr { + struct ib_device *device; + struct ib_uobject *uobject; +}; + +enum ib_comp_cntr_entry { + IB_COMP_CNTR_ENTRY_COMP = IB_UVERBS_COMP_CNTR_ENTRY_COMP, + IB_COMP_CNTR_ENTRY_ERR = IB_UVERBS_COMP_CNTR_ENTRY_ERR, +}; + +enum ib_comp_cntr_modify_op { + IB_COMP_CNTR_MODIFY_OP_SET = IB_UVERBS_COMP_CNTR_MODIFY_OP_SET, + IB_COMP_CNTR_MODIFY_OP_INC = IB_UVERBS_COMP_CNTR_MODIFY_OP_INC, +}; + +struct ib_qp_attach_comp_cntr_attr { + u32 op_mask; /* Bitmask of enum ib_qp_attach_comp_cntr_op */ +}; + struct ib_srq { struct ib_device *device; struct ib_pd *pd; @@ -2629,6 +2657,8 @@ struct ib_device_ops { struct ib_udata *udata); int (*modify_qp)(struct ib_qp *qp, struct ib_qp_attr *qp_attr, int qp_attr_mask, struct ib_udata *udata); + int (*qp_attach_comp_cntr)(struct ib_qp *qp, struct ib_comp_cntr *cc, + struct ib_qp_attach_comp_cntr_attr *attr); int (*query_qp)(struct ib_qp *qp, struct ib_qp_attr *qp_attr, int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr); int (*destroy_qp)(struct ib_qp *qp, struct ib_udata *udata); @@ -2650,6 +2680,12 @@ struct ib_device_ops { * post_destroy_cq - Free all kernel resources */ void (*post_destroy_cq)(struct ib_cq *cq); + int (*create_comp_cntr)(struct ib_comp_cntr *cc, + struct uverbs_attr_bundle *attrs); + int (*destroy_comp_cntr)(struct ib_comp_cntr *cc); + int (*modify_comp_cntr)(struct ib_comp_cntr *cc, enum ib_comp_cntr_entry entry, + enum ib_comp_cntr_modify_op op, u64 value); + int (*read_comp_cntr)(struct ib_comp_cntr *cc, enum ib_comp_cntr_entry entry, u64 *value); struct ib_mr *(*get_dma_mr)(struct ib_pd *pd, int mr_access_flags); struct ib_mr *(*reg_user_mr)(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int mr_access_flags, @@ -2883,6 +2919,7 @@ struct ib_device_ops { DECLARE_RDMA_OBJ_SIZE(ib_ah); DECLARE_RDMA_OBJ_SIZE(ib_counters); DECLARE_RDMA_OBJ_SIZE(ib_cq); + DECLARE_RDMA_OBJ_SIZE(ib_comp_cntr); DECLARE_RDMA_OBJ_SIZE(ib_dmah); DECLARE_RDMA_OBJ_SIZE(ib_mw); DECLARE_RDMA_OBJ_SIZE(ib_pd); diff --git a/include/uapi/rdma/ib_user_ioctl_cmds.h b/include/uapi/rdma/ib_user_ioctl_cmds.h index 839835bd4b23..19a94b91987e 100644 --- a/include/uapi/rdma/ib_user_ioctl_cmds.h +++ b/include/uapi/rdma/ib_user_ioctl_cmds.h @@ -57,6 +57,7 @@ enum uverbs_default_objects { UVERBS_OBJECT_ASYNC_EVENT, UVERBS_OBJECT_DMAH, UVERBS_OBJECT_DMABUF, + UVERBS_OBJECT_COMP_CNTR, }; enum { @@ -169,9 +170,16 @@ enum uverbs_attrs_destroy_qp_cmd_attr_ids { UVERBS_ATTR_DESTROY_QP_RESP, }; +enum uverbs_attrs_qp_attach_comp_cntr_cmd_attr_ids { + UVERBS_ATTR_QP_ATTACH_COMP_CNTR_HANDLE, + UVERBS_ATTR_QP_ATTACH_COMP_CNTR_CNTR_HANDLE, + UVERBS_ATTR_QP_ATTACH_COMP_CNTR_OP_MASK, +}; + enum uverbs_methods_qp { UVERBS_METHOD_QP_CREATE, UVERBS_METHOD_QP_DESTROY, + UVERBS_METHOD_QP_ATTACH_COMP_CNTR, }; enum uverbs_attrs_create_srq_cmd_attr_ids { @@ -438,4 +446,32 @@ enum uverbs_attrs_query_gid_entry_cmd_attr_ids { UVERBS_ATTR_QUERY_GID_ENTRY_RESP_ENTRY, }; +enum uverbs_methods_comp_cntr { + UVERBS_METHOD_COMP_CNTR_CREATE, + UVERBS_METHOD_COMP_CNTR_DESTROY, + UVERBS_METHOD_COMP_CNTR_MODIFY, + UVERBS_METHOD_COMP_CNTR_READ, +}; + +enum uverbs_attrs_create_comp_cntr_cmd_attr_ids { + UVERBS_ATTR_CREATE_COMP_CNTR_HANDLE, +}; + +enum uverbs_attrs_destroy_comp_cntr_cmd_attr_ids { + UVERBS_ATTR_DESTROY_COMP_CNTR_HANDLE, +}; + +enum uverbs_attrs_modify_comp_cntr_cmd_attr_ids { + UVERBS_ATTR_MODIFY_COMP_CNTR_HANDLE, + UVERBS_ATTR_MODIFY_COMP_CNTR_ENTRY, + UVERBS_ATTR_MODIFY_COMP_CNTR_OP, + UVERBS_ATTR_MODIFY_COMP_CNTR_VALUE, +}; + +enum uverbs_attrs_read_comp_cntr_cmd_attr_ids { + UVERBS_ATTR_READ_COMP_CNTR_HANDLE, + UVERBS_ATTR_READ_COMP_CNTR_ENTRY, + UVERBS_ATTR_READ_COMP_CNTR_RESP_VALUE, +}; + #endif diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h index 51030c27d479..21f86cc7bb1f 100644 --- a/include/uapi/rdma/ib_user_ioctl_verbs.h +++ b/include/uapi/rdma/ib_user_ioctl_verbs.h @@ -300,4 +300,23 @@ struct ib_uverbs_buffer_desc { __aligned_u64 length; }; +enum ib_uverbs_comp_cntr_entry { + IB_UVERBS_COMP_CNTR_ENTRY_COMP, + IB_UVERBS_COMP_CNTR_ENTRY_ERR, +}; + +enum ib_uverbs_comp_cntr_modify_op { + IB_UVERBS_COMP_CNTR_MODIFY_OP_SET, + IB_UVERBS_COMP_CNTR_MODIFY_OP_INC, +}; + +enum ib_uverbs_qp_attach_comp_cntr_op { + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_SEND = 1 << 0, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RECV = 1 << 1, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_READ = 1 << 2, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ = 1 << 3, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE = 1 << 4, + IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE = 1 << 5, +}; + #endif From 45e537bf580d540132bcabd8ae3af483461fa1c1 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:35:59 +0000 Subject: [PATCH 089/160] RDMA/core: Prevent destroying in-use completion counters Reject comp_cntr destroy while it is attached to any QP. Track attachments using an xarray in ib_qp keyed by the attach op_mask. Use op bitmask to reject overlapping attaches early. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-3-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- .../core/uverbs_std_types_comp_cntr.c | 3 +++ drivers/infiniband/core/uverbs_std_types_qp.c | 18 +++++++++++++++++- drivers/infiniband/core/verbs.c | 8 ++++++++ include/rdma/ib_verbs.h | 3 +++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/uverbs_std_types_comp_cntr.c b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c index 7db3feace2a9..e12aececbb09 100644 --- a/drivers/infiniband/core/uverbs_std_types_comp_cntr.c +++ b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c @@ -13,6 +13,9 @@ static int uverbs_free_comp_cntr(struct ib_uobject *uobject, enum rdma_remove_re struct ib_comp_cntr *cc = uobject->object; int ret; + if (atomic_read(&cc->usecnt)) + return -EBUSY; + ret = cc->device->ops.destroy_comp_cntr(cc); if (ret) return ret; diff --git a/drivers/infiniband/core/uverbs_std_types_qp.c b/drivers/infiniband/core/uverbs_std_types_qp.c index 1a32a902bdba..30fc20fb251f 100644 --- a/drivers/infiniband/core/uverbs_std_types_qp.c +++ b/drivers/infiniband/core/uverbs_std_types_qp.c @@ -403,7 +403,23 @@ static int UVERBS_HANDLER(UVERBS_METHOD_QP_ATTACH_COMP_CNTR)( if (!attr.op_mask) return -EINVAL; - return qp->device->ops.qp_attach_comp_cntr(qp, cc, &attr); + if (attr.op_mask & qp->comp_cntr_op_mask) + return -EBUSY; + + ret = xa_err(xa_store(&qp->comp_cntrs, attr.op_mask, cc, GFP_KERNEL)); + if (ret) + return ret; + + ret = qp->device->ops.qp_attach_comp_cntr(qp, cc, &attr); + if (ret) { + xa_erase(&qp->comp_cntrs, attr.op_mask); + return ret; + } + + atomic_inc(&cc->usecnt); + qp->comp_cntr_op_mask |= attr.op_mask; + + return 0; } DECLARE_UVERBS_NAMED_METHOD( diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index f86e6f30b1df..367822efff36 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1300,6 +1300,7 @@ static struct ib_qp *create_qp(struct ib_device *dev, struct ib_pd *pd, qp->qp_context = attr->qp_context; spin_lock_init(&qp->mr_lock); + xa_init(&qp->comp_cntrs); INIT_LIST_HEAD(&qp->rdma_mrs); INIT_LIST_HEAD(&qp->sig_mrs); init_completion(&qp->srq_completion); @@ -1334,6 +1335,7 @@ static struct ib_qp *create_qp(struct ib_device *dev, struct ib_pd *pd, qp, uattrs ? uverbs_get_cleared_udata(uattrs) : NULL); err_create: rdma_restrack_put(&qp->res); + xa_destroy(&qp->comp_cntrs); kfree(qp); return ERR_PTR(ret); @@ -2151,6 +2153,8 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata) const struct ib_gid_attr *alt_path_sgid_attr = qp->alt_path_sgid_attr; const struct ib_gid_attr *av_sgid_attr = qp->av_sgid_attr; struct ib_qp_security *sec; + struct ib_comp_cntr *cc; + unsigned long index; int ret; WARN_ON_ONCE(qp->mrs_used > 0); @@ -2184,6 +2188,10 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata) if (av_sgid_attr) rdma_put_gid_attr(av_sgid_attr); + xa_for_each(&qp->comp_cntrs, index, cc) + atomic_dec(&cc->usecnt); + xa_destroy(&qp->comp_cntrs); + ib_qp_usecnt_dec(qp); if (sec) ib_destroy_qp_security_end(sec); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 9a952750f068..84a8904fbad4 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1758,6 +1758,7 @@ enum ib_qp_attach_comp_cntr_op { struct ib_comp_cntr { struct ib_device *device; struct ib_uobject *uobject; + atomic_t usecnt; }; enum ib_comp_cntr_entry { @@ -1944,6 +1945,8 @@ struct ib_qp { struct completion srq_completion; struct ib_xrcd *xrcd; /* XRC TGT QPs only */ struct list_head xrcd_list; + struct xarray comp_cntrs; /* op_mask -> comp_cntr */ + u32 comp_cntr_op_mask; /* count times opened, mcast attaches, flow attaches */ atomic_t usecnt; From c1c13e596f65c2b2744c6d4f1471d7b7058195d3 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:36:00 +0000 Subject: [PATCH 090/160] RDMA/core: Expose Completion Counter capabilities to userspace Add a dedicated query interface for completion counter capabilities via UVERBS_METHOD_QUERY_COMP_CNTR_CAPS on the device object. The query returns the maximum number of counters, maximum counter value, and a bitmask of supported QP attach operations. Each field is an optional ioctl attribute, allowing userspace to request only the capabilities it needs. Drivers implement the query_comp_cntr_caps operation to report device-specific capabilities. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-4-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 1 + .../infiniband/core/uverbs_std_types_device.c | 51 ++++++++++++++++++- include/rdma/ib_verbs.h | 9 ++++ include/uapi/rdma/ib_user_ioctl_cmds.h | 7 +++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index a4c57279f19d..ddf75867ac61 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2829,6 +2829,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, post_srq_recv); SET_DEVICE_OP(dev_ops, process_mad); SET_DEVICE_OP(dev_ops, query_ah); + SET_DEVICE_OP(dev_ops, query_comp_cntr_caps); SET_DEVICE_OP(dev_ops, query_device); SET_DEVICE_OP(dev_ops, query_gid); SET_DEVICE_OP(dev_ops, query_pkey); diff --git a/drivers/infiniband/core/uverbs_std_types_device.c b/drivers/infiniband/core/uverbs_std_types_device.c index 12ca15739cd2..ce0a7de00405 100644 --- a/drivers/infiniband/core/uverbs_std_types_device.c +++ b/drivers/infiniband/core/uverbs_std_types_device.c @@ -472,6 +472,42 @@ static int UVERBS_HANDLER(UVERBS_METHOD_QUERY_GID_ENTRY)( return ret; } +static int UVERBS_HANDLER(UVERBS_METHOD_QUERY_COMP_CNTR_CAPS)( + struct uverbs_attr_bundle *attrs) +{ + struct ib_comp_cntr_caps caps = {}; + struct ib_ucontext *ucontext; + struct ib_device *ib_dev; + int ret; + + ucontext = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ucontext)) + return PTR_ERR(ucontext); + ib_dev = ucontext->device; + + if (!ib_dev->ops.query_comp_cntr_caps) + return -EOPNOTSUPP; + + ret = ib_dev->ops.query_comp_cntr_caps(ib_dev, &caps, attrs); + if (ret) + return ret; + + ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_COUNTERS, + &caps.max_counters, sizeof(caps.max_counters)); + if (IS_UVERBS_COPY_ERR(ret)) + return ret; + + ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_VALUE, + &caps.max_value, sizeof(caps.max_value)); + if (IS_UVERBS_COPY_ERR(ret)) + return ret; + + ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_SUPPORTED_QP_ATTACH_OPS, + &caps.supported_qp_attach_ops, + sizeof(caps.supported_qp_attach_ops)); + return IS_UVERBS_COPY_ERR(ret) ? ret : 0; +} + DECLARE_UVERBS_NAMED_METHOD( UVERBS_METHOD_GET_CONTEXT, UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_GET_CONTEXT_NUM_COMP_VECTORS, @@ -542,6 +578,18 @@ DECLARE_UVERBS_NAMED_METHOD( netdev_ifindex), UA_MANDATORY)); +DECLARE_UVERBS_NAMED_METHOD( + UVERBS_METHOD_QUERY_COMP_CNTR_CAPS, + UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_COUNTERS, + UVERBS_ATTR_TYPE(u32), + UA_OPTIONAL), + UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_VALUE, + UVERBS_ATTR_TYPE(u64), + UA_OPTIONAL), + UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_SUPPORTED_QP_ATTACH_OPS, + UVERBS_ATTR_TYPE(u32), + UA_OPTIONAL)); + DECLARE_UVERBS_GLOBAL_METHODS(UVERBS_OBJECT_DEVICE, &UVERBS_METHOD(UVERBS_METHOD_GET_CONTEXT), &UVERBS_METHOD(UVERBS_METHOD_INVOKE_WRITE), @@ -550,7 +598,8 @@ DECLARE_UVERBS_GLOBAL_METHODS(UVERBS_OBJECT_DEVICE, &UVERBS_METHOD(UVERBS_METHOD_QUERY_PORT_SPEED), &UVERBS_METHOD(UVERBS_METHOD_QUERY_CONTEXT), &UVERBS_METHOD(UVERBS_METHOD_QUERY_GID_TABLE), - &UVERBS_METHOD(UVERBS_METHOD_QUERY_GID_ENTRY)); + &UVERBS_METHOD(UVERBS_METHOD_QUERY_GID_ENTRY), + &UVERBS_METHOD(UVERBS_METHOD_QUERY_COMP_CNTR_CAPS)); const struct uapi_definition uverbs_def_obj_device[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_DEVICE), diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 84a8904fbad4..033f4a2c965f 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1755,6 +1755,12 @@ enum ib_qp_attach_comp_cntr_op { IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE = IB_UVERBS_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE, }; +struct ib_comp_cntr_caps { + u64 max_value; + u32 max_counters; + u32 supported_qp_attach_ops; /* Bitmask of enum ib_qp_attach_comp_cntr_op */ +}; + struct ib_comp_cntr { struct ib_device *device; struct ib_uobject *uobject; @@ -2689,6 +2695,9 @@ struct ib_device_ops { int (*modify_comp_cntr)(struct ib_comp_cntr *cc, enum ib_comp_cntr_entry entry, enum ib_comp_cntr_modify_op op, u64 value); int (*read_comp_cntr)(struct ib_comp_cntr *cc, enum ib_comp_cntr_entry entry, u64 *value); + int (*query_comp_cntr_caps)(struct ib_device *dev, + struct ib_comp_cntr_caps *caps, + struct uverbs_attr_bundle *attrs); struct ib_mr *(*get_dma_mr)(struct ib_pd *pd, int mr_access_flags); struct ib_mr *(*reg_user_mr)(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int mr_access_flags, diff --git a/include/uapi/rdma/ib_user_ioctl_cmds.h b/include/uapi/rdma/ib_user_ioctl_cmds.h index 19a94b91987e..6a3d59d03f54 100644 --- a/include/uapi/rdma/ib_user_ioctl_cmds.h +++ b/include/uapi/rdma/ib_user_ioctl_cmds.h @@ -76,6 +76,7 @@ enum uverbs_methods_device { UVERBS_METHOD_QUERY_GID_TABLE, UVERBS_METHOD_QUERY_GID_ENTRY, UVERBS_METHOD_QUERY_PORT_SPEED, + UVERBS_METHOD_QUERY_COMP_CNTR_CAPS, }; enum uverbs_attrs_invoke_write_cmd_attr_ids { @@ -94,6 +95,12 @@ enum uverbs_attrs_query_port_speed_cmd_attr_ids { UVERBS_ATTR_QUERY_PORT_SPEED_RESP, }; +enum uverbs_attrs_query_comp_cntr_caps_attr_ids { + UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_COUNTERS, + UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_MAX_VALUE, + UVERBS_ATTR_QUERY_COMP_CNTR_CAPS_SUPPORTED_QP_ATTACH_OPS, +}; + enum uverbs_attrs_get_context_attr_ids { UVERBS_ATTR_GET_CONTEXT_NUM_COMP_VECTORS, UVERBS_ATTR_GET_CONTEXT_CORE_SUPPORT, From 0774c6dd209605e65dc18022af2493a3c1a91d68 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:36:01 +0000 Subject: [PATCH 091/160] RDMA/core: Add Completion Counters to resource tracking Track completion counter objects in the resource tracking database so they are visible through the rdma netlink interface. The rdma tool displays the comp_cntr count in the resource summary. Add RDMA_RESTRACK_COMP_CNTR type, embed rdma_restrack_entry in ib_comp_cntr, and add the res_to_dev mapping. Register the resource on create and remove it on destroy. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-5-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/nldev.c | 8 ++++++++ drivers/infiniband/core/restrack.c | 2 ++ drivers/infiniband/core/uverbs_std_types_comp_cntr.c | 12 +++++++++++- include/rdma/ib_verbs.h | 1 + include/rdma/restrack.h | 4 ++++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index b1a6c1670091..b4bae92c5c4e 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -451,8 +451,10 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, [RDMA_RESTRACK_MR] = "mr", [RDMA_RESTRACK_CTX] = "ctx", [RDMA_RESTRACK_SRQ] = "srq", + [RDMA_RESTRACK_COMP_CNTR] = "comp_cntr", }; + struct ib_comp_cntr_caps comp_cntr_caps = {}; struct nlattr *table_attr; u64 curr, max; int ret, i; @@ -464,6 +466,9 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, if (!table_attr) return -EMSGSIZE; + if (device->ops.query_comp_cntr_caps) + device->ops.query_comp_cntr_caps(device, &comp_cntr_caps, NULL); + for (i = 0; i < RDMA_RESTRACK_MAX; i++) { if (!names[i]) continue; @@ -484,6 +489,9 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device, case RDMA_RESTRACK_SRQ: max = device->attrs.max_srq; break; + case RDMA_RESTRACK_COMP_CNTR: + max = comp_cntr_caps.max_counters; + break; default: max = 0; } diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index ff228d6fb977..f89a81dad72f 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -104,6 +104,8 @@ static struct ib_device *res_to_dev(struct rdma_restrack_entry *res) return container_of(res, struct ib_srq, res)->device; case RDMA_RESTRACK_DMAH: return container_of(res, struct ib_dmah, res)->device; + case RDMA_RESTRACK_COMP_CNTR: + return container_of(res, struct ib_comp_cntr, res)->device; default: WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type); return NULL; diff --git a/drivers/infiniband/core/uverbs_std_types_comp_cntr.c b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c index e12aececbb09..2e7de84d94a6 100644 --- a/drivers/infiniband/core/uverbs_std_types_comp_cntr.c +++ b/drivers/infiniband/core/uverbs_std_types_comp_cntr.c @@ -6,6 +6,7 @@ #include #include "rdma_core.h" #include "uverbs.h" +#include "restrack.h" static int uverbs_free_comp_cntr(struct ib_uobject *uobject, enum rdma_remove_reason why, struct uverbs_attr_bundle *attrs) @@ -16,10 +17,14 @@ static int uverbs_free_comp_cntr(struct ib_uobject *uobject, enum rdma_remove_re if (atomic_read(&cc->usecnt)) return -EBUSY; + rdma_restrack_begin_del(&cc->res); ret = cc->device->ops.destroy_comp_cntr(cc); - if (ret) + if (ret) { + rdma_restrack_abort_del(&cc->res); return ret; + } + rdma_restrack_commit_del(&cc->res); kfree(cc); return 0; } @@ -44,15 +49,20 @@ static int UVERBS_HANDLER(UVERBS_METHOD_COMP_CNTR_CREATE)(struct uverbs_attr_bun cc->device = ib_dev; cc->uobject = uobj; + rdma_restrack_new(&cc->res, RDMA_RESTRACK_COMP_CNTR); + rdma_restrack_set_name(&cc->res, NULL); + ret = ib_dev->ops.create_comp_cntr(cc, attrs); if (ret) goto err_free; uobj->object = cc; + rdma_restrack_add(&cc->res); uverbs_finalize_uobj_create(attrs, UVERBS_ATTR_CREATE_COMP_CNTR_HANDLE); return 0; err_free: + rdma_restrack_put(&cc->res); kfree(cc); return ret; } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 033f4a2c965f..cb3b6163961b 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1765,6 +1765,7 @@ struct ib_comp_cntr { struct ib_device *device; struct ib_uobject *uobject; atomic_t usecnt; + struct rdma_restrack_entry res; }; enum ib_comp_cntr_entry { diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h index c081384740ce..47fec3083d28 100644 --- a/include/rdma/restrack.h +++ b/include/rdma/restrack.h @@ -60,6 +60,10 @@ enum rdma_restrack_type { * @RDMA_RESTRACK_DMAH: DMA handle */ RDMA_RESTRACK_DMAH, + /** + * @RDMA_RESTRACK_COMP_CNTR: Completion Counter + */ + RDMA_RESTRACK_COMP_CNTR, /** * @RDMA_RESTRACK_MAX: Last entry, used for array dclarations */ From 6eb179ba09441b4a90de8e2fba2e13c1326879e9 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:36:02 +0000 Subject: [PATCH 092/160] RDMA/efa: Update device interface Align device interface definitions. Reviewed-by: Daniel Kinsbursky Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-6-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- .../infiniband/hw/efa/efa_admin_cmds_defs.h | 186 +++++++++++++++++- drivers/infiniband/hw/efa/efa_io_defs.h | 20 +- 2 files changed, 202 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 3eb3a4de8912..fc07213e26af 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -28,7 +28,13 @@ enum efa_admin_aq_opcode { EFA_ADMIN_CREATE_EQ = 18, EFA_ADMIN_DESTROY_EQ = 19, EFA_ADMIN_ALLOC_MR = 20, - EFA_ADMIN_MAX_OPCODE = 20, + EFA_ADMIN_SERVICE = 21, + EFA_ADMIN_CREATE_EVENT_COUNTER = 25, + EFA_ADMIN_DESTROY_EVENT_COUNTER = 26, + EFA_ADMIN_ATTACH_EVENT_COUNTER = 27, + EFA_ADMIN_MODIFY_EVENT_COUNTER = 28, + EFA_ADMIN_DETACH_EVENT_COUNTER = 29, + EFA_ADMIN_MAX_OPCODE = 29, }; enum efa_admin_aq_feature_id { @@ -722,7 +728,9 @@ struct efa_admin_feature_device_attr_desc { * on TX queues * 4 : unsolicited_write_recv - If set, unsolicited * write with imm. receive is supported - * 31:5 : reserved - MBZ + * 5 : event_counters - If set, event counters are + * supported + * 31:6 : reserved - MBZ */ u32 device_caps; @@ -811,6 +819,34 @@ struct efa_admin_feature_queue_attr_desc_1 { struct efa_admin_feature_queue_attr_desc_2 { /* Maximum size of data that can be sent inline in a Send WQE */ u16 inline_buf_size_ex; + + /* MBZ */ + u8 reserved[6]; + + /* + * Supported counter QP events + * 0 : send_comp + * 1 : send_comp_err + * 2 : recv_comp + * 3 : recv_comp_err + * 4 : read_comp + * 5 : read_comp_err + * 6 : write_comp + * 7 : write_comp_err + * 8 : remote_read_comp + * 9 : remote_write_comp + * 31:10 : reserved - MBZ + */ + u32 supported_event_counter_qp_events; + + /* Maximum number of counters */ + u32 max_event_counters; + + /* + * Maximum counter value, counter wraps around to 0 after reaching + * this value + */ + u64 event_counter_max_val; }; struct efa_admin_event_queue_attr_desc { @@ -1089,6 +1125,127 @@ struct efa_admin_host_info { u32 flags; }; +struct efa_admin_service_cmd { + struct efa_admin_aq_common_desc aq_common_descriptor; + + u8 buffer[60]; +}; + +struct efa_admin_service_resp { + struct efa_admin_acq_common_desc acq_common_desc; + + u8 buffer[56]; +}; + +/* Create Counter command */ +struct efa_admin_create_event_counter_cmd { + struct efa_admin_aq_common_desc aq_common_descriptor; + + /* UAR number */ + u16 uar; + + /* MBZ */ + u16 reserved; + + /* Counter physical address */ + u64 paddr; +}; + +struct efa_admin_create_event_counter_resp { + struct efa_admin_acq_common_desc acq_common_desc; + + /* Counter handle */ + u32 cntr_handle; + + /* MBZ */ + u32 reserved; +}; + +struct efa_admin_destroy_event_counter_cmd { + struct efa_admin_aq_common_desc aq_common_descriptor; + + /* Counter handle */ + u32 cntr_handle; +}; + +struct efa_admin_destroy_event_counter_resp { + struct efa_admin_acq_common_desc acq_common_desc; +}; + +enum efa_admin_event_counter_attach_type { + EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS = 0, +}; + +struct efa_admin_event_counter_attach_qp_events { + /* QP handle */ + u32 qp_handle; + + /* + * Bitmask of counter QP events + * 0 : send_comp + * 1 : send_comp_err + * 2 : recv_comp + * 3 : recv_comp_err + * 4 : read_comp + * 5 : read_comp_err + * 6 : write_comp + * 7 : write_comp_err + * 8 : remote_read_comp + * 9 : remote_write_comp + * 31:10 : reserved - MBZ + */ + u32 events; +}; + +struct efa_admin_attach_detach_event_counter_cmd { + struct efa_admin_aq_common_desc aq_common_descriptor; + + /* Counter handle */ + u32 cntr_handle; + + /* efa_admin_event_counter_attach_type */ + u8 attach_type; + + /* MBZ */ + u8 reserved[3]; + + union { + struct efa_admin_event_counter_attach_qp_events qp_events; + } u; +}; + +struct efa_admin_attach_detach_event_counter_resp { + struct efa_admin_acq_common_desc acq_common_desc; +}; + +/* Counter modify operations */ +enum efa_admin_event_counter_modify_ops { + /* Set counter value */ + EFA_ADMIN_EVENT_COUNTER_MODIFY_SET = 0, + /* Add to counter value */ + EFA_ADMIN_EVENT_COUNTER_MODIFY_ADD = 1, +}; + +struct efa_admin_modify_event_counter_cmd { + struct efa_admin_aq_common_desc aq_common_descriptor; + + /* Counter handle */ + u32 cntr_handle; + + /* Counter operation type (efa_admin_event_counter_modify_ops) */ + u8 operation; + + /* MBZ */ + u8 reserved[7]; + + /* Value for SET or ADD */ + u64 value; +}; + +struct efa_admin_modify_event_counter_resp { + struct efa_admin_acq_common_desc acq_common_desc; +}; + /* create_qp_cmd */ #define EFA_ADMIN_CREATE_QP_CMD_SQ_VIRT_MASK BIT(0) #define EFA_ADMIN_CREATE_QP_CMD_RQ_VIRT_MASK BIT(1) @@ -1129,6 +1286,19 @@ struct efa_admin_host_info { #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_DATA_POLLING_128_MASK BIT(2) #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_RDMA_WRITE_MASK BIT(3) #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_UNSOLICITED_WRITE_RECV_MASK BIT(4) +#define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_EVENT_COUNTERS_MASK BIT(5) + +/* feature_queue_attr_desc_2 */ +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_SEND_COMP_MASK BIT(0) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_SEND_COMP_ERR_MASK BIT(1) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_RECV_COMP_MASK BIT(2) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_RECV_COMP_ERR_MASK BIT(3) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_READ_COMP_MASK BIT(4) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_READ_COMP_ERR_MASK BIT(5) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_WRITE_COMP_MASK BIT(6) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_WRITE_COMP_ERR_MASK BIT(7) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_REMOTE_READ_COMP_MASK BIT(8) +#define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_REMOTE_WRITE_COMP_MASK BIT(9) /* create_eq_cmd */ #define EFA_ADMIN_CREATE_EQ_CMD_ENTRY_SIZE_WORDS_MASK GENMASK(4, 0) @@ -1147,4 +1317,16 @@ struct efa_admin_host_info { #define EFA_ADMIN_HOST_INFO_INTREE_MASK BIT(0) #define EFA_ADMIN_HOST_INFO_GDR_MASK BIT(1) +/* counter_attach_qp_events */ +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP_MASK BIT(0) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP_ERR_MASK BIT(1) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP_MASK BIT(2) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP_ERR_MASK BIT(3) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP_MASK BIT(4) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP_ERR_MASK BIT(5) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP_MASK BIT(6) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP_ERR_MASK BIT(7) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_READ_COMP_MASK BIT(8) +#define EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_WRITE_COMP_MASK BIT(9) + #endif /* _EFA_ADMIN_CMDS_H_ */ diff --git a/drivers/infiniband/hw/efa/efa_io_defs.h b/drivers/infiniband/hw/efa/efa_io_defs.h index a4c9fd33da38..a849d92a6eb6 100644 --- a/drivers/infiniband/hw/efa/efa_io_defs.h +++ b/drivers/infiniband/hw/efa/efa_io_defs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_IO_H_ @@ -65,6 +65,8 @@ enum efa_io_comp_status { EFA_IO_COMP_STATUS_REMOTE_ERROR_UNKNOWN_PEER = 14, /* Unreachable remote - never received a response */ EFA_IO_COMP_STATUS_LOCAL_ERROR_UNREACH_REMOTE = 15, + /* Remote feature mismatch */ + EFA_IO_COMP_STATUS_REMOTE_ERROR_FEATURE_MISMATCH = 18, }; enum efa_io_frwr_pbl_mode { @@ -72,6 +74,11 @@ enum efa_io_frwr_pbl_mode { EFA_IO_FRWR_DIRECT_PBL = 1, }; +enum efa_io_processing_hint { + /* Optimize for throughput */ + EFA_IO_PROCESSING_HINT_BURST_PPS_SENSITIVE = 1 << 0, +}; + struct efa_io_tx_meta_desc { /* Verbs-generated Request ID */ u16 req_id; @@ -121,7 +128,15 @@ struct efa_io_tx_meta_desc { u16 ah; - u16 reserved; + /* + * control flags + * 1:0 : processing_hints - Bitmask of enum + * efa_io_processing_hint + * 7:2 : reserved - MBZ + */ + u8 ctrl3; + + u8 reserved; /* Queue key */ u32 qkey; @@ -365,6 +380,7 @@ struct efa_io_rx_cdesc_ex { #define EFA_IO_TX_META_DESC_FIRST_MASK BIT(2) #define EFA_IO_TX_META_DESC_LAST_MASK BIT(3) #define EFA_IO_TX_META_DESC_COMP_REQ_MASK BIT(4) +#define EFA_IO_TX_META_DESC_PROCESSING_HINTS_MASK GENMASK(1, 0) /* tx_buf_desc */ #define EFA_IO_TX_BUF_DESC_LKEY_MASK GENMASK(23, 0) From 0e8e94c15091041ea8910cbfcade5a9c7cfe3f90 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Wed, 22 Jul 2026 08:36:03 +0000 Subject: [PATCH 093/160] RDMA/efa: Add Completion Counters support Implement completion counters for the EFA device. Each completion counter is backed by two EFA event counters, one for success completions and one for error completions. The driver creates umem for counters from private descriptor ioctl attributes using core utility. Read operations are not implemented as the counter values are accessed directly from userspace through the mapped memory. Reviewed-by: Yonatan Nachum Signed-off-by: Michael Margolin Link: https://patch.msgid.link/20260722083603.30334-7-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa.h | 20 +- drivers/infiniband/hw/efa/efa_com_cmd.c | 136 ++++++++++++++ drivers/infiniband/hw/efa/efa_com_cmd.h | 45 +++++ drivers/infiniband/hw/efa/efa_main.c | 8 +- drivers/infiniband/hw/efa/efa_verbs.c | 237 ++++++++++++++++++++++++ include/uapi/rdma/efa-abi.h | 6 + 6 files changed, 450 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa.h b/drivers/infiniband/hw/efa/efa.h index f4586bb170c1..6f6686eefb1a 100644 --- a/drivers/infiniband/hw/efa/efa.h +++ b/drivers/infiniband/hw/efa/efa.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_H_ @@ -110,6 +110,14 @@ struct efa_cq { struct ib_umem *umem; }; +struct efa_comp_cntr { + struct ib_comp_cntr ibcc; + struct ib_umem *comp_umem; + struct ib_umem *err_umem; + u32 comp_handle; + u32 err_handle; +}; + struct efa_qp { struct ib_qp ibqp; dma_addr_t rq_dma_addr; @@ -164,6 +172,16 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, int efa_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); int efa_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); +int efa_query_comp_cntr_caps(struct ib_device *ibdev, + struct ib_comp_cntr_caps *caps, + struct uverbs_attr_bundle *attrs); +int efa_create_comp_cntr(struct ib_comp_cntr *ibcc, + struct uverbs_attr_bundle *attrs); +int efa_destroy_comp_cntr(struct ib_comp_cntr *ibcc); +int efa_modify_comp_cntr(struct ib_comp_cntr *ibcc, enum ib_comp_cntr_entry entry, + enum ib_comp_cntr_modify_op op, u64 value); +int efa_qp_attach_comp_cntr(struct ib_qp *ibqp, struct ib_comp_cntr *ibcc, + struct ib_qp_attach_comp_cntr_attr *attr); struct ib_mr *efa_reg_mr(struct ib_pd *ibpd, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_dmah *dmah, diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 0b96862c2787..a9fd44b4debf 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -553,6 +553,10 @@ int efa_com_get_device_attr(struct efa_com_dev *edev, } result->inline_buf_size_ex = resp.u.queue_attr_2.inline_buf_size_ex; + result->max_event_counters = resp.u.queue_attr_2.max_event_counters; + result->event_counter_max_val = resp.u.queue_attr_2.event_counter_max_val; + result->supported_event_counter_qp_events = + resp.u.queue_attr_2.supported_event_counter_qp_events; } else { result->inline_buf_size_ex = result->inline_buf_size; } @@ -888,3 +892,135 @@ int efa_com_get_stats(struct efa_com_dev *edev, return 0; } + +int efa_com_create_event_counter(struct efa_com_dev *edev, + struct efa_com_create_event_counter_params *params, + struct efa_com_create_event_counter_result *result) +{ + struct efa_admin_create_event_counter_cmd cmd = {}; + struct efa_admin_create_event_counter_resp resp; + struct efa_com_admin_queue *aq = &edev->aq; + int err; + + cmd.aq_common_descriptor.opcode = EFA_ADMIN_CREATE_EVENT_COUNTER; + cmd.uar = params->uarn; + cmd.paddr = params->dma_addr; + + err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, + sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, + sizeof(resp)); + if (err) { + ibdev_err_ratelimited(edev->efa_dev, + "Failed to create event counter [%d]\n", + err); + return err; + } + + result->cntr_handle = resp.cntr_handle; + return 0; +} + +int efa_com_destroy_event_counter(struct efa_com_dev *edev, + struct efa_com_destroy_event_counter_params *params) +{ + struct efa_admin_destroy_event_counter_cmd cmd = {}; + struct efa_admin_destroy_event_counter_resp resp; + struct efa_com_admin_queue *aq = &edev->aq; + int err; + + cmd.aq_common_descriptor.opcode = EFA_ADMIN_DESTROY_EVENT_COUNTER; + cmd.cntr_handle = params->cntr_handle; + + err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, + sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, + sizeof(resp)); + if (err) { + ibdev_err_ratelimited(edev->efa_dev, + "Failed to destroy event counter [%d]\n", + err); + return err; + } + + return 0; +} + +static int efa_com_attach_detach_event_counter(struct efa_com_dev *edev, u8 opcode, + u32 cntr_handle, u32 qp_handle, + u32 events) +{ + struct efa_admin_attach_detach_event_counter_cmd cmd = {}; + struct efa_admin_attach_detach_event_counter_resp resp; + struct efa_com_admin_queue *aq = &edev->aq; + int err; + + cmd.aq_common_descriptor.opcode = opcode; + cmd.cntr_handle = cntr_handle; + cmd.attach_type = EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS; + cmd.u.qp_events.qp_handle = qp_handle; + cmd.u.qp_events.events = events; + + err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, + sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, + sizeof(resp)); + if (err) { + ibdev_err_ratelimited(edev->efa_dev, + "Failed to %s event counter [%d]\n", + opcode == EFA_ADMIN_ATTACH_EVENT_COUNTER + ? "attach" + : "detach", + err); + return err; + } + + return 0; +} + +int efa_com_attach_event_counter(struct efa_com_dev *edev, + struct efa_com_attach_event_counter_params *params) +{ + return efa_com_attach_detach_event_counter(edev, + EFA_ADMIN_ATTACH_EVENT_COUNTER, + params->cntr_handle, + params->qp_handle, + params->events); +} + +int efa_com_detach_event_counter(struct efa_com_dev *edev, + struct efa_com_detach_event_counter_params *params) +{ + return efa_com_attach_detach_event_counter(edev, + EFA_ADMIN_DETACH_EVENT_COUNTER, + params->cntr_handle, + params->qp_handle, + params->events); +} + +int efa_com_modify_event_counter(struct efa_com_dev *edev, + struct efa_com_modify_event_counter_params *params) +{ + struct efa_admin_modify_event_counter_cmd cmd = {}; + struct efa_admin_modify_event_counter_resp resp; + struct efa_com_admin_queue *aq = &edev->aq; + int err; + + cmd.aq_common_descriptor.opcode = EFA_ADMIN_MODIFY_EVENT_COUNTER; + cmd.cntr_handle = params->cntr_handle; + cmd.operation = params->operation; + cmd.value = params->value; + + err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, + sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, + sizeof(resp)); + if (err) { + ibdev_err_ratelimited(edev->efa_dev, + "Failed to modify event counter [%d]\n", + err); + return err; + } + + return 0; +} diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index 39bd4e06684a..3e7380fa0abb 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -146,6 +146,9 @@ struct efa_com_get_device_attr_result { u16 min_sq_depth; u16 max_link_speed_gbps; u8 db_bar; + u32 max_event_counters; + u64 event_counter_max_val; + u32 supported_event_counter_qp_events; }; struct efa_com_get_hw_hints_result { @@ -301,6 +304,37 @@ union efa_com_get_stats_result { struct efa_com_network_stats network_stats; }; +struct efa_com_create_event_counter_params { + dma_addr_t dma_addr; + u16 uarn; +}; + +struct efa_com_create_event_counter_result { + u32 cntr_handle; +}; + +struct efa_com_destroy_event_counter_params { + u32 cntr_handle; +}; + +struct efa_com_attach_event_counter_params { + u32 cntr_handle; + u32 qp_handle; + u32 events; +}; + +struct efa_com_detach_event_counter_params { + u32 cntr_handle; + u32 qp_handle; + u32 events; +}; + +struct efa_com_modify_event_counter_params { + u32 cntr_handle; + u8 operation; + u64 value; +}; + int efa_com_create_qp(struct efa_com_dev *edev, struct efa_com_create_qp_params *params, struct efa_com_create_qp_result *res); @@ -351,5 +385,16 @@ int efa_com_dealloc_uar(struct efa_com_dev *edev, int efa_com_get_stats(struct efa_com_dev *edev, struct efa_com_get_stats_params *params, union efa_com_get_stats_result *result); +int efa_com_create_event_counter(struct efa_com_dev *edev, + struct efa_com_create_event_counter_params *params, + struct efa_com_create_event_counter_result *result); +int efa_com_destroy_event_counter(struct efa_com_dev *edev, + struct efa_com_destroy_event_counter_params *params); +int efa_com_attach_event_counter(struct efa_com_dev *edev, + struct efa_com_attach_event_counter_params *params); +int efa_com_detach_event_counter(struct efa_com_dev *edev, + struct efa_com_detach_event_counter_params *params); +int efa_com_modify_event_counter(struct efa_com_dev *edev, + struct efa_com_modify_event_counter_params *params); #endif /* _EFA_COM_CMD_H_ */ diff --git a/drivers/infiniband/hw/efa/efa_main.c b/drivers/infiniband/hw/efa/efa_main.c index ee09dc1e0b43..4cd80727dcd2 100644 --- a/drivers/infiniband/hw/efa/efa_main.c +++ b/drivers/infiniband/hw/efa/efa_main.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include @@ -374,20 +374,25 @@ static const struct ib_device_ops efa_dev_ops = { .alloc_pd = efa_alloc_pd, .alloc_ucontext = efa_alloc_ucontext, .create_user_cq = efa_create_user_cq, + .create_comp_cntr = efa_create_comp_cntr, .create_qp = efa_create_qp, .create_user_ah = efa_create_ah, .dealloc_pd = efa_dealloc_pd, .dealloc_ucontext = efa_dealloc_ucontext, .dereg_mr = efa_dereg_mr, .destroy_ah = efa_destroy_ah, + .destroy_comp_cntr = efa_destroy_comp_cntr, .destroy_cq = efa_destroy_cq, .destroy_qp = efa_destroy_qp, .get_hw_stats = efa_get_hw_stats, .get_link_layer = efa_port_link_layer, .get_port_immutable = efa_get_port_immutable, + .modify_comp_cntr = efa_modify_comp_cntr, .mmap = efa_mmap, .mmap_free = efa_mmap_free, .modify_qp = efa_modify_qp, + .qp_attach_comp_cntr = efa_qp_attach_comp_cntr, + .query_comp_cntr_caps = efa_query_comp_cntr_caps, .query_device = efa_query_device, .query_gid = efa_query_gid, .query_pkey = efa_query_pkey, @@ -399,6 +404,7 @@ static const struct ib_device_ops efa_dev_ops = { INIT_RDMA_OBJ_SIZE(ib_ah, efa_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, efa_cq, ibcq), + INIT_RDMA_OBJ_SIZE(ib_comp_cntr, efa_comp_cntr, ibcc), INIT_RDMA_OBJ_SIZE(ib_pd, efa_pd, ibpd), INIT_RDMA_OBJ_SIZE(ib_qp, efa_qp, ibqp), INIT_RDMA_OBJ_SIZE(ib_ucontext, efa_ucontext, ibucontext), diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 6be8ec53dcb3..9bc8199b60bf 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -167,6 +167,11 @@ static inline struct efa_ah *to_eah(struct ib_ah *ibah) return container_of(ibah, struct efa_ah, ibah); } +static inline struct efa_comp_cntr *to_ecc(struct ib_comp_cntr *ibcc) +{ + return container_of(ibcc, struct efa_comp_cntr, ibcc); +} + static inline struct efa_user_mmap_entry * to_emmap(struct rdma_user_mmap_entry *rdma_entry) { @@ -264,6 +269,9 @@ int efa_query_device(struct ib_device *ibdev, if (EFA_DEV_CAP(dev, UNSOLICITED_WRITE_RECV)) resp.device_caps |= EFA_QUERY_DEVICE_CAPS_UNSOLICITED_WRITE_RECV; + if (EFA_DEV_CAP(dev, EVENT_COUNTERS)) + resp.device_caps |= EFA_QUERY_DEVICE_CAPS_COMP_CNTR; + if (dev->neqs) resp.device_caps |= EFA_QUERY_DEVICE_CAPS_CQ_NOTIFICATIONS; @@ -2260,6 +2268,220 @@ enum rdma_link_layer efa_port_link_layer(struct ib_device *ibdev, return IB_LINK_LAYER_UNSPECIFIED; } +int efa_query_comp_cntr_caps(struct ib_device *ibdev, + struct ib_comp_cntr_caps *caps, + struct uverbs_attr_bundle *attrs) +{ + struct efa_dev *dev = to_edev(ibdev); + u32 dev_ops = dev->dev_attr.supported_event_counter_qp_events; + + caps->max_counters = dev->dev_attr.max_event_counters / 2; + caps->max_value = dev->dev_attr.event_counter_max_val; + + caps->supported_qp_attach_ops = 0; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP) && + EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP_ERR)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_SEND; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP) && + EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP_ERR)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_RECV; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP) && + EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP_ERR)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_RDMA_READ; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP) && + EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP_ERR)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_READ_COMP)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ; + if (EFA_GET(&dev_ops, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_WRITE_COMP)) + caps->supported_qp_attach_ops |= IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE; + + return 0; +} + +static int efa_create_event_counter(struct efa_dev *dev, u16 uarn, dma_addr_t addr, u32 *handle) +{ + struct efa_com_create_event_counter_params params = {}; + struct efa_com_create_event_counter_result result; + int err; + + params.uarn = uarn; + params.dma_addr = addr; + + err = efa_com_create_event_counter(&dev->edev, ¶ms, &result); + if (err) + return err; + + *handle = result.cntr_handle; + return 0; +} + +static int efa_destroy_event_counter(struct efa_dev *dev, u32 handle) +{ + struct efa_com_destroy_event_counter_params params = { + .cntr_handle = handle, + }; + + return efa_com_destroy_event_counter(&dev->edev, ¶ms); +} + +int efa_create_comp_cntr(struct ib_comp_cntr *ibcc, struct uverbs_attr_bundle *attrs) +{ + struct efa_dev *dev = to_edev(ibcc->device); + struct efa_comp_cntr *cc = to_ecc(ibcc); + struct efa_ucontext *ucontext; + struct ib_umem *comp_umem; + struct ib_umem *err_umem; + dma_addr_t comp_addr; + dma_addr_t err_addr; + int err; + + ucontext = rdma_udata_to_drv_context(&attrs->driver_udata, struct efa_ucontext, + ibucontext); + + comp_umem = ib_umem_get_attr(ibcc->device, attrs, EFA_IB_ATTR_CREATE_COMP_CNTR_COMP_BUFFER, + sizeof(u64), IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(comp_umem)) + return PTR_ERR(comp_umem); + + err_umem = ib_umem_get_attr(ibcc->device, attrs, EFA_IB_ATTR_CREATE_COMP_CNTR_ERR_BUFFER, + sizeof(u64), IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(err_umem)) { + err = PTR_ERR(err_umem); + goto err_comp_umem; + } + + comp_addr = ib_umem_start_dma_addr(comp_umem); + err_addr = ib_umem_start_dma_addr(err_umem); + + if (!IS_ALIGNED(comp_addr, sizeof(u64)) || !IS_ALIGNED(err_addr, sizeof(u64))) { + ibdev_dbg(&dev->ibdev, "Completion Counter memory is unaligned\n"); + err = -EINVAL; + goto err_err_umem; + } + + err = efa_create_event_counter(dev, ucontext->uarn, comp_addr, &cc->comp_handle); + if (err) { + ibdev_dbg(&dev->ibdev, "Failed to create comp event counter [%d]\n", err); + goto err_err_umem; + } + + err = efa_create_event_counter(dev, ucontext->uarn, err_addr, &cc->err_handle); + if (err) { + ibdev_dbg(&dev->ibdev, "Failed to create err event counter [%d]\n", err); + goto err_destroy_comp_event_cntr; + } + + cc->comp_umem = comp_umem; + cc->err_umem = err_umem; + + return 0; + +err_destroy_comp_event_cntr: + efa_destroy_event_counter(dev, cc->comp_handle); +err_err_umem: + ib_umem_release(err_umem); +err_comp_umem: + ib_umem_release(comp_umem); + return err; +} + +int efa_destroy_comp_cntr(struct ib_comp_cntr *ibcc) +{ + struct efa_dev *dev = to_edev(ibcc->device); + struct efa_comp_cntr *cc = to_ecc(ibcc); + + efa_destroy_event_counter(dev, cc->comp_handle); + efa_destroy_event_counter(dev, cc->err_handle); + + ib_umem_release(cc->comp_umem); + ib_umem_release(cc->err_umem); + return 0; +} + +int efa_modify_comp_cntr(struct ib_comp_cntr *ibcc, enum ib_comp_cntr_entry entry, + enum ib_comp_cntr_modify_op op, u64 value) +{ + struct efa_com_modify_event_counter_params params = {}; + struct efa_comp_cntr *cc = to_ecc(ibcc); + + params.cntr_handle = entry == IB_COMP_CNTR_ENTRY_ERR ? cc->err_handle : cc->comp_handle; + params.operation = op == IB_COMP_CNTR_MODIFY_OP_SET ? + EFA_ADMIN_EVENT_COUNTER_MODIFY_SET : EFA_ADMIN_EVENT_COUNTER_MODIFY_ADD; + params.value = value; + + return efa_com_modify_event_counter(&to_edev(ibcc->device)->edev, ¶ms); +} + +static u32 efa_comp_cntr_op_to_comp_events(u32 op_mask) +{ + u32 events = 0; + + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_SEND) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RECV) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RDMA_READ) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_READ) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_READ_COMP, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_REMOTE_RDMA_WRITE) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_REMOTE_WRITE_COMP, 1); + + return events; +} + +static u32 efa_comp_cntr_op_to_err_events(u32 op_mask) +{ + u32 events = 0; + + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_SEND) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_SEND_COMP_ERR, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RECV) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_RECV_COMP_ERR, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RDMA_READ) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_READ_COMP_ERR, 1); + if (op_mask & IB_QP_ATTACH_COMP_CNTR_OP_RDMA_WRITE) + EFA_SET(&events, EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS_WRITE_COMP_ERR, 1); + + return events; +} + +int efa_qp_attach_comp_cntr(struct ib_qp *ibqp, struct ib_comp_cntr *ibcc, + struct ib_qp_attach_comp_cntr_attr *attr) +{ + struct efa_com_detach_event_counter_params detach_params = {}; + struct efa_com_attach_event_counter_params params = {}; + struct efa_dev *dev = to_edev(ibqp->device); + struct efa_comp_cntr *cc = to_ecc(ibcc); + struct efa_qp *qp = to_eqp(ibqp); + int err; + + params.cntr_handle = cc->comp_handle; + params.qp_handle = qp->qp_handle; + params.events = efa_comp_cntr_op_to_comp_events(attr->op_mask); + + err = efa_com_attach_event_counter(&dev->edev, ¶ms); + if (err) + return err; + + params.cntr_handle = cc->err_handle; + params.events = efa_comp_cntr_op_to_err_events(attr->op_mask); + + err = efa_com_attach_event_counter(&dev->edev, ¶ms); + if (err) { + detach_params.cntr_handle = cc->comp_handle; + detach_params.qp_handle = qp->qp_handle; + detach_params.events = efa_comp_cntr_op_to_comp_events(attr->op_mask); + efa_com_detach_event_counter(&dev->edev, &detach_params); + return err; + } + + return 0; +} + DECLARE_UVERBS_NAMED_METHOD(EFA_IB_METHOD_MR_QUERY, UVERBS_ATTR_IDR(EFA_IB_ATTR_QUERY_MR_HANDLE, UVERBS_OBJECT_MR, @@ -2282,8 +2504,23 @@ ADD_UVERBS_METHODS(efa_mr, UVERBS_OBJECT_MR, &UVERBS_METHOD(EFA_IB_METHOD_MR_QUERY)); +ADD_UVERBS_ATTRIBUTES_SIMPLE( + efa_comp_cntr_create, + UVERBS_OBJECT_COMP_CNTR, + UVERBS_METHOD_COMP_CNTR_CREATE, + UVERBS_ATTR_PTR_IN( + EFA_IB_ATTR_CREATE_COMP_CNTR_COMP_BUFFER, + UVERBS_ATTR_STRUCT(struct ib_uverbs_buffer_desc, length), + UA_MANDATORY), + UVERBS_ATTR_PTR_IN( + EFA_IB_ATTR_CREATE_COMP_CNTR_ERR_BUFFER, + UVERBS_ATTR_STRUCT(struct ib_uverbs_buffer_desc, length), + UA_MANDATORY)); + const struct uapi_definition efa_uapi_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_MR, &efa_mr), + UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_COMP_CNTR, + &efa_comp_cntr_create), {}, }; diff --git a/include/uapi/rdma/efa-abi.h b/include/uapi/rdma/efa-abi.h index d5c18f8de182..c79b54aade23 100644 --- a/include/uapi/rdma/efa-abi.h +++ b/include/uapi/rdma/efa-abi.h @@ -133,6 +133,7 @@ enum { EFA_QUERY_DEVICE_CAPS_RDMA_WRITE = 1 << 5, EFA_QUERY_DEVICE_CAPS_UNSOLICITED_WRITE_RECV = 1 << 6, EFA_QUERY_DEVICE_CAPS_CQ_WITH_EXT_MEM = 1 << 7, + EFA_QUERY_DEVICE_CAPS_COMP_CNTR = 1 << 8, }; struct efa_ibv_ex_query_device_resp { @@ -163,4 +164,9 @@ enum efa_mr_methods { EFA_IB_METHOD_MR_QUERY = (1U << UVERBS_ID_NS_SHIFT), }; +enum efa_comp_cntr_create_attrs { + EFA_IB_ATTR_CREATE_COMP_CNTR_COMP_BUFFER = (1U << UVERBS_ID_NS_SHIFT), + EFA_IB_ATTR_CREATE_COMP_CNTR_ERR_BUFFER, +}; + #endif /* EFA_ABI_USER_H */ From 9b66c9af7172ffcf727214fa0ebe9a5e1ed6eb16 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Tue, 21 Jul 2026 04:54:40 -0700 Subject: [PATCH 094/160] RDMA/bnxt_re: Clear VM_MAYWRITE on DBR/toggle page mmap bnxt_re_mmap() rejects VM_WRITE for the DBR_PAGE and TOGGLE_PAGE mmap flags, but a read-only mapping can still retain VM_MAYWRITE. nd later be upgraded with mprotect(PROT_WRITE). This can bypass the write check that only runs at mmap time. Clear VM_MAYWRITE before vm_insert_page() in the shared DBR/toggle-page branch, matching the existing policy that userspace writes are not expected for these pages. Fixes: ea222485788208 ("RDMA/bnxt_re: Update alloc_page uapi for pacing") Suggested-by: Yousef Alhouseen Signed-off-by: Selvin Xavier Link: https://patch.msgid.link/20260721115440.24021-5-selvin.xavier@broadcom.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index adc693736769..a2a354a0fbef 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -4985,11 +4985,13 @@ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) case BNXT_RE_MMAP_DBR_PAGE: case BNXT_RE_MMAP_TOGGLE_PAGE: /* Driver doesn't expect write access for user space */ - if (vma->vm_flags & VM_WRITE) + if (vma->vm_flags & VM_WRITE) { ret = -EFAULT; - else + } else { + vm_flags_clear(vma, VM_MAYWRITE); ret = vm_insert_page(vma, vma->vm_start, virt_to_page((void *)bnxt_entry->mem_offset)); + } break; default: ret = -EINVAL; From e5e8706dca4df84a24b2a20add9979ddc8332684 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Wed, 22 Jul 2026 11:33:30 +0000 Subject: [PATCH 095/160] RDMA/efa: Add CQ/QP creation with 64-bit SQ req ID support Add the support needed to propagate the user requested flags to config the CQ/QP to support 64-bit SQ request ID to the device. Reviewed-by: Michael Margolin Reviewed-by: Tom Sela Signed-off-by: Yonatan Nachum Link: https://patch.msgid.link/20260722113331.2515247-2-ynachum@amazon.com Signed-off-by: Leon Romanovsky --- .../infiniband/hw/efa/efa_admin_cmds_defs.h | 20 ++++++++++++++++--- drivers/infiniband/hw/efa/efa_com_cmd.c | 8 ++++++++ drivers/infiniband/hw/efa/efa_com_cmd.h | 2 ++ drivers/infiniband/hw/efa/efa_io_defs.h | 12 +++++++++-- drivers/infiniband/hw/efa/efa_verbs.c | 7 +++++++ include/uapi/rdma/efa-abi.h | 4 +++- 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index fc07213e26af..95d1493153cd 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -119,7 +119,10 @@ struct efa_admin_create_qp_cmd { * 2 : unsolicited_write_recv - If set, work requests * will not be consumed for incoming RDMA write with * immediate - * 7:3 : reserved - MBZ + * 3 : sq_64_bit_req_id - If set, requests posted on + * SQ will use 64-bit ids. The corresponding CQ must + * also have 64-bit ids enabled. + * 7:4 : reserved - MBZ */ u8 flags; @@ -523,7 +526,9 @@ struct efa_admin_create_cq_cmd { * 5 : set_src_addr - If set, source address will be * filled on RX completions from unknown senders. * Requires 8 words CQ entry size. - * 7:6 : reserved7 - MBZ + * 6 : sq_comp_64_bit_req_id - If set, send + * completions will use 64-bit work request ids + * 7 : reserved7 - MBZ */ u8 cq_caps_2; @@ -730,7 +735,10 @@ struct efa_admin_feature_device_attr_desc { * write with imm. receive is supported * 5 : event_counters - If set, event counters are * supported - * 31:6 : reserved - MBZ + * 9:6 : reserved1 - MBZ + * 10 : sq_64_bit_req_id - If set, SQ can use 64-bit + * work request ids + * 31:11 : reserved2 - MBZ */ u32 device_caps; @@ -1250,6 +1258,8 @@ struct efa_admin_modify_event_counter_resp { #define EFA_ADMIN_CREATE_QP_CMD_SQ_VIRT_MASK BIT(0) #define EFA_ADMIN_CREATE_QP_CMD_RQ_VIRT_MASK BIT(1) #define EFA_ADMIN_CREATE_QP_CMD_UNSOLICITED_WRITE_RECV_MASK BIT(2) +#define EFA_ADMIN_CREATE_QP_CMD_SQ_64_BIT_REQ_ID_SHIFT 3 +#define EFA_ADMIN_CREATE_QP_CMD_SQ_64_BIT_REQ_ID_MASK BIT(3) /* modify_qp_cmd */ #define EFA_ADMIN_MODIFY_QP_CMD_QP_STATE_MASK BIT(0) @@ -1276,6 +1286,8 @@ struct efa_admin_modify_event_counter_resp { #define EFA_ADMIN_CREATE_CQ_CMD_VIRT_MASK BIT(6) #define EFA_ADMIN_CREATE_CQ_CMD_CQ_ENTRY_SIZE_WORDS_MASK GENMASK(4, 0) #define EFA_ADMIN_CREATE_CQ_CMD_SET_SRC_ADDR_MASK BIT(5) +#define EFA_ADMIN_CREATE_CQ_CMD_SQ_COMP_64_BIT_REQ_ID_SHIFT 6 +#define EFA_ADMIN_CREATE_CQ_CMD_SQ_COMP_64_BIT_REQ_ID_MASK BIT(6) /* create_cq_resp */ #define EFA_ADMIN_CREATE_CQ_RESP_DB_VALID_MASK BIT(0) @@ -1287,6 +1299,8 @@ struct efa_admin_modify_event_counter_resp { #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_RDMA_WRITE_MASK BIT(3) #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_UNSOLICITED_WRITE_RECV_MASK BIT(4) #define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_EVENT_COUNTERS_MASK BIT(5) +#define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_SQ_64_BIT_REQ_ID_SHIFT 10 +#define EFA_ADMIN_FEATURE_DEVICE_ATTR_DESC_SQ_64_BIT_REQ_ID_MASK BIT(10) /* feature_queue_attr_desc_2 */ #define EFA_ADMIN_FEATURE_QUEUE_ATTR_DESC_2_SEND_COMP_MASK BIT(0) diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index a9fd44b4debf..198a27d02b18 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -38,6 +38,9 @@ int efa_com_create_qp(struct efa_com_dev *edev, if (params->unsolicited_write_recv) EFA_SET(&create_qp_cmd.flags, EFA_ADMIN_CREATE_QP_CMD_UNSOLICITED_WRITE_RECV, 1); + if (params->sq_64_bit_req_id) + EFA_SET(&create_qp_cmd.flags, EFA_ADMIN_CREATE_QP_CMD_SQ_64_BIT_REQ_ID, 1); + err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&create_qp_cmd, sizeof(create_qp_cmd), @@ -178,6 +181,11 @@ int efa_com_create_cq(struct efa_com_dev *edev, EFA_SET(&create_cmd.cq_caps_2, EFA_ADMIN_CREATE_CQ_CMD_SET_SRC_ADDR, 1); } + if (params->sq_comp_64_bit_req_id) { + EFA_SET(&create_cmd.cq_caps_2, + EFA_ADMIN_CREATE_CQ_CMD_SQ_COMP_64_BIT_REQ_ID, 1); + } + efa_com_set_dma_addr(params->dma_addr, &create_cmd.cq_ba.mem_addr_high, &create_cmd.cq_ba.mem_addr_low); diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index 3e7380fa0abb..bca722f021ea 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -29,6 +29,7 @@ struct efa_com_create_qp_params { u8 qp_type; u8 sl; u8 unsolicited_write_recv : 1; + u8 sq_64_bit_req_id : 1; }; struct efa_com_create_qp_result { @@ -79,6 +80,7 @@ struct efa_com_create_cq_params { u8 entry_size_in_bytes; u8 interrupt_mode_enabled : 1; u8 set_src_addr : 1; + u8 sq_comp_64_bit_req_id : 1; }; struct efa_com_create_cq_result { diff --git a/drivers/infiniband/hw/efa/efa_io_defs.h b/drivers/infiniband/hw/efa/efa_io_defs.h index a849d92a6eb6..1444552de2de 100644 --- a/drivers/infiniband/hw/efa/efa_io_defs.h +++ b/drivers/infiniband/hw/efa/efa_io_defs.h @@ -79,6 +79,10 @@ enum efa_io_processing_hint { EFA_IO_PROCESSING_HINT_BURST_PPS_SENSITIVE = 1 << 0, }; +struct efa_io_req_id_ex { + u16 w[3]; +}; + struct efa_io_tx_meta_desc { /* Verbs-generated Request ID */ u16 req_id; @@ -141,7 +145,9 @@ struct efa_io_tx_meta_desc { /* Queue key */ u32 qkey; - u8 reserved2[12]; + u8 reserved2[6]; + + struct efa_io_req_id_ex req_id_ex; }; /* @@ -327,8 +333,10 @@ struct efa_io_tx_cdesc { /* Common completion info */ struct efa_io_cdesc_common common; + struct efa_io_req_id_ex req_id_ex; + /* MBZ */ - u16 reserved16; + u8 reserved[4]; }; /* Rx Completion Descriptor */ diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 9bc8199b60bf..73a80caca7e2 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -719,6 +719,9 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, if (EFA_DEV_CAP(dev, UNSOLICITED_WRITE_RECV)) supported_efa_flags |= EFA_CREATE_QP_WITH_UNSOLICITED_WRITE_RECV; + if (EFA_DEV_CAP(dev, SQ_64_BIT_REQ_ID)) + supported_efa_flags |= EFA_CREATE_QP_WITH_SQ_64_BIT_REQ_ID; + if (cmd.flags & ~supported_efa_flags) { ibdev_dbg(&dev->ibdev, "Unsupported EFA QP create flags[%#x], supported[%#x]\n", cmd.flags, supported_efa_flags); @@ -778,6 +781,9 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, if (cmd.flags & EFA_CREATE_QP_WITH_UNSOLICITED_WRITE_RECV) create_qp_params.unsolicited_write_recv = true; + if (cmd.flags & EFA_CREATE_QP_WITH_SQ_64_BIT_REQ_ID) + create_qp_params.sq_64_bit_req_id = true; + err = efa_com_create_qp(&dev->edev, &create_qp_params, &create_qp_resp); if (err) @@ -1212,6 +1218,7 @@ int efa_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, params.entry_size_in_bytes = cmd.cq_entry_size; params.num_sub_cqs = cmd.num_sub_cqs; params.set_src_addr = set_src_addr; + params.sq_comp_64_bit_req_id = !!(cmd.flags & EFA_CREATE_CQ_WITH_SQ_COMP_64_BIT_REQ_ID); if (cmd.flags & EFA_CREATE_CQ_WITH_COMPLETION_CHANNEL) { cq->eq = efa_vec2eq(dev, attr->comp_vector); params.eqn = cq->eq->eeq.eqn; diff --git a/include/uapi/rdma/efa-abi.h b/include/uapi/rdma/efa-abi.h index c79b54aade23..5d3d01b6333e 100644 --- a/include/uapi/rdma/efa-abi.h +++ b/include/uapi/rdma/efa-abi.h @@ -56,7 +56,8 @@ struct efa_ibv_alloc_pd_resp { enum { EFA_CREATE_CQ_WITH_COMPLETION_CHANNEL = 1 << 0, - EFA_CREATE_CQ_WITH_SGID = 1 << 1, + EFA_CREATE_CQ_WITH_SGID = 1 << 1, + EFA_CREATE_CQ_WITH_SQ_COMP_64_BIT_REQ_ID = 1 << 2, }; struct efa_ibv_create_cq { @@ -88,6 +89,7 @@ enum { enum { EFA_CREATE_QP_WITH_UNSOLICITED_WRITE_RECV = 1 << 0, + EFA_CREATE_QP_WITH_SQ_64_BIT_REQ_ID = 1 << 1, }; struct efa_ibv_create_qp { From 524381ef281bb886f215ac9341b3c2e5b59ec178 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Wed, 22 Jul 2026 11:33:31 +0000 Subject: [PATCH 096/160] RDMA/efa: Expose 64-bit send WR ID support to userspace Currently EFA WRs support 16-bit request ID, this requires EFA to manage a translation table to translate the IB WR ID from 64-bits to 16-bits and translating it back on CQ completion. Expose a new device capability to handle 64-bit request ID for SQ WRs allowing userspace to directly post the 64-bit ID to the device. Reviewed-by: Michael Margolin Reviewed-by: Tom Sela Signed-off-by: Yonatan Nachum Link: https://patch.msgid.link/20260722113331.2515247-3-ynachum@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_verbs.c | 3 +++ include/uapi/rdma/efa-abi.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 73a80caca7e2..6abb93b50731 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -272,6 +272,9 @@ int efa_query_device(struct ib_device *ibdev, if (EFA_DEV_CAP(dev, EVENT_COUNTERS)) resp.device_caps |= EFA_QUERY_DEVICE_CAPS_COMP_CNTR; + if (EFA_DEV_CAP(dev, SQ_64_BIT_REQ_ID)) + resp.device_caps |= EFA_QUERY_DEVICE_CAPS_SQ_64_BIT_REQ_ID; + if (dev->neqs) resp.device_caps |= EFA_QUERY_DEVICE_CAPS_CQ_NOTIFICATIONS; diff --git a/include/uapi/rdma/efa-abi.h b/include/uapi/rdma/efa-abi.h index 5d3d01b6333e..2094b4bcc5cf 100644 --- a/include/uapi/rdma/efa-abi.h +++ b/include/uapi/rdma/efa-abi.h @@ -136,6 +136,7 @@ enum { EFA_QUERY_DEVICE_CAPS_UNSOLICITED_WRITE_RECV = 1 << 6, EFA_QUERY_DEVICE_CAPS_CQ_WITH_EXT_MEM = 1 << 7, EFA_QUERY_DEVICE_CAPS_COMP_CNTR = 1 << 8, + EFA_QUERY_DEVICE_CAPS_SQ_64_BIT_REQ_ID = 1 << 9, }; struct efa_ibv_ex_query_device_resp { From db19e7c131c0442f7625d5e4153a4184afc8c215 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 23 Jul 2026 10:14:48 +0300 Subject: [PATCH 097/160] IB/mlx5: simplify set_param() Simplify 'set_param()' by using the convenient 'kstrtou32_from_user()'. Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20260723071448.568641-1-dmantipov@yandex.ru Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/cong.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c index d0edf83a2f20..98c90d0dbab4 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -373,22 +373,12 @@ static ssize_t set_param(struct file *filp, const char __user *buf, { struct mlx5_ib_dbg_param *param = filp->private_data; int offset = param->offset; - char lbuf[11] = { }; u32 var; int ret; - if (count > sizeof(lbuf)) - return -EINVAL; - - if (copy_from_user(lbuf, buf, count)) - return -EFAULT; - - lbuf[sizeof(lbuf) - 1] = '\0'; - - if (kstrtou32(lbuf, 0, &var)) - return -EINVAL; - - ret = mlx5_ib_set_cc_params(param->dev, param->port_num, offset, var); + ret = kstrtou32_from_user(buf, count, 0, &var); + if (!ret) + ret = mlx5_ib_set_cc_params(param->dev, param->port_num, offset, var); return ret ? ret : count; } From 7ddcd75596cf124d84cfe9f36ea877e7d87f9dcc Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 23 Jul 2026 10:16:29 +0300 Subject: [PATCH 098/160] RDMA/bnxt_re: simplify bnxt_re_cc_config_set() and cq_coal_cfg_write() Simplify 'bnxt_re_cc_config_set()' and 'cq_coal_cfg_write()' by using the convenient 'kstrtou32_from_user()'. Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20260723071629.568675-1-dmantipov@yandex.ru Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/debugfs.c | 31 ++++++------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/debugfs.c b/drivers/infiniband/hw/bnxt_re/debugfs.c index 143e9bfc6b79..efcfec8ec25c 100644 --- a/drivers/infiniband/hw/bnxt_re/debugfs.c +++ b/drivers/infiniband/hw/bnxt_re/debugfs.c @@ -308,21 +308,12 @@ static ssize_t bnxt_re_cc_config_set(struct file *filp, const char __user *buffe struct bnxt_re_dev *rdev = dbg_cc_param->rdev; u32 offset = dbg_cc_param->offset; u8 cc_gen = dbg_cc_param->cc_gen; - char buf[16]; u32 val; int rc; - if (count >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(buf, buffer, count)) - return -EFAULT; - - buf[count] = '\0'; - if (kstrtou32(buf, 0, &val)) - return -EINVAL; - - rc = bnxt_re_configure_cc(rdev, cc_gen, offset, val); + rc = kstrtou32_from_user(buffer, count, 0, &val); + if (!rc) + rc = bnxt_re_configure_cc(rdev, cc_gen, offset, val); return rc ? rc : count; } @@ -374,20 +365,12 @@ static ssize_t cq_coal_cfg_write(struct file *file, struct seq_file *s = file->private_data; struct bnxt_re_cq_coal_param *param = s->private; struct bnxt_re_dev *rdev = param->rdev; - int offset = param->offset; - char lbuf[16] = { }; + int ret, offset = param->offset; u32 val; - if (count > sizeof(lbuf)) - return -EINVAL; - - if (copy_from_user(lbuf, buf, count)) - return -EFAULT; - - lbuf[sizeof(lbuf) - 1] = '\0'; - - if (kstrtou32(lbuf, 0, &val)) - return -EINVAL; + ret = kstrtou32_from_user(buf, count, 0, &val); + if (ret) + return ret; switch (offset) { case BNXT_RE_COAL_CQ_BUF_MAXTIME: From 66fd61fe1aa7177bdf9de6f27989307e6a73bfbb Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 23 Jul 2026 10:18:45 +0300 Subject: [PATCH 099/160] RDMA/ocrdma: accept boolean value in ocrdma_dbgfs_ops_write() Since reset is actually controlled by the boolean flag rather than long, switch to 'kstrtobool_from_user()' and use the latter for an overall simplification of 'ocrdma_dbgfs_ops_write()'. Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20260723071845.568718-1-dmantipov@yandex.ru Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/ocrdma/ocrdma_stats.c | 26 +++++++-------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c index 0834416cb3f8..8f26f62f2243 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c @@ -635,39 +635,29 @@ static ssize_t ocrdma_dbgfs_ops_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos) { - char tmp_str[32]; - long reset; + bool reset; int status; struct ocrdma_stats *pstats = filp->private_data; struct ocrdma_dev *dev = pstats->dev; - if (*ppos != 0 || count == 0 || count > sizeof(tmp_str)) - goto err; - - if (copy_from_user(tmp_str, buffer, count)) - goto err; - - tmp_str[count-1] = '\0'; - if (kstrtol(tmp_str, 10, &reset)) - goto err; + status = kstrtobool_from_user(buffer, count, &reset); + if (status) + return status; switch (pstats->type) { case OCRDMA_RESET_STATS: if (reset) { status = ocrdma_mbx_rdma_stats(dev, true); - if (status) { + if (status) pr_err("Failed to reset stats = %d\n", status); - goto err; - } } break; default: - goto err; + status = -EINVAL; + break; } - return count; -err: - return -EFAULT; + return status ? status : count; } void ocrdma_pma_counters(struct ocrdma_dev *dev, struct ib_mad *out_mad) From 652befcba956ef357f480525ccbe25c59bc81d4d Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 23 Jul 2026 07:35:23 -0400 Subject: [PATCH 100/160] RDMA/erdma: complete object teardown when the destroy command fails erdma_destroy_qp(), erdma_destroy_cq(), erdma_dereg_mr(), and erdma_destroy_ah() returned early when erdma_post_cmd_wait() failed, leaking the queue buffers, MTTs, doorbells and the STAG, QPN, CQN and AHN identifiers. A command timeout clears ERDMA_CMDQ_STATE_OK_BIT and permanently disables the command queue, so no retry can succeed; the RDMA core keeps the object after a failed destructor and forced uverbs cleanup then nulls the pointers, making the resources unreachable. Warn on failure but release every software-owned resource and return success, since during terminal destruction the hardware command result is only diagnostic. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Link: https://patch.msgid.link/20260722-b4-qp-and-cq-memory-are-leaked-if-the-d-v1-1-97e223dc1c96@nvidia.com Signed-off-by: Leon Romanovsky Acked-by: Cheng Xu --- drivers/infiniband/hw/erdma/erdma_verbs.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 71e3e8618a61..ab6abbab029e 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -1302,8 +1302,15 @@ int erdma_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) ret = erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL, true); + /* + * A timeout disables the command queue, so retry cannot succeed. Treat + * terminal command failures as diagnostic; propagating them can make + * forced uverbs cleanup discard the last software resource pointers. + */ if (ret) - return ret; + ibdev_warn_ratelimited(&dev->ibdev, + "failed to deregister MR 0x%x: %d\n", + ibmr->lkey, ret); erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], ibmr->lkey >> 8); @@ -1329,7 +1336,9 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) err = erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL, true); if (err) - return err; + ibdev_warn_ratelimited(&dev->ibdev, + "failed to destroy CQ %u: %d\n", + cq->cqn, err); if (rdma_is_kernel_res(&cq->ibcq.res)) { dma_free_coherent(&dev->pdev->dev, cq->depth << CQE_SHIFT, @@ -1377,7 +1386,9 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) err = erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL, true); if (err) - return err; + ibdev_warn_ratelimited(&dev->ibdev, + "failed to destroy QP %u: %d\n", + QP_ID(qp), err); erdma_qp_put(qp); wait_for_completion(&qp->safe_free); @@ -2279,7 +2290,9 @@ int erdma_destroy_ah(struct ib_ah *ibah, u32 flags) ret = erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL, flags & RDMA_DESTROY_AH_SLEEPABLE); if (ret) - return ret; + ibdev_warn_ratelimited(&dev->ibdev, + "failed to destroy AH %u: %d\n", + ah->ahn, ret); erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_AH], ah->ahn); From 1773ca1a8912ccad9e7de458413aef9ab42ec5d9 Mon Sep 17 00:00:00 2001 From: Konstantin Taranov Date: Fri, 17 Jul 2026 13:08:39 -0700 Subject: [PATCH 101/160] RDMA/mana_ib: return PD number to the user Implement returning to userspace applications PDNs of created PDs. The PDN is used by applications that build work requests outside of the rdma-core code base. The PDN is used to build work requests that require mentioning the PD. The HW still ensures PD isolation using PDN attached to MRs and WRs, therefore the PDN mentioned in the work request must match the PDN of the used work queue. The work requests can fit only 16 bit PDNs. Allow users to request short PDNs which are 16 bits. The capability to request short PDN and get PDN is encoded in the response of alloc user context IOCTL. Signed-off-by: Konstantin Taranov Link: https://patch.msgid.link/20260717200839.495327-1-kotaranov@linux.microsoft.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mana/main.c | 62 ++++++++++++++++++++++--------- include/net/mana/gdma.h | 6 +-- include/uapi/rdma/mana-abi.h | 22 +++++++++++ 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index 0bfb1883a186..151874529b56 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -90,20 +90,41 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, return err; } +static int mana_gd_destroy_pd(struct mana_ib_dev *mdev, u64 pd_handle) +{ + struct gdma_destroy_pd_resp resp = {}; + struct gdma_destroy_pd_req req = {}; + + mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_PD, sizeof(req), + sizeof(resp)); + + req.pd_handle = pd_handle; + + return mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), &req, sizeof(resp), &resp); +} + int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); + struct mana_ib_alloc_pd_resp ucmd_resp = {}; struct ib_device *ibdev = ibpd->device; struct gdma_create_pd_resp resp = {}; struct gdma_create_pd_req req = {}; + struct mana_ib_alloc_pd ucmd; enum gdma_pd_flags flags = 0; struct mana_ib_dev *dev; struct gdma_context *gc; int err; - err = ib_no_udata_io(udata); - if (err) - return err; + if (udata && udata->inlen) { + err = ib_copy_validate_udata_in_cm(udata, ucmd, reserved, + MANA_IB_PD_SHORT_PDN); + if (err) + return err; + + if (ucmd.comp_mask & MANA_IB_PD_SHORT_PDN) + flags |= GDMA_PD_FLAG_SHORT_PDN; + } dev = container_of(ibdev, struct mana_ib_dev, ib_dev); gc = mdev_to_gc(dev); @@ -121,22 +142,29 @@ int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) pd->pd_handle = resp.pd_handle; pd->pdn = resp.pd_id; - ibdev_dbg(&dev->ib_dev, "pd_handle 0x%llx pd_id %d\n", - pd->pd_handle, pd->pdn); - mutex_init(&pd->vport_mutex); pd->vport_use_count = 0; + + if (udata) { + ucmd_resp.pdn = pd->pdn; + err = ib_respond_udata(udata, ucmd_resp); + if (err) + goto destroy_pd; + } + return 0; + +destroy_pd: + mana_gd_destroy_pd(dev, pd->pd_handle); + + return err; } int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd); struct ib_device *ibdev = ibpd->device; - struct gdma_destory_pd_resp resp = {}; - struct gdma_destroy_pd_req req = {}; struct mana_ib_dev *dev; - struct gdma_context *gc; int err; err = ib_no_udata_io(udata); @@ -144,14 +172,7 @@ int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) return err; dev = container_of(ibdev, struct mana_ib_dev, ib_dev); - gc = mdev_to_gc(dev); - - mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_PD, sizeof(req), - sizeof(resp)); - - req.pd_handle = pd->pd_handle; - - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + err = mana_gd_destroy_pd(dev, pd->pd_handle); if (err) return err; @@ -205,13 +226,14 @@ int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext, { struct mana_ib_ucontext *ucontext = container_of(ibcontext, struct mana_ib_ucontext, ibucontext); + struct mana_ib_alloc_ucontext_resp ucmd_resp = {}; struct ib_device *ibdev = ibcontext->device; struct mana_ib_dev *mdev; struct gdma_context *gc; int doorbell_page; int ret; - ret = ib_no_udata_io(udata); + ret = ib_is_udata_in_empty(udata); if (ret) return ret; @@ -224,6 +246,10 @@ int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext, return ret; ucontext->doorbell = doorbell_page; + ucmd_resp.comp_mask = MANA_IB_UCNTX_ALLOC_PDN_SUPPORT; + ret = ib_respond_udata(udata, ucmd_resp); + if (ret) + return ret; return 0; } diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 0c395917b214..de17c9bba04b 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -867,8 +867,8 @@ struct gdma_destroy_dma_region_req { }; /* HW DATA */ enum gdma_pd_flags { - GDMA_PD_FLAG_INVALID = 0, - GDMA_PD_FLAG_ALLOW_GPA_MR = 1, + GDMA_PD_FLAG_ALLOW_GPA_MR = BIT(0), + GDMA_PD_FLAG_SHORT_PDN = BIT(2), }; struct gdma_create_pd_req { @@ -889,7 +889,7 @@ struct gdma_destroy_pd_req { u64 pd_handle; };/* HW DATA */ -struct gdma_destory_pd_resp { +struct gdma_destroy_pd_resp { struct gdma_resp_hdr hdr; };/* HW DATA */ diff --git a/include/uapi/rdma/mana-abi.h b/include/uapi/rdma/mana-abi.h index 8336bf51b7c5..410f0ddc8c89 100644 --- a/include/uapi/rdma/mana-abi.h +++ b/include/uapi/rdma/mana-abi.h @@ -87,4 +87,26 @@ struct mana_ib_create_qp_rss_resp { struct rss_resp_entry entries[64]; }; +enum mana_ib_ucontext_support { + MANA_IB_UCNTX_ALLOC_PDN_SUPPORT = 1 << 0, +}; + +struct mana_ib_alloc_ucontext_resp { + __aligned_u64 comp_mask; +}; + +enum mana_ib_create_pd_flags { + MANA_IB_PD_SHORT_PDN = 1 << 0, +}; + +struct mana_ib_alloc_pd { + __u32 comp_mask; + __u32 reserved; +}; + +struct mana_ib_alloc_pd_resp { + __u32 pdn; + __u32 reserved; +}; + #endif From fe34dc1fa082243f087d8d0aea287b2c04d813b6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 1 Jul 2026 14:40:13 +0200 Subject: [PATCH 102/160] RDMA/uverbs: Add SRQ buffer UMEM attribute Apply the per-attribute UMEM model to the SRQ create method. Add an optional UMEM attribute that backs the SRQ WQE buffer, so userspace can supply it as either a VA or a dma-buf through a single descriptor, consistent with the CQ and QP create methods. mlx5 is the only driver that pins an SRQ WQE buffer via umem; it maps a single ucmd->buf_addr region through this attribute. No other driver implements a user SRQ buffer, so none of them use the attribute. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260701124015.64350-2-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/uverbs_std_types_srq.c | 2 ++ include/uapi/rdma/ib_user_ioctl_cmds.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/infiniband/core/uverbs_std_types_srq.c b/drivers/infiniband/core/uverbs_std_types_srq.c index e5513f828bdc..0421bdd225df 100644 --- a/drivers/infiniband/core/uverbs_std_types_srq.c +++ b/drivers/infiniband/core/uverbs_std_types_srq.c @@ -192,6 +192,8 @@ DECLARE_UVERBS_NAMED_METHOD( UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_SRQ_RESP_SRQ_NUM, UVERBS_ATTR_TYPE(u32), UA_OPTIONAL), + UVERBS_ATTR_UMEM(UVERBS_ATTR_CREATE_SRQ_BUF_UMEM, + UA_OPTIONAL), UVERBS_ATTR_UHW()); static int UVERBS_HANDLER(UVERBS_METHOD_SRQ_DESTROY)( diff --git a/include/uapi/rdma/ib_user_ioctl_cmds.h b/include/uapi/rdma/ib_user_ioctl_cmds.h index 6a3d59d03f54..816b3107464f 100644 --- a/include/uapi/rdma/ib_user_ioctl_cmds.h +++ b/include/uapi/rdma/ib_user_ioctl_cmds.h @@ -204,6 +204,7 @@ enum uverbs_attrs_create_srq_cmd_attr_ids { UVERBS_ATTR_CREATE_SRQ_RESP_MAX_WR, UVERBS_ATTR_CREATE_SRQ_RESP_MAX_SGE, UVERBS_ATTR_CREATE_SRQ_RESP_SRQ_NUM, + UVERBS_ATTR_CREATE_SRQ_BUF_UMEM, }; enum uverbs_attrs_destroy_srq_cmd_attr_ids { From 023349192e79cfb00ef9ad320549e4f6b6cf8309 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 1 Jul 2026 14:40:14 +0200 Subject: [PATCH 103/160] RDMA/mlx5: Use UMEM attribute for SRQ buffer in create_srq Use the per-attribute UMEM helper to pin the SRQ buffer umem on demand. ib_umem_get_attr_or_va() resolves the new CREATE_SRQ_BUF_UMEM attribute when present and otherwise falls back to the existing UHW ucmd->buf_addr VA, preserving the legacy behavior. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260701124015.64350-3-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/srq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 5bc48fef3744..6fa4c5a9a0d5 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -48,6 +48,8 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, struct mlx5_ib_create_srq ucmd; struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( udata, struct mlx5_ib_ucontext, ibucontext); + struct uverbs_attr_bundle *attrs = + rdma_udata_to_uverbs_attr_bundle(udata); int err; u32 uidx = MLX5_IB_DEFAULT_UIDX; @@ -66,7 +68,9 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE); - srq->umem = ib_umem_get_va(pd->device, ucmd.buf_addr, buf_size, 0); + srq->umem = ib_umem_get_attr_or_va(pd->device, attrs, + UVERBS_ATTR_CREATE_SRQ_BUF_UMEM, + ucmd.buf_addr, buf_size, 0); if (IS_ERR(srq->umem)) { mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size); err = PTR_ERR(srq->umem); From aac287f4f1ebebc85f36c0680bcf955ef9145c66 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 1 Jul 2026 14:40:15 +0200 Subject: [PATCH 104/160] RDMA/mlx5: Use UMEM attribute for SRQ doorbell record Add an optional mlx5 driver-namespace UMEM attribute on SRQ create so userspace can supply the doorbell record umem explicitly, symmetric to the CQ and QP sides. Resolve it inside mlx5_ib_db_map_user() and use it as a private DBR page when present; otherwise take the existing UHW share-or-pin path that preserves per-page DBR sharing across CQ/QP/SRQ in the same process. Add mlx5's first UVERBS_OBJECT_SRQ UAPI definition chain to attach the new attr. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260701124015.64350-4-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 1 + drivers/infiniband/hw/mlx5/mlx5_ib.h | 1 + drivers/infiniband/hw/mlx5/srq.c | 19 ++++++++++++++++++- include/uapi/rdma/mlx5_user_ioctl_cmds.h | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index d65ebdef2823..788f79f040b0 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4476,6 +4476,7 @@ static const struct uapi_definition mlx5_ib_defs[] = { UAPI_DEF_CHAIN(mlx5_ib_dm_defs), UAPI_DEF_CHAIN(mlx5_ib_create_cq_defs), UAPI_DEF_CHAIN(mlx5_ib_create_qp_defs), + UAPI_DEF_CHAIN(mlx5_ib_create_srq_defs), UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_DEVICE, &mlx5_ib_query_context), UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_MR, &mlx5_ib_reg_dmabuf_mr), diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 522984d958bb..e9ddf2e97a76 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1517,6 +1517,7 @@ extern const struct uapi_definition mlx5_ib_qos_defs[]; extern const struct uapi_definition mlx5_ib_std_types_defs[]; extern const struct uapi_definition mlx5_ib_create_cq_defs[]; extern const struct uapi_definition mlx5_ib_create_qp_defs[]; +extern const struct uapi_definition mlx5_ib_create_srq_defs[]; static inline int is_qp1(enum ib_qp_type qp_type) { diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 6fa4c5a9a0d5..a973c1b7515f 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -10,6 +10,9 @@ #include "mlx5_ib.h" #include "srq.h" +#define UVERBS_MODULE_NAME mlx5_ib +#include + static void *get_wqe(struct mlx5_ib_srq *srq, int n) { return mlx5_frag_buf_get_wqe(&srq->fbc, n); @@ -78,7 +81,9 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, } in->umem = srq->umem; - err = mlx5_ib_db_map_user(ucontext, NULL, 0, ucmd.db_addr, &srq->db); + err = mlx5_ib_db_map_user(ucontext, attrs, + MLX5_IB_ATTR_CREATE_SRQ_DBR_BUF_UMEM, + ucmd.db_addr, &srq->db); if (err) { mlx5_ib_dbg(dev, "map doorbell failed\n"); goto err_umem; @@ -466,3 +471,15 @@ int mlx5_ib_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr, return err; } + +ADD_UVERBS_ATTRIBUTES_SIMPLE( + mlx5_ib_srq_create, + UVERBS_OBJECT_SRQ, + UVERBS_METHOD_SRQ_CREATE, + UVERBS_ATTR_UMEM(MLX5_IB_ATTR_CREATE_SRQ_DBR_BUF_UMEM, + UA_OPTIONAL)); + +const struct uapi_definition mlx5_ib_create_srq_defs[] = { + UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_SRQ, &mlx5_ib_srq_create), + {}, +}; diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index ddb898afd813..3528743e3858 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -281,6 +281,10 @@ enum mlx5_ib_create_qp_attrs { MLX5_IB_ATTR_CREATE_QP_DBR_BUF_UMEM = UVERBS_ID_DRIVER_NS_WITH_UHW, }; +enum mlx5_ib_create_srq_attrs { + MLX5_IB_ATTR_CREATE_SRQ_DBR_BUF_UMEM = UVERBS_ID_DRIVER_NS_WITH_UHW, +}; + enum mlx5_ib_reg_dmabuf_mr_attrs { MLX5_IB_ATTR_REG_DMABUF_MR_ACCESS_FLAGS = (1U << UVERBS_ID_NS_SHIFT), }; From 8d010293845e21a848fc8e63787e1945f70dde6e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:02 +0200 Subject: [PATCH 105/160] RDMA/core: Pass the net namespace to the device name lookups Prepare for per-netns RDMA device names by passing the target net namespace through the name lookup and allocation helpers. Keep current global uniqueness behaviour. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-2-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index ddf75867ac61..84158f71c613 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -351,7 +351,8 @@ void ib_device_put(struct ib_device *device) } EXPORT_SYMBOL(ib_device_put); -static struct ib_device *__ib_device_get_by_name(const char *name) +static struct ib_device *__ib_device_get_by_name(const char *name, + const struct net *net) { struct ib_device *device; unsigned long index; @@ -395,7 +396,7 @@ int ib_device_rename(struct ib_device *ibdev, const char *name) return 0; } - if (__ib_device_get_by_name(name)) { + if (__ib_device_get_by_name(name, rdma_dev_net(ibdev))) { up_write(&devices_rwsem); return -EEXIST; } @@ -435,7 +436,8 @@ int ib_device_set_dim(struct ib_device *ibdev, u8 use_dim) return 0; } -static int alloc_name(struct ib_device *ibdev, const char *name) +/* Pick a free index for the '%d' style @name pattern. */ +static int alloc_name_id(struct net *net, const char *name) { struct ib_device *device; unsigned long index; @@ -462,15 +464,22 @@ static int alloc_name(struct ib_device *ibdev, const char *name) } rc = ida_alloc(&inuse, GFP_KERNEL); - if (rc < 0) - goto out; - - rc = dev_set_name(&ibdev->dev, name, rc); out: ida_destroy(&inuse); return rc; } +static int alloc_name(struct ib_device *ibdev, const char *name) +{ + int id; + + id = alloc_name_id(rdma_dev_net(ibdev), name); + if (id < 0) + return id; + + return dev_set_name(&ibdev->dev, name, id); +} + static void ib_device_release(struct device *device) { struct ib_device *dev = container_of(device, struct ib_device, dev); @@ -1223,7 +1232,8 @@ static int assign_name(struct ib_device *device, const char *name) if (ret) goto out; - if (__ib_device_get_by_name(dev_name(&device->dev))) { + if (__ib_device_get_by_name(dev_name(&device->dev), + rdma_dev_net(device))) { ret = -ENFILE; goto out; } From fa8f67f797d53dbb2ad921049b9d8efcc894c340 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:03 +0200 Subject: [PATCH 106/160] RDMA/core: Handle device name conflicts when changing net namespace Prepare namespace moves for per-netns names. Check user-initiated moves for destination-name conflicts before disabling the device, keep same-netns moves as no-ops, and make teardown moves detach from the exiting namespace even if fallback naming fails. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-3-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 157 ++++++++++++++++++++++++++----- drivers/infiniband/core/nldev.c | 3 + 2 files changed, 138 insertions(+), 22 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 84158f71c613..cac1c83cede9 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -268,7 +268,7 @@ static struct notifier_block ibdev_lsm_nb = { }; static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, - struct net *net); + struct net *net, const char *fallback_pattern); /* Pointer to the RCU head at the start of the ib_port_data array */ struct ib_port_data_rcu { @@ -437,7 +437,8 @@ int ib_device_set_dim(struct ib_device *ibdev, u8 use_dim) } /* Pick a free index for the '%d' style @name pattern. */ -static int alloc_name_id(struct net *net, const char *name) +static int __alloc_name_id(struct net *net, const char *name, + const struct ib_device *skip) { struct ib_device *device; unsigned long index; @@ -450,6 +451,8 @@ static int alloc_name_id(struct net *net, const char *name) xa_for_each (&devices, index, device) { char buf[IB_DEVICE_NAME_MAX]; + if (device == skip) + continue; if (sscanf(dev_name(&device->dev), name, &i) != 1) continue; if (i < 0 || i >= INT_MAX) @@ -469,6 +472,11 @@ static int alloc_name_id(struct net *net, const char *name) return rc; } +static int alloc_name_id(struct net *net, const char *name) +{ + return __alloc_name_id(net, name, NULL); +} + static int alloc_name(struct ib_device *ibdev, const char *name) { int id; @@ -1160,8 +1168,17 @@ static void rdma_dev_exit_net(struct net *net) /* * If the real device is in the NS then move it back to init. + * Provide a fallback pattern so a name conflict in init_net + * cannot make the teardown move fail. */ - rdma_dev_change_netns(dev, net, &init_net); + if (net_eq(net, read_pnet(&dev->coredev.rdma_net))) { + ret = rdma_dev_change_netns(dev, net, &init_net, + "ibdev%d"); + if (ret && ret != -ENODEV) + WARN(1, + "Failed to move RDMA device %s to init_net on netns exit: %d\n", + dev_name(&dev->dev), ret); + } put_device(&dev->dev); down_read(&devices_rwsem); @@ -1680,14 +1697,71 @@ void ib_unregister_device_queued(struct ib_device *ib_dev) } EXPORT_SYMBOL(ib_unregister_device_queued); +static bool rdma_dev_name_in_netns(struct ib_device *skip, struct net *net, + const char *name) +{ + struct ib_device *device; + unsigned long index; + + lockdep_assert_held_write(&devices_rwsem); + + xa_for_each(&devices, index, device) + if (device != skip && + !strcmp(name, dev_name(&device->dev))) + return true; + + return false; +} + +/* + * Choose the name @device should use in net namespace @net: keep the current + * name when it is free, otherwise use a trusted '%d' @fallback_pattern + * (namespace teardown) to pick a free index. The caller must hold the write + * side of devices_rwsem. + */ +static int rdma_dev_pick_netns_name(struct ib_device *device, struct net *net, + const char *fallback_pattern, + char *buf, size_t buf_len, + const char **new_name) +{ + int id; + + lockdep_assert_held_write(&devices_rwsem); + + if (!rdma_dev_name_in_netns(device, net, dev_name(&device->dev))) { + *new_name = dev_name(&device->dev); + return 0; + } + + if (!fallback_pattern) + return -EEXIST; + + snprintf(buf, buf_len, "ibdev%u", device->index); + if (!rdma_dev_name_in_netns(device, net, buf)) { + *new_name = buf; + return 0; + } + + id = __alloc_name_id(net, fallback_pattern, device); + if (id < 0) + return id; + snprintf(buf, buf_len, fallback_pattern, id); + *new_name = buf; + return 0; +} + /* * The caller must pass in a device that has the kref held and the refcount * released. If the device is in cur_net and still registered then it is moved * into net. + * + * Naming rules are handled by rdma_dev_pick_netns_name(). */ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, - struct net *net) + struct net *net, const char *fallback_pattern) { + char buf[IB_DEVICE_NAME_MAX]; + const char *new_name; int ret2 = -EINVAL; int ret; @@ -1704,31 +1778,64 @@ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, goto out; } + if (!fallback_pattern) { + /* + * Reject a predictable name conflict before tearing anything + * down, so a doomed user move does not disable a live device. + */ + down_write(&devices_rwsem); + ret = rdma_dev_pick_netns_name(device, net, fallback_pattern, + buf, sizeof(buf), &new_name); + up_write(&devices_rwsem); + if (ret) + goto out; + } + kobject_uevent(&device->dev.kobj, KOBJ_REMOVE); disable_device(device); /* - * At this point no one can be using the device, so it is safe to - * change the namespace. + * Recompute the destination name under the write side of devices_rwsem + * now that the device is disabled, closing races with a concurrent + * registration or rename, then publish the new namespace at the sysfs + * level. */ - write_pnet(&device->coredev.rdma_net, net); - - down_read(&devices_rwsem); - /* - * Currently rdma devices are system wide unique. So the device name - * is guaranteed free in the new namespace. Publish the new namespace - * at the sysfs level. - */ - ret = device_rename(&device->dev, dev_name(&device->dev)); - up_read(&devices_rwsem); + down_write(&devices_rwsem); + ret = rdma_dev_pick_netns_name(device, net, fallback_pattern, buf, + sizeof(buf), &new_name); if (ret) { - dev_warn(&device->dev, - "%s: Couldn't rename device after namespace change\n", - __func__); - /* Try and put things back and re-enable the device */ - write_pnet(&device->coredev.rdma_net, cur_net); + if (fallback_pattern) { + WARN(1, + "%s: failed to pick device name during namespace teardown: %d\n", + __func__, ret); + write_pnet(&device->coredev.rdma_net, net); + ret = 0; + } + goto rename_done; } + write_pnet(&device->coredev.rdma_net, net); + ret = device_rename(&device->dev, new_name); + if (ret) { + if (fallback_pattern) { + WARN(1, + "%s: failed to rename device during namespace teardown: %d\n", + __func__, ret); + ret = 0; + } else { + dev_warn(&device->dev, + "%s: Couldn't rename device after namespace change\n", + __func__); + /* Try and put things back and re-enable the device */ + write_pnet(&device->coredev.rdma_net, cur_net); + } + } else { + strscpy(device->name, dev_name(&device->dev), + IB_DEVICE_NAME_MAX); + } +rename_done: + up_write(&devices_rwsem); + ret2 = enable_device_and_get(device); if (ret2) { /* @@ -1766,6 +1873,12 @@ int ib_device_set_netns_put(struct sk_buff *skb, goto ns_err; } + /* Moving a device to the namespace it already lives in is a no-op. */ + if (net_eq(net, read_pnet(&dev->coredev.rdma_net))) { + ret = 0; + goto ns_err; + } + /* * All the ib_clients, including uverbs, are reset when the namespace is * changed and this cannot be blocked waiting for userspace to do @@ -1778,7 +1891,7 @@ int ib_device_set_netns_put(struct sk_buff *skb, get_device(&dev->dev); ib_device_put(dev); - ret = rdma_dev_change_netns(dev, current->nsproxy->net_ns, net); + ret = rdma_dev_change_netns(dev, current->nsproxy->net_ns, net, NULL); put_device(&dev->dev); put_net(net); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index b4bae92c5c4e..936ca5867eee 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1204,6 +1204,9 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); err = ib_device_set_netns_put(skb, device, ns_fd); + if (err == -EEXIST) + NL_SET_ERR_MSG(extack, + "Device name already exists in the target net namespace"); goto put_done; } From 8fc673bebd07ce0d550987ac047dabe72f05d5d9 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:04 +0200 Subject: [PATCH 107/160] RDMA/core: Support renaming a device when changing its net namespace Allow namespace moves to request a destination device name. Keep requested names on the same literal-name path as the existing RDMA rename operation, and keep teardown fallback naming on the trusted kernel-controlled path. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-4-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/core_priv.h | 2 +- drivers/infiniband/core/device.c | 48 ++++++++++++++++++++--------- drivers/infiniband/core/nldev.c | 2 +- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index 19104c542b27..3bd5bb7135a3 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -356,7 +356,7 @@ void ib_port_unregister_client_groups(struct ib_device *ibdev, u32 port_num, const struct attribute_group **groups); int ib_device_set_netns_put(struct sk_buff *skb, - struct ib_device *dev, u32 ns_fd); + struct ib_device *dev, u32 ns_fd, const char *name); int rdma_nl_net_init(struct rdma_dev_net *rnet); void rdma_nl_net_exit(struct rdma_dev_net *rnet); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index cac1c83cede9..88b74e31d1bc 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -268,7 +268,8 @@ static struct notifier_block ibdev_lsm_nb = { }; static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, - struct net *net, const char *fallback_pattern); + struct net *net, const char *requested_name, + const char *fallback_pattern); /* Pointer to the RCU head at the start of the ib_port_data array */ struct ib_port_data_rcu { @@ -1173,7 +1174,7 @@ static void rdma_dev_exit_net(struct net *net) */ if (net_eq(net, read_pnet(&dev->coredev.rdma_net))) { ret = rdma_dev_change_netns(dev, net, &init_net, - "ibdev%d"); + NULL, "ibdev%d"); if (ret && ret != -ENODEV) WARN(1, "Failed to move RDMA device %s to init_net on netns exit: %d\n", @@ -1714,12 +1715,13 @@ static bool rdma_dev_name_in_netns(struct ib_device *skip, struct net *net, } /* - * Choose the name @device should use in net namespace @net: keep the current - * name when it is free, otherwise use a trusted '%d' @fallback_pattern - * (namespace teardown) to pick a free index. The caller must hold the write - * side of devices_rwsem. + * Choose the name @device should use in net namespace @net. @requested_name + * is used as a literal device name when set. Otherwise keep the current name + * when it is free, or use a trusted '%d' @fallback_pattern for teardown. The + * caller must hold the write side of devices_rwsem. */ static int rdma_dev_pick_netns_name(struct ib_device *device, struct net *net, + const char *requested_name, const char *fallback_pattern, char *buf, size_t buf_len, const char **new_name) @@ -1728,6 +1730,15 @@ static int rdma_dev_pick_netns_name(struct ib_device *device, struct net *net, lockdep_assert_held_write(&devices_rwsem); + if (requested_name) { + if (!rdma_dev_name_in_netns(device, net, requested_name)) { + *new_name = requested_name; + return 0; + } + + return -EEXIST; + } + if (!rdma_dev_name_in_netns(device, net, dev_name(&device->dev))) { *new_name = dev_name(&device->dev); return 0; @@ -1758,7 +1769,8 @@ static int rdma_dev_pick_netns_name(struct ib_device *device, struct net *net, * Naming rules are handled by rdma_dev_pick_netns_name(). */ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, - struct net *net, const char *fallback_pattern) + struct net *net, const char *requested_name, + const char *fallback_pattern) { char buf[IB_DEVICE_NAME_MAX]; const char *new_name; @@ -1784,8 +1796,9 @@ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, * down, so a doomed user move does not disable a live device. */ down_write(&devices_rwsem); - ret = rdma_dev_pick_netns_name(device, net, fallback_pattern, - buf, sizeof(buf), &new_name); + ret = rdma_dev_pick_netns_name(device, net, requested_name, + fallback_pattern, buf, + sizeof(buf), &new_name); up_write(&devices_rwsem); if (ret) goto out; @@ -1801,8 +1814,9 @@ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, * level. */ down_write(&devices_rwsem); - ret = rdma_dev_pick_netns_name(device, net, fallback_pattern, buf, - sizeof(buf), &new_name); + ret = rdma_dev_pick_netns_name(device, net, requested_name, + fallback_pattern, buf, sizeof(buf), + &new_name); if (ret) { if (fallback_pattern) { WARN(1, @@ -1857,7 +1871,7 @@ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, } int ib_device_set_netns_put(struct sk_buff *skb, - struct ib_device *dev, u32 ns_fd) + struct ib_device *dev, u32 ns_fd, const char *name) { struct net *net; int ret; @@ -1873,9 +1887,12 @@ int ib_device_set_netns_put(struct sk_buff *skb, goto ns_err; } - /* Moving a device to the namespace it already lives in is a no-op. */ + /* + * Moving a device to the namespace it already lives in is a no-op; a + * supplied name still renames it in place. + */ if (net_eq(net, read_pnet(&dev->coredev.rdma_net))) { - ret = 0; + ret = name ? ib_device_rename(dev, name) : 0; goto ns_err; } @@ -1891,7 +1908,8 @@ int ib_device_set_netns_put(struct sk_buff *skb, get_device(&dev->dev); ib_device_put(dev); - ret = rdma_dev_change_netns(dev, current->nsproxy->net_ns, net, NULL); + ret = rdma_dev_change_netns(dev, current->nsproxy->net_ns, net, name, + NULL); put_device(&dev->dev); put_net(net); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 936ca5867eee..a68d992530e3 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1203,7 +1203,7 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, u32 ns_fd; ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); - err = ib_device_set_netns_put(skb, device, ns_fd); + err = ib_device_set_netns_put(skb, device, ns_fd, NULL); if (err == -EEXIST) NL_SET_ERR_MSG(extack, "Device name already exists in the target net namespace"); From e11553414e1e90f78ec0bae7a6f92457d606eda3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:05 +0200 Subject: [PATCH 108/160] RDMA/nldev: Report net namespace move errors through extack Thread extack through the existing net namespace move helper and report the main failure reasons from the core path. Keep the existing move UAPI shape unchanged. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-5-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/core_priv.h | 3 ++- drivers/infiniband/core/device.c | 24 ++++++++++++++++++++++-- drivers/infiniband/core/nldev.c | 6 ++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index 3bd5bb7135a3..aaf330b0d333 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -356,7 +356,8 @@ void ib_port_unregister_client_groups(struct ib_device *ibdev, u32 port_num, const struct attribute_group **groups); int ib_device_set_netns_put(struct sk_buff *skb, - struct ib_device *dev, u32 ns_fd, const char *name); + struct ib_device *dev, u32 ns_fd, const char *name, + struct netlink_ext_ack *extack); int rdma_nl_net_init(struct rdma_dev_net *rnet); void rdma_nl_net_exit(struct rdma_dev_net *rnet); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 88b74e31d1bc..f91e75211d52 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1871,18 +1871,22 @@ static int rdma_dev_change_netns(struct ib_device *device, struct net *cur_net, } int ib_device_set_netns_put(struct sk_buff *skb, - struct ib_device *dev, u32 ns_fd, const char *name) + struct ib_device *dev, u32 ns_fd, const char *name, + struct netlink_ext_ack *extack) { struct net *net; int ret; net = get_net_ns_by_fd(ns_fd); if (IS_ERR(net)) { + NL_SET_ERR_MSG(extack, "Invalid target net namespace fd"); ret = PTR_ERR(net); goto net_err; } if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + NL_SET_ERR_MSG(extack, + "Missing CAP_NET_ADMIN in the target net namespace"); ret = -EPERM; goto ns_err; } @@ -1893,6 +1897,10 @@ int ib_device_set_netns_put(struct sk_buff *skb, */ if (net_eq(net, read_pnet(&dev->coredev.rdma_net))) { ret = name ? ib_device_rename(dev, name) : 0; + + if (ret == -EEXIST) + NL_SET_ERR_MSG(extack, + "Device name already exists in the target net namespace"); goto ns_err; } @@ -1901,7 +1909,16 @@ int ib_device_set_netns_put(struct sk_buff *skb, * changed and this cannot be blocked waiting for userspace to do * something, so disassociation is mandatory. */ - if (!dev->ops.disassociate_ucontext || ib_devices_shared_netns) { + if (ib_devices_shared_netns) { + NL_SET_ERR_MSG(extack, + "Cannot change net namespace of RDMA device in shared netns mode"); + ret = -EOPNOTSUPP; + goto ns_err; + } + + if (!dev->ops.disassociate_ucontext) { + NL_SET_ERR_MSG(extack, + "Device does not support namespace changes (no disassociate support)"); ret = -EOPNOTSUPP; goto ns_err; } @@ -1911,6 +1928,9 @@ int ib_device_set_netns_put(struct sk_buff *skb, ret = rdma_dev_change_netns(dev, current->nsproxy->net_ns, net, name, NULL); put_device(&dev->dev); + if (ret == -EEXIST) + NL_SET_ERR_MSG(extack, + "Device name already exists in the target net namespace"); put_net(net); return ret; diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index a68d992530e3..0251695ff3ba 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1203,10 +1203,8 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, u32 ns_fd; ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); - err = ib_device_set_netns_put(skb, device, ns_fd, NULL); - if (err == -EEXIST) - NL_SET_ERR_MSG(extack, - "Device name already exists in the target net namespace"); + err = ib_device_set_netns_put(skb, device, ns_fd, NULL, + extack); goto put_done; } From 601f3fd97dec0525f1fca9887ca16574f81588b7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:06 +0200 Subject: [PATCH 109/160] RDMA/nldev: Allow setting the device name while changing net namespace Accept RDMA_NLDEV_ATTR_DEV_NAME together with RDMA_NLDEV_NET_NS_FD so a netlink move can rename the device in the destination namespace. Keep the name semantics aligned with the existing RDMA rename path. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-6-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 6 ++++++ drivers/infiniband/core/nldev.c | 27 ++++++++++++++++++--------- include/uapi/rdma/rdma_netlink.h | 5 ++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index f91e75211d52..14fcdc07f6e3 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1901,6 +1901,9 @@ int ib_device_set_netns_put(struct sk_buff *skb, if (ret == -EEXIST) NL_SET_ERR_MSG(extack, "Device name already exists in the target net namespace"); + else if (ret == -EINVAL && name) + NL_SET_ERR_MSG(extack, + "Unable to use requested device name in the target net namespace"); goto ns_err; } @@ -1931,6 +1934,9 @@ int ib_device_set_netns_put(struct sk_buff *skb, if (ret == -EEXIST) NL_SET_ERR_MSG(extack, "Device name already exists in the target net namespace"); + else if (ret == -EINVAL && name) + NL_SET_ERR_MSG(extack, + "Unable to use requested device name in the target net namespace"); put_net(net); return ret; diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 0251695ff3ba..a4014a230639 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1186,6 +1186,24 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, if (!device) return -EINVAL; + if (tb[RDMA_NLDEV_NET_NS_FD]) { + char name[IB_DEVICE_NAME_MAX] = {}; + u32 ns_fd; + + if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) { + nla_strscpy(name, tb[RDMA_NLDEV_ATTR_DEV_NAME], + IB_DEVICE_NAME_MAX); + if (strlen(name) == 0) { + err = -EINVAL; + goto done; + } + } + ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); + err = ib_device_set_netns_put(skb, device, ns_fd, + name[0] ? name : NULL, extack); + goto put_done; + } + if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) { char name[IB_DEVICE_NAME_MAX] = {}; @@ -1199,15 +1217,6 @@ static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, goto done; } - if (tb[RDMA_NLDEV_NET_NS_FD]) { - u32 ns_fd; - - ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); - err = ib_device_set_netns_put(skb, device, ns_fd, NULL, - extack); - goto put_done; - } - if (tb[RDMA_NLDEV_ATTR_DEV_DIM]) { u8 use_dim; diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 3af946ecbac3..ee11c3bbbae2 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -516,7 +516,10 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_DEV_PROTOCOL, /* string */ /* - * File descriptor handle of the net namespace object + * File descriptor handle of the net namespace object. May be combined + * with RDMA_NLDEV_ATTR_DEV_NAME (a literal device name) to also rename + * the device in the destination namespace; the move fails with -EEXIST + * if that name is already taken there. */ RDMA_NLDEV_NET_NS_FD, /* u32 */ /* From 47b9ef3c0013e1cc6ca7b342c8d28ddb9f35cb12 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:07 +0200 Subject: [PATCH 110/160] net/smc: Look up the pnetid ib device within the net namespace Scope smc_pnet_find_ib() to the caller's net namespace so pnetid setup cannot bind to a same-named RDMA device from another namespace once names become per-netns. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-7-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- net/smc/smc_pnet.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index 63e286e2dfaa..ff9c9c35cc2f 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -304,13 +304,18 @@ static bool smc_pnetid_valid(const char *pnet_name, char *pnetid) return true; } -/* Find an infiniband device by a given name. The device might not exist. */ -static struct smc_ib_device *smc_pnet_find_ib(char *ib_name) +/* + * Find an infiniband device by a given name, restricted to the devices + * accessible from @net. The device might not exist. + */ +static struct smc_ib_device *smc_pnet_find_ib(struct net *net, char *ib_name) { struct smc_ib_device *ibdev; mutex_lock(&smc_ib_devices.mutex); list_for_each_entry(ibdev, &smc_ib_devices.list, list) { + if (!rdma_dev_access_netns(ibdev->ibdev, net)) + continue; if (!strncmp(ibdev->ibdev->name, ib_name, sizeof(ibdev->ibdev->name)) || (ibdev->ibdev->dev.parent && @@ -408,8 +413,8 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net, return rc; } -static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name, - u8 ib_port, char *pnet_name) +static int smc_pnet_add_ib(struct smc_pnettable *pnettable, struct net *net, + char *ib_name, u8 ib_port, char *pnet_name) { struct smc_pnetentry *tmp_pe, *new_pe; struct smc_ib_device *ib_dev; @@ -419,7 +424,7 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name, bool new_ibdev; /* try to apply the pnetid to active devices */ - ib_dev = smc_pnet_find_ib(ib_name); + ib_dev = smc_pnet_find_ib(net, ib_name); if (ib_dev) { ibdev_applied = smc_pnet_apply_ib(ib_dev, ib_port, pnet_name); if (ibdev_applied) @@ -518,7 +523,7 @@ static int smc_pnet_enter(struct net *net, struct nlattr *tb[]) if (ibport < 1 || ibport > SMC_MAX_PORTS) goto error; } - rc = smc_pnet_add_ib(pnettable, string, ibport, pnet_name); + rc = smc_pnet_add_ib(pnettable, net, string, ibport, pnet_name); if (!rc) new_ibdev = true; else if (rc != -EEXIST) @@ -1170,6 +1175,9 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port) struct smc_net *sn; int rc = -ENOENT; + if (!rdma_dev_access_netns(smcibdev->ibdev, &init_net)) + return -ENOENT; + /* get pnettable for init namespace */ sn = net_generic(&init_net, smc_net_id); pnettable = &sn->pnettable; From e9153a2671194ed5c86d5432f0207d9320a8320c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:08 +0200 Subject: [PATCH 111/160] RDMA/srp: Make the SRP sysfs class net namespace aware Tag srp_class by the RDMA device's net namespace so SRP hosts derived from same-named RDMA devices can coexist across namespaces. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-8-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/srp/ib_srp.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 0caebbc2810f..2fc8e133c70f 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -3189,10 +3189,24 @@ static struct attribute *srp_class_attrs[]; ATTRIBUTE_GROUPS(srp_class); +/* + * SRP hosts are named after their ib device, so tag the class by the ib + * device's net namespace. + */ +static const struct ns_common *srp_net_namespace(const struct device *dev) +{ + struct srp_host *host = container_of(dev, struct srp_host, dev); + struct net *net = rdma_dev_net(host->srp_dev->dev); + + return net ? to_ns_common(net) : NULL; +} + static struct class srp_class = { .name = "infiniband_srp", .dev_groups = srp_class_groups, - .dev_release = srp_release_dev + .dev_release = srp_release_dev, + .ns_type = &net_ns_type_operations, + .namespace = srp_net_namespace, }; /** From c4ef67e5aa3e08637dcd6c3fd325c0938a428058 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:09 +0200 Subject: [PATCH 112/160] RDMA/cgroup: Disambiguate devices across net namespaces RDMA device names are unique only within a network namespace, but an RDMA cgroup can account resources for devices from multiple namespaces. Duplicate names therefore make cgroup output ambiguous and can cause limit writes to select the wrong device. Use the system-wide RDMA device index to distinguish duplicate names while preserving the existing UAPI for unique names. Reject ambiguous name-only writes with -ENOTUNIQ and expose a complete device view to administrators. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-9-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- Documentation/admin-guide/cgroup-v1/rdma.rst | 8 +++ Documentation/admin-guide/cgroup-v2.rst | 15 ++++- drivers/infiniband/core/cgroup.c | 1 + include/linux/cgroup_rdma.h | 1 + kernel/cgroup/rdma.c | 71 ++++++++++++++++---- 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v1/rdma.rst b/Documentation/admin-guide/cgroup-v1/rdma.rst index e69369b7252e..8d0c3a796ee3 100644 --- a/Documentation/admin-guide/cgroup-v1/rdma.rst +++ b/Documentation/admin-guide/cgroup-v1/rdma.rst @@ -90,6 +90,13 @@ Following resources can be accounted by rdma controller. hca_object Maximum number of HCA Objects ========== ============================= +RDMA devices from all network namespaces are listed. Each line starts with +the device name. If more than one device has the same name, ``index=N`` +follows the name, where ``N`` is the system-wide RDMA device index, unique +among registered devices. When configuring a limit, the index is optional +for a globally unique name and required for a duplicate name. A write without +the required index fails with ``-ENOTUNIQ``. + 2. Usage Examples ================= @@ -97,6 +104,7 @@ Following resources can be accounted by rdma controller. echo mlx4_0 hca_handle=2 hca_object=2000 > /sys/fs/cgroup/rdma/1/rdma.max echo ocrdma1 hca_handle=3 > /sys/fs/cgroup/rdma/2/rdma.max + echo "rxe0 index=5 hca_handle=2" > /sys/fs/cgroup/rdma/3/rdma.max (b) Query resource limit:: diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 993446ab66d0..df74d554d2cd 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2752,6 +2752,11 @@ RDMA The "rdma" controller regulates the distribution and accounting of RDMA resources. +RDMA devices from all network namespaces are listed. Each line starts with +the device name. If more than one device has the same name, ``index=N`` +follows the name, where ``N`` is the system-wide RDMA device index, unique +among registered devices. + RDMA Interface Files ~~~~~~~~~~~~~~~~~~~~ @@ -2760,7 +2765,11 @@ RDMA Interface Files except root that describes current configured resource limit for a RDMA/IB device. - Lines are keyed by device name and are not ordered. + Lines are keyed by device name and are not ordered. A write may + include ``index=N`` after the device name. The index is optional + when the name is globally unique. If multiple devices have that + name, the index is required and a write without it fails with + ``-ENOTUNIQ``. Each line contains space separated resource name and its configured limit that can be distributed. @@ -2776,6 +2785,10 @@ RDMA Interface Files mlx4_0 hca_handle=2 hca_object=2000 ocrdma1 hca_handle=3 hca_object=max + For devices with duplicate names, select the device by index:: + + echo "rxe0 index=5 hca_handle=2" > rdma.max + rdma.current A read-only file that describes current resource usage. It exists for all the cgroup except root. diff --git a/drivers/infiniband/core/cgroup.c b/drivers/infiniband/core/cgroup.c index 1f037fe01450..8611b4e32cfb 100644 --- a/drivers/infiniband/core/cgroup.c +++ b/drivers/infiniband/core/cgroup.c @@ -17,6 +17,7 @@ void ib_device_register_rdmacg(struct ib_device *device) { device->cg_device.name = device->name; + device->cg_device.index = device->index; rdmacg_register_device(&device->cg_device); } diff --git a/include/linux/cgroup_rdma.h b/include/linux/cgroup_rdma.h index 404e746552ca..9a5c9ee728e7 100644 --- a/include/linux/cgroup_rdma.h +++ b/include/linux/cgroup_rdma.h @@ -34,6 +34,7 @@ struct rdmacg_device { struct list_head dev_node; struct list_head rpools; char *name; + u32 index; }; /* diff --git a/kernel/cgroup/rdma.c b/kernel/cgroup/rdma.c index 5e82a03b3270..9489f3df0bf3 100644 --- a/kernel/cgroup/rdma.c +++ b/kernel/cgroup/rdma.c @@ -19,6 +19,7 @@ #define RDMACG_MAX_STR "max" enum rdmacg_limit_tokens { + RDMACG_DEVICE_INDEX, RDMACG_HCA_HANDLE_VAL, RDMACG_HCA_HANDLE_MAX, RDMACG_HCA_OBJECT_VAL, @@ -27,6 +28,7 @@ enum rdmacg_limit_tokens { }; static const match_table_t rdmacg_limit_tokens = { + { RDMACG_DEVICE_INDEX, "index=%u" }, { RDMACG_HCA_HANDLE_VAL, "hca_handle=%d" }, { RDMACG_HCA_HANDLE_MAX, "hca_handle=max" }, { RDMACG_HCA_OBJECT_VAL, "hca_object=%d" }, @@ -464,17 +466,53 @@ void rdmacg_unregister_device(struct rdmacg_device *device) } EXPORT_SYMBOL(rdmacg_unregister_device); -static struct rdmacg_device *rdmacg_get_device_locked(const char *name) +static struct rdmacg_device * +rdmacg_get_device_locked(const char *name, bool has_index, u32 index) { + struct rdmacg_device *match = NULL; struct rdmacg_device *device; lockdep_assert_held(&rdmacg_mutex); - list_for_each_entry(device, &rdmacg_devices, dev_node) - if (!strcmp(name, device->name)) - return device; + list_for_each_entry(device, &rdmacg_devices, dev_node) { + if (strcmp(name, device->name)) + continue; - return NULL; + if (has_index) { + if (device->index == index) + return device; + continue; + } + + if (match) + return ERR_PTR(-ENOTUNIQ); + match = device; + } + + return match ?: ERR_PTR(-ENODEV); +} + +static bool +rdmacg_device_name_unique_locked(const struct rdmacg_device *device) +{ + struct rdmacg_device *other; + + lockdep_assert_held(&rdmacg_mutex); + + list_for_each_entry(other, &rdmacg_devices, dev_node) + if (other != device && !strcmp(other->name, device->name)) + return false; + + return true; +} + +static void rdmacg_print_device_key(struct seq_file *sf, + const struct rdmacg_device *device) +{ + seq_puts(sf, device->name); + if (!rdmacg_device_name_unique_locked(device)) + seq_printf(sf, " index=%u", device->index); + seq_putc(sf, ' '); } static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, @@ -488,6 +526,8 @@ static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, char *p; int *new_limits; unsigned long enables = 0; + u32 dev_index = 0; + bool has_index = false; int i = 0, ret = 0; /* extract the device name first */ @@ -503,7 +543,7 @@ static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, goto err; } - /* parse resource limit tokens */ + /* parse the optional device index and resource limit tokens */ while ((p = strsep(&options, " \t\n"))) { substring_t args[MAX_OPT_ARGS]; int tok, intval; @@ -513,6 +553,13 @@ static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, tok = match_token(p, rdmacg_limit_tokens, args); switch (tok) { + case RDMACG_DEVICE_INDEX: + if (has_index || match_uint(&args[0], &dev_index)) { + ret = -EINVAL; + goto parse_err; + } + has_index = true; + break; case RDMACG_HCA_HANDLE_VAL: if (match_int(&args[0], &intval) || intval < 0) { ret = -EINVAL; @@ -546,9 +593,9 @@ static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, /* acquire lock to synchronize with hot plug devices */ mutex_lock(&rdmacg_mutex); - device = rdmacg_get_device_locked(dev_name); - if (!device) { - ret = -ENODEV; + device = rdmacg_get_device_locked(dev_name, has_index, dev_index); + if (IS_ERR(device)) { + ret = PTR_ERR(device); goto dev_err; } @@ -626,7 +673,7 @@ static int rdmacg_resource_read(struct seq_file *sf, void *v) mutex_lock(&rdmacg_mutex); list_for_each_entry(device, &rdmacg_devices, dev_node) { - seq_printf(sf, "%s ", device->name); + rdmacg_print_device_key(sf, device); rpool = find_cg_rpool_locked(cg, device); print_rpool_values(sf, rpool); @@ -650,7 +697,7 @@ static int rdmacg_events_show(struct seq_file *sf, void *v) list_for_each_entry(device, &rdmacg_devices, dev_node) { rpool = find_cg_rpool_locked(cg, device); - seq_printf(sf, "%s ", device->name); + rdmacg_print_device_key(sf, device); for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { seq_printf(sf, "%s.max=%llu %s.alloc_fail=%llu", rdmacg_resource_names[i], @@ -679,7 +726,7 @@ static int rdmacg_events_local_show(struct seq_file *sf, void *v) list_for_each_entry(device, &rdmacg_devices, dev_node) { rpool = find_cg_rpool_locked(cg, device); - seq_printf(sf, "%s ", device->name); + rdmacg_print_device_key(sf, device); for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { seq_printf(sf, "%s.max=%llu %s.alloc_fail=%llu", rdmacg_resource_names[i], From bca2c9d781bccb5103a1b22de0e3fc103fa5131a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:10 +0200 Subject: [PATCH 113/160] RDMA/cma: Document that CM configfs cannot be net namespace scoped Document the rdma_cm configfs limitation: configfs is global, so same-named RDMA devices in different net namespaces cannot both be represented there. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-10-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- Documentation/ABI/testing/configfs-rdma_cm | 4 ++++ drivers/infiniband/core/cma_configfs.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/ABI/testing/configfs-rdma_cm b/Documentation/ABI/testing/configfs-rdma_cm index 74f9506f42e7..739f7b6a1259 100644 --- a/Documentation/ABI/testing/configfs-rdma_cm +++ b/Documentation/ABI/testing/configfs-rdma_cm @@ -12,6 +12,10 @@ Description: Interface is used to configure RDMA-cable HCAs in respect to for this HCA has to be created: mkdir -p /config/rdma_cm/ + Note: configfs has no network namespace support, so this + interface cannot represent two devices that share a name in + different network namespaces (possible in exclusive netns mode). + What: /config/rdma_cm//ports//default_roce_mode Date: November 29, 2015 diff --git a/drivers/infiniband/core/cma_configfs.c b/drivers/infiniband/core/cma_configfs.c index 891e52afb8f4..c389d4e37b6b 100644 --- a/drivers/infiniband/core/cma_configfs.c +++ b/drivers/infiniband/core/cma_configfs.c @@ -65,6 +65,10 @@ static struct cma_dev_port_group *to_dev_port_group(struct config_item *item) return container_of(group, struct cma_dev_port_group, group); } +/* + * configfs is not net namespace aware, so a name shared by devices in + * different namespaces resolves to the first match here. + */ static bool filter_by_name(struct ib_device *ib_dev, void *cookie) { return !strcmp(dev_name(&ib_dev->dev), cookie); From 4ad1fe8e25e41a45f52705c1624f362684423b4d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:11 +0200 Subject: [PATCH 114/160] RDMA/core: Document the SELinux ibendport net namespace limitation Document that SELinux ibendport labels use a global (device name, port) key, so same-named RDMA devices in different net namespaces share a label. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-11-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/security.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 9af31d1d9d70..a82c46965416 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -700,6 +700,12 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, if (qp_type != IB_QPT_SMI) return 0; + /* + * SELinux labels an endport by (device name, port) from a global + * policy. If devices in different net namespaces share a name, they get + * the same label; distinguishing them would need net namespace support + * in the policy language and tooling. + */ spin_lock(&mad_agent_list_lock); ret = security_ib_endport_manage_subnet(agent->security, dev_name(&agent->device->dev), From 625ca0d94cb13ac6f1109e0a13d2de5517a73c10 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:12 +0200 Subject: [PATCH 115/160] RDMA/core: Make device names unique per net namespace Use rdma_dev_access_netns() to scope RDMA device name lookup and "%d" name allocation to the relevant net namespace. Keep shared mode and CONFIG_NET_NS=n behaviour system-wide. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-12-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/device.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 14fcdc07f6e3..3d15489a011c 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -129,7 +129,7 @@ static DECLARE_RWSEM(rdma_nets_rwsem); bool ib_devices_shared_netns = true; module_param_named(netns_mode, ib_devices_shared_netns, bool, 0444); MODULE_PARM_DESC(netns_mode, - "Share device among net namespaces; default=1 (shared)"); + "Share device among net namespaces; default=1 (shared). In exclusive mode device names are unique per net namespace"); /** * rdma_dev_access_netns() - Return whether an rdma device can be accessed * from a specified net namespace or not. @@ -359,7 +359,8 @@ static struct ib_device *__ib_device_get_by_name(const char *name, unsigned long index; xa_for_each (&devices, index, device) - if (!strcmp(name, dev_name(&device->dev))) + if (rdma_dev_access_netns(device, net) && + !strcmp(name, dev_name(&device->dev))) return device; return NULL; @@ -437,7 +438,11 @@ int ib_device_set_dim(struct ib_device *ibdev, u8 use_dim) return 0; } -/* Pick a free index for the '%d' style @name pattern. */ +/* + * Pick a free index for the '%d' style @name pattern within net namespace + * @net. Returns the index on success or a negative errno. The caller builds + * the final unique device name from the returned index. + */ static int __alloc_name_id(struct net *net, const char *name, const struct ib_device *skip) { @@ -452,7 +457,7 @@ static int __alloc_name_id(struct net *net, const char *name, xa_for_each (&devices, index, device) { char buf[IB_DEVICE_NAME_MAX]; - if (device == skip) + if (device == skip || !rdma_dev_access_netns(device, net)) continue; if (sscanf(dev_name(&device->dev), name, &i) != 1) continue; @@ -1233,7 +1238,8 @@ static __net_init int rdma_dev_init_net(struct net *net) } /* - * Assign the unique string device name and the unique device index. This is + * Assign the unique string device name and the unique device index. The device + * name is unique within the net namespace the device is assigned to. This is * undone by ib_dealloc_device. */ static int assign_name(struct ib_device *device, const char *name) @@ -1416,8 +1422,9 @@ static void ib_device_notify_register(struct ib_device *device) /** * ib_register_device - Register an IB device with IB core * @device: Device to register - * @name: unique string device name. This may include a '%' which will - * cause a unique index to be added to the passed device name. + * @name: device name, unique within the device's net namespace. This may + * include a '%' which will cause a unique index to be added to the + * passed device name. * @dma_device: pointer to a DMA-capable device. If %NULL, then the IB * device will be used. In this case the caller should fully * setup the ibdev for DMA. This usually means using dma_virt_ops. @@ -1708,6 +1715,7 @@ static bool rdma_dev_name_in_netns(struct ib_device *skip, struct net *net, xa_for_each(&devices, index, device) if (device != skip && + rdma_dev_access_netns(device, net) && !strcmp(name, dev_name(&device->dev))) return true; From 22fe73aef39f274417f262aee3bc6c907e5f75ae Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:13 +0200 Subject: [PATCH 116/160] RDMA/rxe: Allow queue VMAs to outlive ucontexts Prepare queue mappings for asynchronous ucontext disassociation during device disable. Rely on the VMA page references to preserve mapped memory until the final unmap. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-13-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_mmap.c | 35 ++-------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mmap.c b/drivers/infiniband/sw/rxe/rxe_mmap.c index 7f723a2f3700..a4ead89ccbd3 100644 --- a/drivers/infiniband/sw/rxe/rxe_mmap.c +++ b/drivers/infiniband/sw/rxe/rxe_mmap.c @@ -11,7 +11,6 @@ #include "rxe.h" #include "rxe_loc.h" -#include "rxe_queue.h" void rxe_mmap_release(struct kref *ref) { @@ -30,29 +29,6 @@ void rxe_mmap_release(struct kref *ref) kfree(ip); } -/* - * open and close keep track of how many times the memory region is mapped, - * to avoid releasing it. - */ -static void rxe_vma_open(struct vm_area_struct *vma) -{ - struct rxe_mmap_info *ip = vma->vm_private_data; - - kref_get(&ip->ref); -} - -static void rxe_vma_close(struct vm_area_struct *vma) -{ - struct rxe_mmap_info *ip = vma->vm_private_data; - - kref_put(&ip->ref, rxe_mmap_release); -} - -static const struct vm_operations_struct rxe_vm_ops = { - .open = rxe_vma_open, - .close = rxe_vma_close, -}; - /** * rxe_mmap - create a new mmap region * @context: the IB user context of the process making the mmap() call @@ -106,17 +82,10 @@ int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) list_del_init(&ip->pending_mmaps); spin_unlock_bh(&rxe->pending_lock); - vma->vm_ops = &rxe_vm_ops; - vma->vm_private_data = ip; - ret = remap_vmalloc_range(vma, ip->obj, 0); - if (ret) { - vma->vm_private_data = NULL; - vma->vm_ops = NULL; - kref_put(&ip->ref, rxe_mmap_release); + kref_put(&ip->ref, rxe_mmap_release); + if (ret) rxe_dbg_dev(rxe, "err %d from remap_vmalloc_range\n", ret); - goto done; - } done: return ret; From acf9cbb06f1fad094167b7a9c06ff11b5ad0df7f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:14 +0200 Subject: [PATCH 117/160] RDMA/rxe: Implement disassociate_ucontext callback Implement an empty disassociate_ucontext() callback so the RDMA core can move rxe devices between net namespaces. The core requires this callback to reset user contexts without waiting for userspace. rxe needs no teardown here: its user-mapped queues live in reference-counted vmalloc memory (see rxe_mmap.c) that stays valid while userspace holds the mappings. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-14-jiri@resnulli.us Reviewed-by: Yanjun Zhu Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_verbs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index c8562866e21f..96c7716057fe 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -240,6 +240,10 @@ static void rxe_dealloc_ucontext(struct ib_ucontext *ibuc) rxe_err_uc(uc, "cleanup failed, err = %d\n", err); } +static void rxe_disassociate_ucontext(struct ib_ucontext *ibuc) +{ +} + /* pd */ static int rxe_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { @@ -1478,6 +1482,7 @@ static const struct ib_device_ops rxe_dev_ops = { .destroy_srq = rxe_destroy_srq, .detach_mcast = rxe_detach_mcast, .device_group = &rxe_attr_group, + .disassociate_ucontext = rxe_disassociate_ucontext, .enable_driver = rxe_enable_driver, .get_dma_mr = rxe_get_dma_mr, .get_hw_stats = rxe_ib_get_hw_stats, From ecdcc8a455b7845714b7b086a8ad229056867558 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 16 Jul 2026 15:23:15 +0200 Subject: [PATCH 118/160] RDMA/selftests: Add rxe_netns_names test Add a kselftest script that exercises per-netns RDMA device naming with RXE. Cover duplicate names across namespaces, move conflict handling, move-with-rename, and same-namespace rename requests. Signed-off-by: Jiri Pirko Link: https://patch.msgid.link/20260716132316.1495242-15-jiri@resnulli.us Signed-off-by: Leon Romanovsky --- tools/testing/selftests/rdma/Makefile | 3 +- tools/testing/selftests/rdma/config | 2 + .../testing/selftests/rdma/rxe_netns_names.sh | 334 ++++++++++++++++++ 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/rdma/rxe_netns_names.sh diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile index 07af7f15c1bf..a91c14c45006 100644 --- a/tools/testing/selftests/rdma/Makefile +++ b/tools/testing/selftests/rdma/Makefile @@ -3,6 +3,7 @@ TEST_PROGS := rxe_rping_between_netns.sh \ rxe_ipv6.sh \ rxe_socket_with_netns.sh \ rxe_test_NETDEV_UNREGISTER.sh \ - rxe_sent_rcvd_bytes.sh + rxe_sent_rcvd_bytes.sh \ + rxe_netns_names.sh include ../lib.mk diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config index 4ffb814e253b..e1ff54ec0f57 100644 --- a/tools/testing/selftests/rdma/config +++ b/tools/testing/selftests/rdma/config @@ -1,3 +1,5 @@ CONFIG_TUN CONFIG_VETH +CONFIG_DUMMY +CONFIG_NET_NS CONFIG_RDMA_RXE diff --git a/tools/testing/selftests/rdma/rxe_netns_names.sh b/tools/testing/selftests/rdma/rxe_netns_names.sh new file mode 100755 index 000000000000..f40118407f4c --- /dev/null +++ b/tools/testing/selftests/rdma/rxe_netns_names.sh @@ -0,0 +1,334 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Exercise RDMA device name handling across network namespaces. + +source "$(dirname "$0")/../kselftest/ktap_helpers.sh" + +NAME_PREFIX="rxe_netns_names_$$" +NETDEV_PREFIX="rxn$$" +NS1="${NAME_PREFIX}ns1" +NS2="${NAME_PREFIX}ns2" +RXE_A="${NAME_PREFIX}rxe_a" +RXE_B="${NAME_PREFIX}rxe_b" +RXE_SAME="${NAME_PREFIX}rxe_same" +RXE_NEW="${NAME_PREFIX}rxe_new" +DUMMY_A="${NETDEV_PREFIX}a" +DUMMY_B="${NETDEV_PREFIX}b" +OLD_MODE="" +MODE_CHANGED=0 +MODS=("dummy" "rdma_rxe") +TEST_SAME_NAMES="same RDMA device name can exist in two net namespaces" +TEST_MOVE_CONFLICT="move without rename fails on destination name conflict" +TEST_MOVE_RENAME="move then rename succeeds" +TEST_COMBINED_MOVE_RENAME="move with requested destination name succeeds" +TEST_SAME_NETNS_DUP_RENAME="same-netns rename rejects duplicate name" +TEST_TEARDOWN_RETURN="netns delete returns device to init_net and renames on conflict" + +ksft_skip() +{ + ktap_skip_all "$*" + exit "$KSFT_SKIP" +} + +fail() +{ + ktap_exit_fail_msg "$*" +} + +need_cmd() +{ + command -v "$1" >/dev/null 2>&1 || ksft_skip "missing command: $1" +} + +rdma_ns() +{ + local ns=$1 + + shift + ip netns exec "$ns" rdma "$@" +} + +rdma_dev_exists() +{ + local ns=$1 + local dev=$2 + + if [ -n "$ns" ]; then + rdma_ns "$ns" dev show "$dev" >/dev/null 2>&1 + else + rdma dev show "$dev" >/dev/null 2>&1 + fi +} + +add_dummy() +{ + local netdev=$1 + + ip link add "$netdev" type dummy || return 1 + ip link set "$netdev" up || return 1 +} + +add_rxe() +{ + local dev=$1 + local netdev=$2 + + rdma link add "$dev" type rxe netdev "$netdev" +} + +rdma_dev_on_netdev() +{ + local netdev=$1 + + rdma link show 2>/dev/null | awk -v want="$netdev" ' + { + for (i = 1; i < NF; i++) + if ($i == "netdev" && $(i + 1) == want) { + dev = $2 + sub(/\/.*/, "", dev) + print dev + exit + } + }' +} + +wait_rdma_dev_on_netdev() +{ + local netdev=$1 + local dev + local i + + for i in $(seq 1 50); do + dev=$(rdma_dev_on_netdev "$netdev") + if [ -n "$dev" ]; then + echo "$dev" + return 0 + fi + sleep 0.1 + done + + return 1 +} + +# ip link del returns after NETDEV_UNREGISTER, but rxe tears the RDMA device +# down asynchronously via ib_unregister_device_queued(). Wait until our names +# are gone. +wait_rdma_devs_gone() +{ + local i name ns + local names=("$RXE_A" "$RXE_B" "$RXE_SAME" "$RXE_NEW") + + for i in $(seq 1 50); do + local found=0 + + for name in "${names[@]}"; do + if rdma_dev_exists "" "$name"; then + found=1 + break + fi + for ns in "$NS1" "$NS2"; do + ip netns exec "$ns" true 2>/dev/null || continue + if rdma_dev_exists "$ns" "$name"; then + found=1 + break 2 + fi + done + done + + [ "$found" -eq 0 ] && return 0 + sleep 0.1 + done + + return 1 +} + +setup_devs() +{ + cleanup_devs || return 1 + + add_dummy "$DUMMY_A" || return 1 + add_dummy "$DUMMY_B" || return 1 + + add_rxe "$RXE_A" "$DUMMY_A" || return 1 + add_rxe "$RXE_B" "$DUMMY_B" || return 1 +} + +cleanup_devs() +{ + ip link del "$DUMMY_A" 2>/dev/null + ip link del "$DUMMY_B" 2>/dev/null + wait_rdma_devs_gone +} + +setup() +{ + OLD_MODE=$(rdma system show 2>/dev/null | + sed -n 's/.*netns \([^ ]*\).*/\1/p') + [ -n "$OLD_MODE" ] || ksft_skip "failed to read RDMA netns mode" + + rdma system set netns exclusive >/dev/null 2>&1 || + ksft_skip "rdma netns exclusive mode is not supported" + MODE_CHANGED=1 + + ip netns add "$NS1" || return 1 + ip netns add "$NS2" || return 1 +} + +# ip netns del returns before rdma_dev_exit_net() removes the net from +# rdma_nets. rdma_compatdev_set() returns -EBUSY until that completes, so +# retry the mode restore instead of leaving the system in exclusive mode. +restore_netns_mode() +{ + local i + + [ "$MODE_CHANGED" -eq 1 ] || return 0 + + for i in $(seq 1 50); do + if rdma system set netns "$OLD_MODE" >/dev/null 2>&1; then + MODE_CHANGED=0 + return 0 + fi + sleep 0.1 + done + + echo "warning: failed to restore RDMA netns mode to $OLD_MODE" >&2 + return 1 +} + +cleanup() +{ + cleanup_devs + + ip netns del "$NS1" 2>/dev/null + ip netns del "$NS2" 2>/dev/null + + restore_netns_mode + + for m in "${MODS[@]}"; do + modprobe -r "$m" 2>/dev/null + done +} + +rdma_supports_combined_move_rename() +{ + rdma dev help 2>&1 | grep -Eq 'netns .*name|name .*netns' +} + +[ "$(id -u)" -eq 0 ] || ksft_skip "must be run as root" +need_cmd ip +need_cmd rdma +need_cmd modprobe + +trap cleanup EXIT + +for m in "${MODS[@]}"; do + modinfo "$m" >/dev/null 2>&1 || ksft_skip "module $m not found" + modprobe "$m" || fail "failed to load $m" +done + +setup || fail "failed to create net namespaces" + +ktap_print_header +ktap_set_plan 6 + +if setup_devs && + rdma dev set "$RXE_A" netns "$NS1" && + rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" && + rdma dev set "$RXE_B" netns "$NS2" && + rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" && + rdma_dev_exists "$NS1" "$RXE_SAME" && + rdma_dev_exists "$NS2" "$RXE_SAME"; then + ktap_test_pass "$TEST_SAME_NAMES" +else + ktap_test_fail "$TEST_SAME_NAMES" +fi +cleanup_devs + +if ! setup_devs || + ! rdma dev set "$RXE_A" netns "$NS1" || + ! rdma_ns "$NS1" dev set "$RXE_A" name "$RXE_SAME" || + ! rdma dev set "$RXE_B" netns "$NS2" || + ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME"; then + ktap_test_fail "$TEST_MOVE_CONFLICT" +elif rdma_ns "$NS1" dev set "$RXE_SAME" netns "$NS2" >/dev/null 2>&1; then + ktap_test_fail "$TEST_MOVE_CONFLICT" +elif rdma_dev_exists "$NS1" "$RXE_SAME" && + rdma_dev_exists "$NS2" "$RXE_SAME"; then + ktap_test_pass "$TEST_MOVE_CONFLICT" +else + ktap_test_fail "$TEST_MOVE_CONFLICT" +fi +cleanup_devs + +if ! setup_devs; then + ktap_test_fail "$TEST_MOVE_RENAME" +elif rdma dev set "$RXE_A" netns "$NS2" && + rdma_ns "$NS2" dev set "$RXE_A" name "$RXE_NEW"; then + if rdma_dev_exists "$NS2" "$RXE_NEW" && + ! rdma_dev_exists "" "$RXE_A"; then + ktap_test_pass "$TEST_MOVE_RENAME" + else + ktap_test_fail "$TEST_MOVE_RENAME" + fi +else + ktap_test_fail "$TEST_MOVE_RENAME" +fi +cleanup_devs + +if ! rdma_supports_combined_move_rename; then + ktap_test_skip "$TEST_COMBINED_MOVE_RENAME" +elif ! setup_devs; then + ktap_test_fail "$TEST_COMBINED_MOVE_RENAME" +elif rdma dev set "$RXE_A" netns "$NS2" name "$RXE_NEW"; then + if rdma_dev_exists "$NS2" "$RXE_NEW" && + ! rdma_dev_exists "" "$RXE_A"; then + ktap_test_pass "$TEST_COMBINED_MOVE_RENAME" + else + ktap_test_fail "$TEST_COMBINED_MOVE_RENAME" + fi +else + ktap_test_fail "$TEST_COMBINED_MOVE_RENAME" +fi +cleanup_devs + +if ! setup_devs; then + ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME" +elif rdma dev set "$RXE_A" name "$RXE_SAME" && + rdma dev set "$RXE_B" name "$RXE_NEW"; then + if rdma dev set "$RXE_SAME" name "$RXE_NEW" >/dev/null 2>&1; then + ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME" + elif rdma_dev_exists "" "$RXE_SAME" && + rdma_dev_exists "" "$RXE_NEW"; then + ktap_test_pass "$TEST_SAME_NETNS_DUP_RENAME" + else + ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME" + fi +else + ktap_test_fail "$TEST_SAME_NETNS_DUP_RENAME" +fi +cleanup_devs + +if ! setup_devs; then + ktap_test_fail "$TEST_TEARDOWN_RETURN" +elif ! rdma dev set "$RXE_A" name "$RXE_SAME" || + ! rdma dev set "$RXE_B" netns "$NS2" || + ! rdma_ns "$NS2" dev set "$RXE_B" name "$RXE_SAME" || + ! rdma_dev_exists "$NS2" "$RXE_SAME"; then + ktap_test_fail "$TEST_TEARDOWN_RETURN" +else + ip netns del "$NS2" + returned=$(wait_rdma_dev_on_netdev "$DUMMY_B") + ktap_print_msg "device returned to init_net as '${returned:-}'" + if rdma_dev_exists "" "$RXE_SAME" && + [ -n "$returned" ] && + [ "$returned" != "$RXE_SAME" ] && + [ "${returned#ibdev}" != "$returned" ]; then + ktap_test_pass "$TEST_TEARDOWN_RETURN" + else + ktap_test_fail "$TEST_TEARDOWN_RETURN" + fi +fi +cleanup_devs + +ktap_finished From a273210687f255b12c2f6baf8f25ff80cdbb29ed Mon Sep 17 00:00:00 2001 From: Gou Hao Date: Fri, 24 Jul 2026 10:28:51 +0800 Subject: [PATCH 119/160] RDMA/cxgb4: remove dead NULL checks after GFP_NOFAIL allocations alloc_skb() with the __GFP_NOFAIL flag will never return NULL, so the subsequent NULL checks and error handling are unreachable dead code. Remove them. Signed-off-by: Gou Hao Link: https://patch.msgid.link/20260724022851.466017-7-gouhao@uniontech.com Reviewed-by: Potnuri Bharat Teja Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/cxgb4/mem.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 49498c75f38f..dc2f494b1007 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -74,11 +74,8 @@ static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr, c4iw_init_wr_wait(wr_waitp); wr_len = roundup(sizeof(*req) + sizeof(*sgl), 16); - if (!skb) { + if (!skb) skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); - if (!skb) - return -ENOMEM; - } set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); req = __skb_put_zero(skb, wr_len); @@ -134,11 +131,8 @@ static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len, roundup(copy_len, T4_ULPTX_MIN_IO), 16); - if (!skb) { + if (!skb) skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); - if (!skb) - return -ENOMEM; - } set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); req = __skb_put_zero(skb, wr_len); From c715007b21a4f6c8ed855a48adf1732702cf1c15 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 27 Jul 2026 02:22:01 -0400 Subject: [PATCH 120/160] RDMA/mlx5: Make sure that UMR page is aligned to PAGE_SIZE The UMR XLT buffer needs to be aligned to PAGE_SIZE. Fixes: b2022068dea0 ("RDMA/mlx5: use kmalloc() for UMR translation buffers") Link: https://patch.msgid.link/20260722-fix-get-order-alignment-v1-1-ece212ddb5dc@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/umr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 80d0d190b26c..951de1d85632 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -518,7 +518,7 @@ static void *mlx5r_umr_alloc_xlt(size_t *nents, size_t ent_size, gfp_t gfp_mask) size = min_t(size_t, ent_size * ALIGN(*nents, xlt_chunk_align), MLX5_MAX_UMR_CHUNK); *nents = size / ent_size; - res = kmalloc(size, gfp_mask | __GFP_NOWARN); + res = kmalloc(PAGE_ALIGN(size), gfp_mask | __GFP_NOWARN); if (res) return res; From 0087470661f6a105013ba3ee8e12273703a5b714 Mon Sep 17 00:00:00 2001 From: Michael Gur Date: Thu, 23 Jul 2026 18:15:14 +0300 Subject: [PATCH 121/160] RDMA/mlx5: Expose RoCE acceleration counters on all functions Decouple RoCE acceleration counters exposure from the roce_accl device cap. The device cap is intended to protect the access to the roce_accl register and was disabled on VFs for that purpose. Reading the acceleration counters, however, does not involve that register, the counters are read-only statistics that carry no configuration risk. Gating their exposure on the capability therefore needlessly hides useful diagnostic data on VFs. Expose the counters on all functions regardless of the capability. Signed-off-by: Michael Gur Reviewed-by: Chiara Meiohas Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260723-expose-roce-accl-counters-v1-1-967618b550cd@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/counters.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/counters.c b/drivers/infiniband/hw/mlx5/counters.c index 5a79e834ddea..2250b195571c 100644 --- a/drivers/infiniband/hw/mlx5/counters.c +++ b/drivers/infiniband/hw/mlx5/counters.c @@ -742,11 +742,9 @@ static void mlx5_ib_fill_counters(struct mlx5_ib_dev *dev, names = is_vport ? vport_roce_accl_cnts : roce_accl_cnts; size = is_vport ? ARRAY_SIZE(vport_roce_accl_cnts) : ARRAY_SIZE(roce_accl_cnts); - if (MLX5_CAP_GEN(dev->mdev, roce_accl)) { - for (i = 0; i < size; i++, j++) { - descs[j].name = names[i].name; - offsets[j] = names[i].offset; - } + for (i = 0; i < size; i++, j++) { + descs[j].name = names[i].name; + offsets[j] = names[i].offset; } if (is_vport) @@ -826,8 +824,7 @@ static int __mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev, size = is_vport ? ARRAY_SIZE(vport_roce_accl_cnts) : ARRAY_SIZE(roce_accl_cnts); - if (MLX5_CAP_GEN(dev->mdev, roce_accl)) - num_counters += size; + num_counters += size; cnts->num_q_counters = num_counters; From dec47e4b0fe34afdf38caa72b4408ba95502e5de Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Thu, 23 Jul 2026 18:23:49 +0300 Subject: [PATCH 122/160] RDMA/mlx5: Fix integer overflow of user QP buffer size set_user_buf_size() computes the QP buffer size by left-shifting the user-supplied rq.wqe_cnt and rq.wqe_shift values as signed integers. A sufficiently large rq.wqe_cnt causes signed integer overflow, which is undefined behavior, and yields a small or negative buf_size, causing ib_umem_get() to map a buffer smaller than the hardware will actually write into. Replace the shifts and addition with check_shl_overflow() and check_add_overflow(), rejecting invalid user inputs. Moreover, guard the identical shift computing qp->sq.offset in _create_user_qp() before set_user_buf_size() is reached. Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Signed-off-by: Maher Sanalla Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260723-fix-qp-buf-size-overflow-v1-1-ccb05ee43a7b@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/qp.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 7ff02d89c31d..e25ac139e43f 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -647,6 +647,7 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr) { int desc_sz = 1 << qp->sq.wqe_shift; + int rq_buf_size, sq_buf_size; if (desc_sz > MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq)) { mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n", @@ -671,11 +672,21 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev, if (attr->qp_type == IB_QPT_RAW_PACKET || qp->flags & IB_QP_CREATE_SOURCE_QPN) { - base->ubuffer.buf_size = qp->rq.wqe_cnt << qp->rq.wqe_shift; - qp->raw_packet_qp.sq.ubuffer.buf_size = qp->sq.wqe_cnt << 6; + if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift, + &base->ubuffer.buf_size)) + return -EINVAL; + if (check_shl_overflow(qp->sq.wqe_cnt, 6, + &qp->raw_packet_qp.sq.ubuffer.buf_size)) + return -EINVAL; } else { - base->ubuffer.buf_size = (qp->rq.wqe_cnt << qp->rq.wqe_shift) + - (qp->sq.wqe_cnt << 6); + if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift, + &rq_buf_size)) + return -EINVAL; + if (check_shl_overflow(qp->sq.wqe_cnt, 6, &sq_buf_size)) + return -EINVAL; + if (check_add_overflow(rq_buf_size, sq_buf_size, + &base->ubuffer.buf_size)) + return -EINVAL; } return 0; @@ -1004,7 +1015,11 @@ static int _create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, qp->rq.offset = 0; qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB); - qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift; + if (check_shl_overflow(qp->rq.wqe_cnt, qp->rq.wqe_shift, + &qp->sq.offset)) { + err = -EINVAL; + goto err_bfreg; + } err = set_user_buf_size(dev, qp, ucmd, base, attr); if (err) From 36b1d3299d0b6aa51485cc9a79b8d94948d5f3d4 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Sun, 26 Jul 2026 12:09:40 +0300 Subject: [PATCH 123/160] net/mlx5: Add qp_latency_sensitive_disable cap bit Introduce the qp_latency_sensitive_disable capability bit to indicate that the device no longer implements a separate class for latency-sensitive QPs, so drivers can stop programming the latency_sensitive QPC field and skip allocating the dedicated fast-path bfreg on such HW. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260726-deprecate-lat-qps-v2-1-5e0c2ee55046@nvidia.com Signed-off-by: Leon Romanovsky --- include/linux/mlx5/mlx5_ifc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index c7206a9d6731..a712463e89b4 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -2005,7 +2005,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 log_max_stride_sz_rq[0x5]; u8 reserved_at_3a8[0x3]; u8 log_min_stride_sz_rq[0x5]; - u8 reserved_at_3b0[0x3]; + u8 reserved_at_3b0[0x2]; + u8 qp_latency_sensitive_disable[0x1]; u8 log_max_stride_sz_sq[0x5]; u8 reserved_at_3b8[0x3]; u8 log_min_stride_sz_sq[0x5]; From ba7f6f2f168081482919529f50c5aea802997f43 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Sun, 26 Jul 2026 12:09:41 +0300 Subject: [PATCH 124/160] RDMA/mlx5: Deprecate latency-sensitive QPs feature New HW no longer implements a separate class for latency-sensitive QPs and advertises this by a new cap bit. REG_UMR is the only QP that used this feature, so gate its usage of the feature on this cap. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Link: https://patch.msgid.link/20260726-deprecate-lat-qps-v2-2-5e0c2ee55046@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 6 +++++- drivers/infiniband/hw/mlx5/qp.c | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 788f79f040b0..cc7e9a3e7631 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4951,6 +4951,9 @@ static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev) if (err) return err; + if (MLX5_CAP_GEN(dev->mdev, qp_latency_sensitive_disable)) + return 0; + err = mlx5_alloc_bfreg(dev->mdev, &dev->fp_bfreg, false, true); if (err) mlx5_free_bfreg(dev->mdev, &dev->bfreg); @@ -4960,7 +4963,8 @@ static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev) static void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev) { - mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg); + if (!MLX5_CAP_GEN(dev->mdev, qp_latency_sensitive_disable)) + mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg); mlx5_free_bfreg(dev->mdev, &dev->bfreg); } diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index e25ac139e43f..0d4f8b109ad2 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1138,7 +1138,8 @@ static int _create_kernel_qp(struct mlx5_ib_dev *dev, void *qpc; int err; - if (init_attr->qp_type == MLX5_IB_QPT_REG_UMR) + if (init_attr->qp_type == MLX5_IB_QPT_REG_UMR && + !MLX5_CAP_GEN(dev->mdev, qp_latency_sensitive_disable)) qp->bf.bfreg = &dev->fp_bfreg; else qp->bf.bfreg = &dev->bfreg; @@ -2524,11 +2525,12 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, MLX5_SET(qpc, qpc, st, mlx5_st); MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED); - if (attr->qp_type != MLX5_IB_QPT_REG_UMR) + if (attr->qp_type == MLX5_IB_QPT_REG_UMR) { + if (!MLX5_CAP_GEN(dev->mdev, qp_latency_sensitive_disable)) + MLX5_SET(qpc, qpc, latency_sensitive, 1); + } else { MLX5_SET(qpc, qpc, pd, to_mpd(pd ? pd : devr->p0)->pdn); - else - MLX5_SET(qpc, qpc, latency_sensitive, 1); - + } if (qp->flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) MLX5_SET(qpc, qpc, block_lb_mc, 1); From 97eafb59d41e62ae54bb7ec61004409d02b7dc74 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Tue, 21 Jul 2026 04:54:37 -0700 Subject: [PATCH 125/160] RDMA/bnxt_re: Replace per-device hash tables with per-context XArrays The CQ and SRQ hash tables (cq_hash, srq_hash) on struct bnxt_re_dev were used exclusively to look up a toggle-page pointer from a user-space-supplied hardware queue ID in the GET_TOGGLE_MEM ioctl handler. This approach has couple of problems. First, because the tables are per-device, any user can look up another user's CQ or SRQ by guessing the hardware queue ID. Second, concurrent add and remove operations on the hash table are not protected by any lock, leaving a race window. The correct fix is to retrieve the CQ and SRQ objects via the uverbs object handle, which gives built-in ownership verification and reference pinning for the duration of the ioctl. That is added in a later patch of this series. To maintain backward compatibility with older rdma-core versions that do not send a uverbs object handle, the driver must continue to support the existing TYPE + RES_ID lookup path. This patch replaces the per-device hash tables with per-ucontext XArrays (cq_xa and srq_xa on struct bnxt_re_ucontext), which narrows the lookup scope to the calling context, eliminating the cross-user visibility. Also adds Xarray locking mechanism for synchronization. The GET_TOGGLE_MEM ioctl handler is updated to call xa_load() in place of the now-removed bnxt_re_search_for_cq()/ bnxt_re_search_for_srq() helpers. No ABI changes are required. bnxt_re_create_user_cq()/bnxt_re_create_srq() publish the uobject into cq_xa/srq_xa before returning to the uverbs core, but the core only sets uobject->object once the create callback has returned success. Guard the lookup against this so a concurrent GET_TOGGLE_MEM racing an in-progress create cannot feed a NULL ->object into container_of(). Signed-off-by: Selvin Xavier Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/bnxt_re.h | 6 -- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 87 +++++++++++++++++++----- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 6 +- drivers/infiniband/hw/bnxt_re/main.c | 4 -- drivers/infiniband/hw/bnxt_re/uapi.c | 81 +++++++++------------- 5 files changed, 105 insertions(+), 79 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index 3a7ce4729fcf..a43e678151d3 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -41,7 +41,6 @@ #define __BNXT_RE_H__ #include #include "hw_counters.h" -#include #define ROCE_DRV_MODULE_NAME "bnxt_re" #define BNXT_RE_DESC "Broadcom NetXtreme-C/E RoCE Driver" @@ -158,9 +157,6 @@ struct bnxt_re_nq_record { struct mutex load_lock; }; -#define MAX_CQ_HASH_BITS (16) -#define MAX_SRQ_HASH_BITS (16) - static inline bool bnxt_re_chip_gen_p7(u16 chip_num) { return (chip_num == CHIP_NUM_58818 || @@ -215,8 +211,6 @@ struct bnxt_re_dev { struct bnxt_re_pacing pacing; struct work_struct dbq_fifo_check_work; struct delayed_work dbq_pacing_work; - DECLARE_HASHTABLE(cq_hash, MAX_CQ_HASH_BITS); - DECLARE_HASHTABLE(srq_hash, MAX_SRQ_HASH_BITS); struct dentry *dbg_root; struct dentry *qp_debugfs; unsigned long event_bitmap; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index a2a354a0fbef..9184b2b95834 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -2151,11 +2151,26 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) if (ret) return ret; - if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) - hash_del(&srq->hash_entry); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { + struct bnxt_re_ucontext *uctx = + rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); + + /* + * Untrack the SRQ before releasing its hardware ID below, so a + * concurrent create that gets the same ID reused by firmware + * cannot have its fresh XArray entry erased by this destroy. + */ + if (uctx) + xa_erase(&uctx->srq_xa, srq->qplib_srq.id); + } bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); - if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) - free_page((unsigned long)srq->uctx_srq_page); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { + struct bnxt_re_ucontext *uctx = + rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); + + if (uctx) + free_page((unsigned long)srq->uctx_srq_page); + } ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); return 0; @@ -2262,20 +2277,21 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, resp.srqid = srq->qplib_srq.id; if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { - hash_add(rdev->srq_hash, &srq->hash_entry, srq->qplib_srq.id); srq->uctx_srq_page = (void *)get_zeroed_page(GFP_KERNEL); if (!srq->uctx_srq_page) { rc = -ENOMEM; - goto fail; + goto fail_destroy_srq; + } + if (xa_is_err(xa_store(&uctx->srq_xa, srq->qplib_srq.id, + ib_srq->uobject, GFP_KERNEL))) { + rc = -ENOMEM; + goto fail_free_srq_page; } resp.comp_mask |= BNXT_RE_SRQ_TOGGLE_PAGE_SUPPORT; } rc = ib_respond_udata(udata, resp); - if (rc) { - bnxt_qplib_destroy_srq(&rdev->qplib_res, - &srq->qplib_srq); - goto fail; - } + if (rc) + goto fail_respond; } active_srqs = atomic_inc_return(&rdev->stats.res.srq_count); if (active_srqs > rdev->stats.res.srq_watermark) @@ -2284,6 +2300,17 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, return 0; +fail_respond: + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { + xa_erase(&uctx->srq_xa, srq->qplib_srq.id); + free_page((unsigned long)srq->uctx_srq_page); + } + bnxt_qplib_destroy_srq(&rdev->qplib_res, &srq->qplib_srq); + goto fail; +fail_free_srq_page: + free_page((unsigned long)srq->uctx_srq_page); +fail_destroy_srq: + bnxt_qplib_destroy_srq(&rdev->qplib_res, &srq->qplib_srq); fail: ib_umem_release(srq->umem); exit: @@ -3465,11 +3492,26 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) if (ret) return ret; - if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) - hash_del(&cq->hash_entry); + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { + struct bnxt_re_ucontext *uctx = + rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); + + /* + * Untrack the CQ before releasing its hardware ID below, so a + * concurrent create that gets the same ID reused by firmware + * cannot have its fresh XArray entry erased by this destroy. + */ + if (uctx) + xa_erase(&uctx->cq_xa, cq->qplib_cq.id); + } bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); - if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) - free_page((unsigned long)cq->uctx_cq_page); + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { + struct bnxt_re_ucontext *uctx = + rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); + + if (uctx) + free_page((unsigned long)cq->uctx_cq_page); + } bnxt_re_put_nq(rdev, nq); @@ -3544,14 +3586,16 @@ int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *att spin_lock_init(&cq->cq_lock); if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { - hash_add(rdev->cq_hash, &cq->hash_entry, cq->qplib_cq.id); - /* Allocate a page */ cq->uctx_cq_page = (void *)get_zeroed_page(GFP_KERNEL); if (!cq->uctx_cq_page) { rc = -ENOMEM; goto destroy_cq; } - + if (xa_is_err(xa_store(&uctx->cq_xa, cq->qplib_cq.id, + ibcq->uobject, GFP_KERNEL))) { + rc = -ENOMEM; + goto free_cq_page; + } resp.comp_mask |= BNXT_RE_CQ_TOGGLE_PAGE_SUPPORT; } resp.cqid = cq->qplib_cq.id; @@ -3564,6 +3608,9 @@ int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *att return 0; free_mem: + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) + xa_erase(&uctx->cq_xa, cq->qplib_cq.id); +free_cq_page: free_page((unsigned long)cq->uctx_cq_page); destroy_cq: bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); @@ -4789,6 +4836,8 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) goto cfail; } uctx->shpage_mmap = &entry->rdma_entry; + xa_init(&uctx->cq_xa); + xa_init(&uctx->srq_xa); if (rdev->pacing.dbr_pacing) resp.comp_mask |= BNXT_RE_UCNTX_CMASK_DBR_PACING_ENABLED; @@ -4841,6 +4890,8 @@ void bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx) uctx->shpage_mmap = NULL; if (uctx->shpg) free_page((unsigned long)uctx->shpg); + xa_destroy(&uctx->cq_xa); + xa_destroy(&uctx->srq_xa); if (uctx->dpi.dbr) { /* Free DPI only if this is the first PD allocated by the diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 22bf81668cfb..4c78c183784b 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -70,6 +70,8 @@ struct bnxt_re_ah { struct bnxt_qplib_ah qplib_ah; }; +struct bnxt_re_user_mmap_entry; + struct bnxt_re_srq { struct ib_srq ib_srq; struct bnxt_re_dev *rdev; @@ -78,7 +80,6 @@ struct bnxt_re_srq { struct ib_umem *umem; spinlock_t lock; /* protect srq */ void *uctx_srq_page; - struct hlist_node hash_entry; }; struct bnxt_re_qp { @@ -113,7 +114,6 @@ struct bnxt_re_cq { struct ib_umem *resize_umem; int resize_cqe; void *uctx_cq_page; - struct hlist_node hash_entry; }; struct bnxt_re_mr { @@ -147,6 +147,8 @@ struct bnxt_re_ucontext { void *shpg; spinlock_t sh_lock; /* protect shpg */ struct rdma_user_mmap_entry *shpage_mmap; + struct xarray cq_xa; /* cqid → ib_uobject, per-context toggle page lookup */ + struct xarray srq_xa; /* srqid → ib_uobject, per-context toggle page lookup */ u64 cmask; }; diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index d25fdc458120..ce72db1b4bc3 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -2337,10 +2337,6 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 op_type) if (!(rdev->qplib_res.en_dev->flags & BNXT_EN_FLAG_ROCE_VF_RES_MGMT)) bnxt_re_vf_res_config(rdev); } - hash_init(rdev->cq_hash); - if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) - hash_init(rdev->srq_hash); - bnxt_re_debugfs_add_pdev(rdev); bnxt_re_init_dcb_wq(rdev); diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 263238a6e4cd..c5e4e6e47b5f 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -22,31 +22,6 @@ #include "bnxt_re.h" #include "ib_verbs.h" -static struct bnxt_re_cq *bnxt_re_search_for_cq(struct bnxt_re_dev *rdev, u32 cq_id) -{ - struct bnxt_re_cq *cq = NULL, *tmp_cq; - - hash_for_each_possible(rdev->cq_hash, tmp_cq, hash_entry, cq_id) { - if (tmp_cq->qplib_cq.id == cq_id) { - cq = tmp_cq; - break; - } - } - return cq; -} - -static struct bnxt_re_srq *bnxt_re_search_for_srq(struct bnxt_re_dev *rdev, u32 srq_id) -{ - struct bnxt_re_srq *srq = NULL, *tmp_srq; - - hash_for_each_possible(rdev->srq_hash, tmp_srq, hash_entry, srq_id) { - if (tmp_srq->qplib_srq.id == srq_id) { - srq = tmp_srq; - break; - } - } - return srq; -} static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) { @@ -244,12 +219,10 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; enum bnxt_re_get_toggle_mem_type res_type; struct bnxt_re_user_mmap_entry *entry; + struct ib_uobject *res_uobj; struct bnxt_re_ucontext *uctx; struct ib_ucontext *ib_uctx; - struct bnxt_re_dev *rdev; - struct bnxt_re_srq *srq; u32 length = PAGE_SIZE; - struct bnxt_re_cq *cq; u64 mem_offset; u32 offset = 0; u64 addr = 0; @@ -265,35 +238,45 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund return err; uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); - rdev = uctx->rdev; err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); if (err) return err; - switch (res_type) { - case BNXT_RE_CQ_TOGGLE_MEM: - cq = bnxt_re_search_for_cq(rdev, res_id); - if (!cq) - return -EINVAL; + /* + * bnxt_re_create_cq/srq() publishes the uobject into cq_xa/srq_xa + * before returning to the uverbs core, but the core only sets + * uobject->object once the create callback has returned success. + * A lookup that races with an in-progress create can therefore + * find a uobject whose ->object is still NULL; skip it instead of + * feeding NULL to container_of(). + */ + if (res_type == BNXT_RE_CQ_TOGGLE_MEM) { + struct bnxt_re_cq *cq; - addr = (u64)cq->uctx_cq_page; - if (!addr) - return -EOPNOTSUPP; - break; - case BNXT_RE_SRQ_TOGGLE_MEM: - srq = bnxt_re_search_for_srq(rdev, res_id); - if (!srq) - return -EINVAL; + xa_lock(&uctx->cq_xa); + res_uobj = xa_load(&uctx->cq_xa, res_id); + if (res_uobj && res_uobj->object) { + cq = container_of(res_uobj->object, struct bnxt_re_cq, ib_cq); + addr = (u64)cq->uctx_cq_page; + } + xa_unlock(&uctx->cq_xa); + } else if (res_type == BNXT_RE_SRQ_TOGGLE_MEM) { + struct bnxt_re_srq *srq; - addr = (u64)srq->uctx_srq_page; - if (!addr) - return -EOPNOTSUPP; - break; - - default: + xa_lock(&uctx->srq_xa); + res_uobj = xa_load(&uctx->srq_xa, res_id); + if (res_uobj && res_uobj->object) { + srq = container_of(res_uobj->object, struct bnxt_re_srq, ib_srq); + addr = (u64)srq->uctx_srq_page; + } + xa_unlock(&uctx->srq_xa); + } else { return -EOPNOTSUPP; } + if (!addr) + return -EOPNOTSUPP; + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); if (!entry) return -ENOMEM; @@ -322,7 +305,7 @@ static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why, struct uverbs_attr_bundle *attrs) { - struct bnxt_re_user_mmap_entry *entry = uobject->object; + struct bnxt_re_user_mmap_entry *entry = uobject->object; rdma_user_mmap_entry_remove(&entry->rdma_entry); return 0; From e202e3a55cf10cdbc36c9a7847b0af1f4290fb87 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Tue, 21 Jul 2026 04:54:38 -0700 Subject: [PATCH 126/160] RDMA/bnxt_re: Defer toggle page free to rdma_user_mmap_entry teardown Fix the page lifetime by making the rdma_user_mmap_entry the sole owner of the toggle page allocation. Creating the rdma_user_mmap_entry and page during the CQ/SRQ creation time. Freeing the page is handled when the mmap free is called. Introduce struct bnxt_re_toggle_mem to carry the mmap_offset for the lifetime of the GET_TOGGLE_MEM uobject handle. bnxt_re_destroy_cq/srq can erase the entry from the XArray and call rdma_user_mmap_entry_remove() on the toggle_entry concurrently with the caller's xa_load() and its subsequent use of that toggle_entry. Guard against this by taking an extra kref directly on the toggle_entry's rdma_user_mmap_entry while the GET_TOGGLE_MEM handle exists, released when the handle is destroyed. This pins exactly the resource that GET_TOGGLE_MEM hands out (the mmap offset/page), independent of the CQ/SRQ's own lifetime. Signed-off-by: Selvin Xavier Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 40 ++++++++++++++++--- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 2 + drivers/infiniband/hw/bnxt_re/uapi.c | 51 ++++++++++++++++++------ 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 9184b2b95834..0e44c7fcf5fe 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -2169,7 +2169,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); if (uctx) - free_page((unsigned long)srq->uctx_srq_page); + rdma_user_mmap_entry_remove(&srq->toggle_entry->rdma_entry); } ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); @@ -2282,10 +2282,17 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, rc = -ENOMEM; goto fail_destroy_srq; } + srq->toggle_entry = bnxt_re_mmap_entry_insert(uctx, (u64)srq->uctx_srq_page, + BNXT_RE_MMAP_TOGGLE_PAGE, + NULL); + if (!srq->toggle_entry) { + rc = -ENOMEM; + goto fail_free_srq_page; + } if (xa_is_err(xa_store(&uctx->srq_xa, srq->qplib_srq.id, ib_srq->uobject, GFP_KERNEL))) { rc = -ENOMEM; - goto fail_free_srq_page; + goto fail_remove_toggle_entry; } resp.comp_mask |= BNXT_RE_SRQ_TOGGLE_PAGE_SUPPORT; } @@ -2303,10 +2310,13 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, fail_respond: if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { xa_erase(&uctx->srq_xa, srq->qplib_srq.id); - free_page((unsigned long)srq->uctx_srq_page); + goto fail_remove_toggle_entry; } bnxt_qplib_destroy_srq(&rdev->qplib_res, &srq->qplib_srq); goto fail; +fail_remove_toggle_entry: + rdma_user_mmap_entry_remove(&srq->toggle_entry->rdma_entry); + goto fail_destroy_srq; fail_free_srq_page: free_page((unsigned long)srq->uctx_srq_page); fail_destroy_srq: @@ -3510,7 +3520,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); if (uctx) - free_page((unsigned long)cq->uctx_cq_page); + rdma_user_mmap_entry_remove(&cq->toggle_entry->rdma_entry); } bnxt_re_put_nq(rdev, nq); @@ -3591,10 +3601,16 @@ int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *att rc = -ENOMEM; goto destroy_cq; } + cq->toggle_entry = bnxt_re_mmap_entry_insert(uctx, (u64)cq->uctx_cq_page, + BNXT_RE_MMAP_TOGGLE_PAGE, NULL); + if (!cq->toggle_entry) { + rc = -ENOMEM; + goto free_cq_page; + } if (xa_is_err(xa_store(&uctx->cq_xa, cq->qplib_cq.id, ibcq->uobject, GFP_KERNEL))) { rc = -ENOMEM; - goto free_cq_page; + goto remove_toggle_entry; } resp.comp_mask |= BNXT_RE_CQ_TOGGLE_PAGE_SUPPORT; } @@ -3610,6 +3626,10 @@ int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *att free_mem: if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) xa_erase(&uctx->cq_xa, cq->qplib_cq.id); +remove_toggle_entry: + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) + rdma_user_mmap_entry_remove(&cq->toggle_entry->rdma_entry); + goto destroy_cq; free_cq_page: free_page((unsigned long)cq->uctx_cq_page); destroy_cq: @@ -5060,6 +5080,16 @@ void bnxt_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry) bnxt_entry = container_of(rdma_entry, struct bnxt_re_user_mmap_entry, rdma_entry); + /* + * For toggle pages the kernel VA was stored directly in mem_offset + * at creation time (bnxt_re_create_user_cq / bnxt_re_create_srq). + * Free it here — this is the only place it is freed, ensuring the + * page outlives every concurrent bnxt_re_mmap() call that may have + * incremented the entry's reference count. + */ + if (bnxt_entry->mmap_flag == BNXT_RE_MMAP_TOGGLE_PAGE) + free_page((unsigned long)bnxt_entry->mem_offset); + if (bnxt_entry->dpi_valid) bnxt_qplib_free_uc_dpi(&bnxt_entry->uctx->rdev->qplib_res, &bnxt_entry->dpi); diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 4c78c183784b..b7b33f6acf91 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -80,6 +80,7 @@ struct bnxt_re_srq { struct ib_umem *umem; spinlock_t lock; /* protect srq */ void *uctx_srq_page; + struct bnxt_re_user_mmap_entry *toggle_entry; }; struct bnxt_re_qp { @@ -114,6 +115,7 @@ struct bnxt_re_cq { struct ib_umem *resize_umem; int resize_cqe; void *uctx_cq_page; + struct bnxt_re_user_mmap_entry *toggle_entry; }; struct bnxt_re_mr { diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index c5e4e6e47b5f..97bc0e755511 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -213,19 +213,23 @@ DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); /* Toggle MEM */ +struct bnxt_re_toggle_mem { + struct bnxt_re_user_mmap_entry *toggle_entry; + u64 mmap_offset; +}; + static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bundle *attrs) { struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); - enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; + struct bnxt_re_user_mmap_entry *toggle_entry = NULL; enum bnxt_re_get_toggle_mem_type res_type; - struct bnxt_re_user_mmap_entry *entry; + struct bnxt_re_toggle_mem *tmem; struct ib_uobject *res_uobj; struct bnxt_re_ucontext *uctx; struct ib_ucontext *ib_uctx; u32 length = PAGE_SIZE; - u64 mem_offset; + u64 mmap_offset = 0; u32 offset = 0; - u64 addr = 0; u32 res_id; int err; @@ -243,6 +247,10 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund return err; /* + * Hold xa_lock across xa_load + kref_get so that a concurrent + * bnxt_re_destroy_cq/srq cannot call __xa_erase and remove the + * toggle_entry between our load and our reference on it. + * * bnxt_re_create_cq/srq() publishes the uobject into cq_xa/srq_xa * before returning to the uverbs core, but the core only sets * uobject->object once the create callback has returned success. @@ -257,7 +265,13 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund res_uobj = xa_load(&uctx->cq_xa, res_id); if (res_uobj && res_uobj->object) { cq = container_of(res_uobj->object, struct bnxt_re_cq, ib_cq); - addr = (u64)cq->uctx_cq_page; + if (cq->toggle_entry) + mmap_offset = + rdma_user_mmap_get_offset(&cq->toggle_entry->rdma_entry); + if (mmap_offset) { + kref_get(&cq->toggle_entry->rdma_entry.ref); + toggle_entry = cq->toggle_entry; + } } xa_unlock(&uctx->cq_xa); } else if (res_type == BNXT_RE_SRQ_TOGGLE_MEM) { @@ -267,24 +281,34 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund res_uobj = xa_load(&uctx->srq_xa, res_id); if (res_uobj && res_uobj->object) { srq = container_of(res_uobj->object, struct bnxt_re_srq, ib_srq); - addr = (u64)srq->uctx_srq_page; + if (srq->toggle_entry) + mmap_offset = + rdma_user_mmap_get_offset(&srq->toggle_entry->rdma_entry); + if (mmap_offset) { + kref_get(&srq->toggle_entry->rdma_entry.ref); + toggle_entry = srq->toggle_entry; + } } xa_unlock(&uctx->srq_xa); } else { return -EOPNOTSUPP; } - if (!addr) + if (!mmap_offset) return -EOPNOTSUPP; - entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); - if (!entry) + tmem = kzalloc_obj(*tmem); + if (!tmem) { + rdma_user_mmap_entry_put(&toggle_entry->rdma_entry); return -ENOMEM; + } - uobj->object = entry; + tmem->toggle_entry = toggle_entry; + tmem->mmap_offset = mmap_offset; + uobj->object = tmem; uverbs_finalize_uobj_create(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_PAGE, - &mem_offset, sizeof(mem_offset)); + &mmap_offset, sizeof(mmap_offset)); if (err) return err; @@ -305,9 +329,10 @@ static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why, struct uverbs_attr_bundle *attrs) { - struct bnxt_re_user_mmap_entry *entry = uobject->object; + struct bnxt_re_toggle_mem *tmem = uobject->object; - rdma_user_mmap_entry_remove(&entry->rdma_entry); + rdma_user_mmap_entry_put(&tmem->toggle_entry->rdma_entry); + kfree(tmem); return 0; } From a1c0d4719b089e34c1c90c0404f414f9345997ad Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Tue, 21 Jul 2026 04:54:39 -0700 Subject: [PATCH 127/160] RDMA/bnxt_re: Add uverbs object handle path for CQ/SRQ toggle page The current GET_TOGGLE_MEM ioctl requires the caller to supply a type enum and a raw hardware queue ID (RES_ID). The kernel looks up the CQ or SRQ by that ID without verifying that the caller owns the resource. Add a new, preferred code path that accepts standard uverbs object handles (BNXT_RE_TOGGLE_MEM_CQ_HANDLE / BNXT_RE_TOGGLE_MEM_SRQ_HANDLE) instead. The uverbs core validates that the handle belongs to the calling context as part of resolving it, so this path no longer needs the driver's own XArray lookup for ownership checking. As with the legacy path, the toggle_entry's own mmap-entry refcount (not a CQ/SRQ uobject reference) is what pins the toggle page for the life of the GET_TOGGLE_MEM handle. Only newer rdma-core versions support this path, if the driver reports the supported resp mask (BNXT_RE_UCNTX_CMASK_TOGGLE_MEM_UOBJ_SUPPORT). The existing TYPE + RES_ID path is retained for backward compatibility with older rdma-core. Suggested-by: Jason Gunthorpe Signed-off-by: Selvin Xavier Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 2 + drivers/infiniband/hw/bnxt_re/uapi.c | 55 +++++++++++++++++++++--- include/uapi/rdma/bnxt_re-abi.h | 3 ++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 0e44c7fcf5fe..ccd2702db78b 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -4867,6 +4867,8 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) if (_is_modify_qp_rate_limit_supported(dev_attr->dev_cap_flags2)) resp.comp_mask |= BNXT_RE_UCNTX_CMASK_QP_RATE_LIMIT_ENABLED; + resp.comp_mask |= BNXT_RE_UCNTX_CMASK_TOGGLE_MEM_UOBJ_SUPPORT; + if (udata->inlen) { rc = ib_copy_validate_udata_in_cm( udata, ureq, comp_mask, diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 97bc0e755511..feaf98631fc5 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -237,16 +237,52 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund if (IS_ERR(ib_uctx)) return PTR_ERR(ib_uctx); + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + + /* New path: updated libbnxt_re passes the CQ or SRQ uverbs handle */ + if (uverbs_attr_is_valid(attrs, BNXT_RE_TOGGLE_MEM_CQ_HANDLE)) { + struct bnxt_re_cq *cq; + + res_uobj = uverbs_attr_get_uobject(attrs, + BNXT_RE_TOGGLE_MEM_CQ_HANDLE); + if (IS_ERR(res_uobj)) + return PTR_ERR(res_uobj); + cq = container_of(res_uobj->object, struct bnxt_re_cq, ib_cq); + if (!cq->toggle_entry) + return -EOPNOTSUPP; + mmap_offset = rdma_user_mmap_get_offset(&cq->toggle_entry->rdma_entry); + if (!mmap_offset) + return -EOPNOTSUPP; + kref_get(&cq->toggle_entry->rdma_entry.ref); + toggle_entry = cq->toggle_entry; + goto alloc_tmem; + } else if (uverbs_attr_is_valid(attrs, BNXT_RE_TOGGLE_MEM_SRQ_HANDLE)) { + struct bnxt_re_srq *srq; + + res_uobj = uverbs_attr_get_uobject(attrs, + BNXT_RE_TOGGLE_MEM_SRQ_HANDLE); + if (IS_ERR(res_uobj)) + return PTR_ERR(res_uobj); + srq = container_of(res_uobj->object, struct bnxt_re_srq, ib_srq); + if (!srq->toggle_entry) + return -EOPNOTSUPP; + mmap_offset = rdma_user_mmap_get_offset(&srq->toggle_entry->rdma_entry); + if (!mmap_offset) + return -EOPNOTSUPP; + kref_get(&srq->toggle_entry->rdma_entry.ref); + toggle_entry = srq->toggle_entry; + goto alloc_tmem; + } + err = uverbs_get_const(&res_type, attrs, BNXT_RE_TOGGLE_MEM_TYPE); if (err) return err; - - uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); if (err) return err; /* + * Legacy path: old libbnxt_re sends TYPE + RES_ID. * Hold xa_lock across xa_load + kref_get so that a concurrent * bnxt_re_destroy_cq/srq cannot call __xa_erase and remove the * toggle_entry between our load and our reference on it. @@ -297,6 +333,7 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bund if (!mmap_offset) return -EOPNOTSUPP; +alloc_tmem: tmem = kzalloc_obj(*tmem); if (!tmem) { rdma_user_mmap_entry_put(&toggle_entry->rdma_entry); @@ -343,10 +380,10 @@ DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, UA_MANDATORY), UVERBS_ATTR_CONST_IN(BNXT_RE_TOGGLE_MEM_TYPE, enum bnxt_re_get_toggle_mem_type, - UA_MANDATORY), + UA_OPTIONAL), UVERBS_ATTR_PTR_IN(BNXT_RE_TOGGLE_MEM_RES_ID, UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), + UA_OPTIONAL), UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_PAGE, UVERBS_ATTR_TYPE(u64), UA_MANDATORY), @@ -355,7 +392,15 @@ DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, UA_MANDATORY), UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); + UA_MANDATORY), + UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_CQ_HANDLE, + UVERBS_OBJECT_CQ, + UVERBS_ACCESS_READ, + UA_OPTIONAL), + UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_SRQ_HANDLE, + UVERBS_OBJECT_SRQ, + UVERBS_ACCESS_READ, + UA_OPTIONAL)); DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM, UVERBS_ATTR_IDR(BNXT_RE_RELEASE_TOGGLE_MEM_HANDLE, diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index a4599d7b736a..856a1b3036e9 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -57,6 +57,7 @@ enum { BNXT_RE_UCNTX_CMASK_POW2_DISABLED = 0x10ULL, BNXT_RE_UCNTX_CMASK_MSN_TABLE_ENABLED = 0x40, BNXT_RE_UCNTX_CMASK_QP_RATE_LIMIT_ENABLED = 0x80ULL, + BNXT_RE_UCNTX_CMASK_TOGGLE_MEM_UOBJ_SUPPORT = 0x400000ULL, }; enum bnxt_re_wqe_mode { @@ -218,6 +219,8 @@ enum bnxt_re_var_toggle_mem_attrs { BNXT_RE_TOGGLE_MEM_MMAP_PAGE, BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, + BNXT_RE_TOGGLE_MEM_CQ_HANDLE, + BNXT_RE_TOGGLE_MEM_SRQ_HANDLE, }; enum bnxt_re_toggle_mem_attrs { From 51f2c8d2c99fc1f452f7113c08a35edcc4bf8732 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 27 Jul 2026 13:06:59 +0800 Subject: [PATCH 128/160] RDMA/rxe: Fix UAF in ODP init error-handling path rxe_odp_mr_init_user() stores &umem_odp->umem in mr->umem before calling rxe_odp_init_pages(). If rxe_odp_init_pages() fails, rxe_odp_mr_init_user() releases umem_odp and returns an error. rxe_reg_user_mr() then unwinds the error through rxe_cleanup(), rxe_mr_cleanup(), ib_umem_release(mr->umem). There is an IS_ERR_OR_NULL(umem) check at the start of ib_umem_release(). But since mr->umem is NOT reset to NULL in the error handling path of rxe_odp_mr_init_user(), the check passes and it reads already-freed fields like umem->is_dmabuf, causing UAF. Fix the UAF by clearing mr->umem after releasing the failed ODP umem so the MR cleanup path does not release it again. Fixes: d03fb5c6599e ("RDMA/rxe: Allow registering MRs for On-Demand Paging") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Peiyang He Link: https://patch.msgid.link/70CB6DBCB19624C7+20260727050659.1543627-1-peiyang_he@smail.nju.edu.cn Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_odp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/sw/rxe/rxe_odp.c b/drivers/infiniband/sw/rxe/rxe_odp.c index c189c033175d..e870efa7a0a3 100644 --- a/drivers/infiniband/sw/rxe/rxe_odp.c +++ b/drivers/infiniband/sw/rxe/rxe_odp.c @@ -109,6 +109,7 @@ int rxe_odp_mr_init_user(struct rxe_dev *rxe, u64 start, u64 length, err = rxe_odp_init_pages(mr); if (err) { ib_umem_odp_release(umem_odp); + mr->umem = NULL; return err; } From 229b42d7450c1cf96f45ec39ebb69211b06bc036 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Mon, 27 Jul 2026 09:02:55 +0000 Subject: [PATCH 129/160] RDMA/efa: Fix PBL chunk length computation On register MR, when creating the PBL, if it's an indirect PBL we create a chunk list to hold the PBL pages pointers. Each chunk is 4KB in size and can hold 510 addresses (EFA_PTRS_PER_CHUNK) and has a 12-byte control buffer at the end of it holding the next chunk's pointer and its length. If the PBL number of pages is a multiple of EFA_PTRS_PER_CHUNK, the calculated last chunk length is wrongly computed as 0, even though that chunk is fully populated with 510 real page pointers. This wrong length is used both to DMA map the chunk and is propagated to the device, causing the device to see the chunk as empty and reject the memory registration. Fix the calculation so it will be performed only if the number of pages isn't a multiple of EFA_PTRS_PER_CHUNK, if it is, its already handled in the above loop correctly. Also prevent out-of-bounds reach in the chunks array in such scenario. Fixes: 40909f664d27 ("RDMA/efa: Add EFA verbs implementation") Reviewed-by: Firas Jahjah Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Link: https://patch.msgid.link/20260727090255.1175120-1-ynachum@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_verbs.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 6abb93b50731..2d28d68efe77 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -1362,9 +1362,11 @@ static int pbl_chunk_list_create(struct efa_dev *dev, struct pbl_context *pbl) chunk_list->chunks[i].length = EFA_CHUNK_USED_SIZE; } - chunk_list->chunks[chunk_list_size - 1].length = - ((page_cnt % EFA_PTRS_PER_CHUNK) * EFA_CHUNK_PAYLOAD_PTR_SIZE) + - EFA_CHUNK_PTR_SIZE; + + if (page_cnt % EFA_PTRS_PER_CHUNK != 0) + chunk_list->chunks[chunk_list_size - 1].length = + ((page_cnt % EFA_PTRS_PER_CHUNK) * EFA_CHUNK_PAYLOAD_PTR_SIZE) + + EFA_CHUNK_PTR_SIZE; /* fill the dma addresses of sg list pages to chunks: */ chunk_idx = 0; @@ -1376,9 +1378,12 @@ static int pbl_chunk_list_create(struct efa_dev *dev, struct pbl_context *pbl) rdma_block_iter_dma_address(&biter); if (payload_idx == EFA_PTRS_PER_CHUNK) { - chunk_idx++; - cur_chunk_buf = chunk_list->chunks[chunk_idx].buf; payload_idx = 0; + chunk_idx++; + if (chunk_idx >= chunk_list_size) + break; + + cur_chunk_buf = chunk_list->chunks[chunk_idx].buf; } } From 373f3716a2de7adc739269ebb4d87e5bf4dc180c Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 29 Jul 2026 07:21:28 -0400 Subject: [PATCH 130/160] RDMA/cxgb4: Fix dereg_skb leak and double free in write_tpt_entry() When the device is in the fatal error state, write_tpt_entry() returns -EIO before handing the caller's preallocated skb to the transmit path; its allocation-failure returns do the same. c4iw_dereg_mr() ignores the error and frees mhp, leaking mhp->dereg_skb. c4iw_get_dma_mr() instead frees the skb a second time after dereg_mem() already consumed it, a double free. Make write_tpt_entry() the sole owner of a non-NULL skb, freeing it on every return preceding handoff to c4iw_ofld_send(): fatal error, tpt and stag allocation failure. c4iw_ofld_send() consumes the skb on success and error alike, so drop the redundant kfree_skb() in c4iw_get_dma_mr() after dereg_mem(). Fixes: 0f8ab0b6e91b ("RDMA/iw_cxgb4: Low resource fixes for Memory registration") Link: https://patch.msgid.link/20260726-leaked-mhp-dereg-skb-in-c4iw-dereg-m-v1-1-ebd6df364d53@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/cxgb4/mem.c | 46 ++++++++++++------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index dc2f494b1007..1413bd0c0752 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -193,7 +193,8 @@ static int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, daddr = dma_map_single(&rdev->lldi.pdev->dev, data, len, DMA_TO_DEVICE); if (dma_mapping_error(&rdev->lldi.pdev->dev, daddr)) - return -1; + return _c4iw_write_mem_inline(rdev, addr, len, data, skb, + wr_waitp); save = daddr; while (remain > inline_threshold) { @@ -229,30 +230,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data, struct sk_buff *skb, struct c4iw_wr_wait *wr_waitp) { - int ret; - - if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl) { - ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb, + if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl || + len <= inline_threshold) + return _c4iw_write_mem_inline(rdev, addr, len, data, skb, wr_waitp); - goto out; - } - - if (len <= inline_threshold) { - ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb, - wr_waitp); - goto out; - } - - ret = _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp); - if (ret) { - pr_warn_ratelimited("%s: dma map failure (non fatal)\n", - pci_name(rdev->lldi.pdev)); - ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb, - wr_waitp); - } -out: - return ret; + return _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp); } /* @@ -273,12 +256,16 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, u32 stag_idx; static atomic_t key; - if (c4iw_fatal_error(rdev)) + if (c4iw_fatal_error(rdev)) { + kfree_skb(skb); return -EIO; + } tpt = kmalloc_obj(*tpt); - if (!tpt) + if (!tpt) { + kfree_skb(skb); return -ENOMEM; + } stag_state = stag_state > 0; stag_idx = (*stag) >> 8; @@ -290,6 +277,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, rdev->stats.stag.fail++; mutex_unlock(&rdev->stats.lock); kfree(tpt); + kfree_skb(skb); return -ENOMEM; } mutex_lock(&rdev->stats.lock); @@ -463,8 +451,10 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc) FW_RI_STAG_NSMR, mhp->attr.perms, mhp->attr.mw_bind_enable, 0, 0, ~0ULL, 0, 0, 0, NULL, mhp->wr_waitp); - if (ret) - goto err_free_skb; + if (ret) { + kfree_skb(mhp->dereg_skb); + goto err_free_wr_wait; + } ret = finish_mem_reg(mhp, stag); if (ret) @@ -473,8 +463,6 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc) err_dereg_mem: dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, mhp->attr.pbl_addr, mhp->dereg_skb, mhp->wr_waitp); -err_free_skb: - kfree_skb(mhp->dereg_skb); err_free_wr_wait: c4iw_put_wr_wait(mhp->wr_waitp); err_free_mhp: From 03826bc1fa6c90405bf05831f2b501a8368dcd27 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 26 Jul 2026 12:13:55 +0300 Subject: [PATCH 131/160] RDMA/mlx5: Fix stack out-of-bounds read in cc_params debugfs get_param() reads a congestion parameter as a u32 but formats it with the signed "%d" into an 11-byte stack buffer. A value with bit 31 set, such as 0x80000000, renders as "-2147483648\n" whose full length is 12. snprintf() stores only 11 bytes yet returns 12, so simple_read_from_buffer() treats 12 bytes as valid and reads one byte past lbuf[]. Size the buffer for the widest unsigned decimal, format with "%u" to match the u32, and use scnprintf() so the length passed to simple_read_from_buffer() reflects the bytes actually stored. Fixes: 4a2da0b8c0782 ("IB/mlx5: Add debug control parameters for congestion control") Link: https://patch.msgid.link/20260726-get-param-leaks-kernel-stack-memory-v1-1-d61a4d39662d@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/cong.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c index 98c90d0dbab4..e91bf278ab97 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -389,15 +389,13 @@ static ssize_t get_param(struct file *filp, char __user *buf, size_t count, int offset = param->offset; u32 var = 0; int ret; - char lbuf[11]; + char lbuf[12]; ret = mlx5_ib_get_cc_params(param->dev, param->port_num, offset, &var); if (ret) return ret; - ret = snprintf(lbuf, sizeof(lbuf), "%d\n", var); - if (ret < 0) - return ret; + ret = scnprintf(lbuf, sizeof(lbuf), "%u\n", var); return simple_read_from_buffer(buf, count, pos, lbuf, ret); } From 033a79e308e4fe832b0924347eda8c4364055174 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 26 Jul 2026 12:22:11 +0300 Subject: [PATCH 132/160] RDMA/mlx5: Send cong param changes to the resolved port mdev mlx5_ib_set_cc_params() resolves the port-specific mlx5_core_dev via mlx5_ib_get_native_port_mdev() but issued MLX5_CMD_OP_MODIFY_CONG_PARAMS through dev->mdev. On an affiliated secondary RoCE port those pointers refer to different devices, so a write to the secondary port's cc_params debugfs file either altered the master port or failed with a master-side command error, while the read path already used the resolved mdev and returned the unchanged secondary value. Issue the command to the resolved mdev, the same device whose capabilities were checked when its debugfs directory was created. It is already referenced by the get/put pair, so its lifetime is safe. Fixes: 31578defe4eb ("RDMA/mlx5: Update mlx5_ib to use new cmd interface") Link: https://patch.msgid.link/20260726-mlx5-ib-set-cc-params-applies-conges-v1-1-a253edafe1f3@nvidia.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/cong.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c index e91bf278ab97..a775b4c86e11 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -361,7 +361,7 @@ static int mlx5_ib_set_cc_params(struct mlx5_ib_dev *dev, u32 port_num, MLX5_SET(field_select_r_roce_rp, field, field_select_r_roce_rp, attr_mask); - err = mlx5_cmd_exec_in(dev->mdev, modify_cong_params, in); + err = mlx5_cmd_exec_in(mdev, modify_cong_params, in); kvfree(in); alloc_err: mlx5_ib_put_native_port_mdev(dev, port_num + 1); From fdfb5cea4bf070cdb31d997efd87bb684df041fd Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 26 Jul 2026 15:21:44 +0300 Subject: [PATCH 133/160] RDMA/cxgb4: free STAG index when TPT entry write fails write_tpt_entry() allocates a new STAG index with c4iw_get_resource() and bumps stats.stag.cur before programming the entry. When write_adapter_mem() fails, it returns the error without releasing the index or reversing the statistic. No MR is inserted into rhp->mrs, so deregistration never reclaims it, leaking the index until device teardown. Record whether this call allocated the index and, on a failed write, return it to tpt_table and decrement stats.stag.cur. Key the rollback on both the write error and that flag, not the error alone: a non-reset update carries a caller-owned STAG that this call did not allocate and must not free. Fixes: ec3eead21718 ("RDMA/cxgb4: Remove kfifo usage") Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/cxgb4/mem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 1413bd0c0752..c28f76a32d90 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -254,6 +254,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, int err; struct fw_ri_tpte *tpt; u32 stag_idx; + bool stag_idx_allocated = false; static atomic_t key; if (c4iw_fatal_error(rdev)) { @@ -281,6 +282,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, return -ENOMEM; } mutex_lock(&rdev->stats.lock); + stag_idx_allocated = true; rdev->stats.stag.cur += 32; if (rdev->stats.stag.cur > rdev->stats.stag.max) rdev->stats.stag.max = rdev->stats.stag.cur; @@ -315,7 +317,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, (rdev->lldi.vr->stag.start >> 5), sizeof(*tpt), tpt, skb, wr_waitp); - if (reset_tpt_entry) { + if (reset_tpt_entry || (err && stag_idx_allocated)) { c4iw_put_resource(&rdev->resource.tpt_table, stag_idx); mutex_lock(&rdev->stats.lock); rdev->stats.stag.cur -= 32; From 957f92ea4022fb6af4618271615a2a21a7b5bef9 Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Mon, 27 Jul 2026 01:39:30 +0900 Subject: [PATCH 134/160] IB/isert: reject PDUs declaring more data than was received isert_recv_done() hands each received PDU to the opcode handlers without ever looking at wc->byte_len, the number of bytes the HCA actually placed in the receive descriptor. The handlers then copy that many bytes - the data-segment length the initiator declared in the BHS (ntoh24(hdr->dlength), via the derived unsol_data_len / imm_data_len) - out of the fixed-size descriptor: isert_handle_iscsi_dataout(): sg_copy_from_buffer(sg_start, sg_nents, isert_get_data(rx_desc), unsol_data_len); isert_handle_scsi_cmd(): sg_copy_from_buffer(cmd->se_cmd.t_data_sg, sg_nents, isert_get_data(rx_desc), imm_data_len); Because the declared length is never checked against wc->byte_len, an initiator can declare a data segment larger than the bytes it actually sent (and larger than the descriptor) and cause an out-of-bounds read of the receive buffer. Nothing upstream of isert closes this door: - __iscsit_check_dataout_hdr() bounds the inbound payload against conn_ops->MaxXmitDataSegmentLength (MXDSL) - a transmit parameter, used here for the inbound check. - iscsi_set_connection_parameters() sets ops->MaxXmitDataSegmentLength = ops->TargetRecvDataSegmentLength; and TARGETRECVDATASEGMENTLENGTH is absent from the min()-clamp list in iscsi_check_acceptor_state(), so the value the initiator declares is adopted verbatim (type range 512..16777215). The initiator effectively raises its own ceiling. - isert never clamps the negotiated value to its own fixed receive descriptor (ISER_RX_SIZE, 9216 bytes), so the target core's bound and the descriptor size are unrelated. The imm_data_len == data_len path is more than an over-read: it aliases the receive descriptor via sg_set_buf() and passes it to the backend as the data source for the SCSI WRITE, so an over-declared length causes heap contents past the descriptor to be written through the backend to the backing store. The backend is the victim of the oversized scatterlist isert hands it, not the cause; no read-back of the written bytes was demonstrated. Trigger: after login completes (full feature phase), an initiator that has declared a large TargetRecvDataSegmentLength and a FirstBurstLength that permits unsolicited/immediate data sends a PDU whose declared data-segment length exceeds what was received. With KASAN: BUG: KASAN: slab-out-of-bounds in sg_copy_buffer+0x150/0x1c0 Read of size 4096 at addr ffff888109720800 by task kworker/1:0H/25 Workqueue: ib-comp-wq ib_cq_poll_work Call Trace: sg_copy_buffer+0x150/0x1c0 isert_recv_done+0xba6/0x2390 __ib_process_cq+0xe1/0x390 ib_cq_poll_work+0x46/0x150 isert_recv_done+0xba6 resolves to isert_handle_iscsi_dataout() (ib_isert.c:1160), inlined through isert_rx_opcode(). Validate wc->byte_len against the framing in isert_recv_done() before the PDU reaches any handler, and reinstate the connection if it is short. Because the test compares without subtracting the header length, it also rejects PDUs shorter than the iSER and iSCSI headers, which would otherwise be parsed out of stale descriptor contents. The login handler rejects PDUs shorter than ISER_HEADERS_LEN (commit 29e7b925ae6d ("IB/isert: Reject login PDUs shorter than ISER_HEADERS_LEN")) but does not bound the declared length either; that is fixed in the next patch. The data handlers had no length check at all. isert reads the data segment from a fixed offset: isert_get_data() returns the iSER header plus ISER_HEADERS_LEN and makes no adjustment for an AHS. The bytes the handlers touch are therefore exactly [ISER_HEADERS_LEN, ISER_HEADERS_LEN + dlength), and comparing that sum against wc->byte_len bounds precisely the region that is read. An AHS term would only make the test stricter without bounding anything further, and cannot cause a false reject: a PDU carrying an AHS is longer, not shorter. This is a memory-safety fix that verifies the bytes that were actually received; it does not touch RFC 7145 length negotiation and is not the MaxXmitDataSegmentLength negotiation redesign raised in the 2017 "[Query] iSER-Target: QP errors observed on increasing MaxXmitDataSegmentLength" discussion. That redesign is explicitly out of scope here. The patched kernel rejects the malformed DataOut PDU and both immediate-data variants with "PDU declares ... bytes were received" and continues to pass normal traffic with no regression. Reproduced with soft-RoCE (rdma_rxe) and a raw rdma_cm/ibv initiator; no kernel-side test hooks were needed. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260726163931.971063-2-yhlee@isslab.korea.ac.kr Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/isert/ib_isert.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 4691845bf815..9e050d034005 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1332,6 +1332,21 @@ isert_recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(ib_dev, rx_desc->dma_addr, ISER_RX_SIZE, DMA_FROM_DEVICE); + /* + * The data segment length declared in the BHS is attacker controlled + * and is used further down to read that many bytes out of the fixed + * size receive descriptor, so it has to be checked against the number + * of bytes that were actually received. Comparing without subtracting + * also rejects PDUs shorter than the iSER and iSCSI headers, which + * would otherwise be parsed out of stale descriptor contents. + */ + if (unlikely(wc->byte_len < ISER_HEADERS_LEN + ntoh24(hdr->dlength))) { + isert_err("PDU declares %u data bytes but only %u bytes were received\n", + ntoh24(hdr->dlength), wc->byte_len); + iscsit_cause_connection_reinstatement(isert_conn->conn, 0); + return; + } + isert_dbg("DMA: 0x%llx, iSCSI opcode: 0x%02x, ITT: 0x%08x, flags: 0x%02x dlen: %d\n", rx_desc->dma_addr, hdr->opcode, hdr->itt, hdr->flags, (int)(wc->byte_len - ISER_HEADERS_LEN)); From 2488b5b4827e5415768afc8daf097e8eb83c98df Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Mon, 27 Jul 2026 01:39:31 +0900 Subject: [PATCH 135/160] IB/isert: reject login PDUs declaring more data than was received isert_login_recv_done() records how many bytes the HCA actually placed in the login buffer, but nothing compares that against the length the login PDU's BHS declares. isert_rx_login_req() copies min(login_req_len, MAX_KEY_VALUE_PAIRS) bytes into login->req_buf, and the login code then reads the declared length back out of that buffer - for the first PDU in iscsi_target_locate_portal(), payload_length = ntoh24(login_req->dlength); tmpbuf = kmemdup_nul(login->req_buf, payload_length, GFP_KERNEL); and for the ones after it in iscsi_decode_text_input(), reached from iscsi_target_do_login(). login->req_buf is a fixed MAX_KEY_VALUE_PAIRS (8192) byte allocation, so an initiator that declares more than it sends reads off the end of it, before authentication and with the length under its control: BUG: KASAN: slab-out-of-bounds in kmemdup_nul+0x43/0x80 Read of size 8193 at addr ffff8881056a8000 by task iscsi_np/167 __asan_memcpy+0x23/0x60 kmemdup_nul+0x43/0x80 iscsi_target_locate_portal+0x48d/0x1180 iscsi_target_login_thread+0x19a9/0x3350 Allocated by task 167: __kmalloc_cache_noprof+0x158/0x370 iscsi_target_login_thread+0x971/0x3350 which belongs to the cache kmalloc-8k of size 8192 allocated 8192-byte region Falsifying the second login PDU instead reaches the other reader, on the same buffer: BUG: KASAN: slab-out-of-bounds in kmemdup_nul+0x43/0x80 Read of size 8193 at addr ffff888104d10000 by task kworker/1:1/50 Workqueue: isert_login_wq iscsi_target_do_login_rx __asan_memcpy+0x23/0x60 kmemdup_nul+0x43/0x80 iscsi_decode_text_input+0xc6/0x11c0 iscsi_target_do_login+0x261/0x1470 iscsi_target_do_login_rx+0x51d/0x7d0 iscsit over TCP is not exposed: iscsit_get_login_rx() validates the declared length with iscsi_target_check_login_request() and then reads exactly that many bytes off the socket, so the declared length governs how much arrives rather than how much is copied out of an already-filled buffer. isert does not call iscsi_target_check_login_request() at all. Reject a login PDU whose declared DataSegmentLength exceeds what was received, in both paths that reach isert_rx_login_req(): isert_get_login_rx() for the first login PDU and isert_login_recv_done() for the ones after it. dlength <= login_req_len is allowed because the received count can include up to three bytes of iSCSI padding. Once the check is in place the copy out can no longer exceed the copy in: the posted login SGE is ISER_RX_PAYLOAD_SIZE, so login_req_len cannot exceed MAX_KEY_VALUE_PAIRS and the min() in isert_rx_login_req() is login_req_len. Like the existing short-PDU check added by 29e7b925ae6d, the reject in isert_login_recv_done() returns without completing login_req_comp, so a malformed subsequent PDU leaves the login to be torn down by the login timer rather than failing immediately. The first-PDU path returns an error and fails straight away. Reproduced on 7.2.0-rc4 with soft-RoCE (rdma_rxe) under KASAN, using an initiator that sends the real key=value payload while declaring 8193 in the BHS, on the first login PDU and on the second in separate runs. The reported read size tracks the declared value exactly; 16384 and 61440 behave the same. Unpatched 3 of 3 runs report on each of the two paths, patched 0 of 3 on both, run alternately in a single session, and a normal login still completes on the patched build. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Suggested-by: Leon Romanovsky Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260726163931.971063-3-yhlee@isslab.korea.ac.kr Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/isert/ib_isert.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 9e050d034005..e57545cf337a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -970,6 +970,21 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login, return 0; } +static int +isert_check_login_req(struct isert_conn *isert_conn) +{ + struct iscsi_hdr *hdr = isert_get_iscsi_hdr(isert_conn->login_desc); + u32 dlength = ntoh24(hdr->dlength); + + if (unlikely(dlength > (u32)isert_conn->login_req_len)) { + isert_dbg("login PDU declares %u data bytes but only %d were received\n", + dlength, isert_conn->login_req_len); + return -EINVAL; + } + + return 0; +} + static void isert_rx_login_req(struct isert_conn *isert_conn) { @@ -1408,8 +1423,12 @@ isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc) if (isert_conn->conn) { struct iscsi_login *login = isert_conn->conn->conn_login; - if (login && !login->first_request) + if (login && !login->first_request) { + if (isert_check_login_req(isert_conn)) + return; + isert_rx_login_req(isert_conn); + } } mutex_lock(&isert_conn->mutex); @@ -2374,6 +2393,10 @@ isert_get_login_rx(struct iscsit_conn *conn, struct iscsi_login *login) if (!login->first_request) return 0; + ret = isert_check_login_req(isert_conn); + if (ret) + return ret; + isert_rx_login_req(isert_conn); isert_info("before login_comp conn: %p\n", conn); From 43598807f71ac1c9164f26004acf2496d4038daf Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 28 Jul 2026 11:34:03 -0700 Subject: [PATCH 136/160] IB/mad: cleanup all kernel-doc comments Add a leading "struct" keyword to struct descriptions: Warning: ../include/uapi/rdma/ib_user_mad.h:72 cannot understand function prototype: 'struct ib_user_mad_hdr_old' Warning: ../include/uapi/rdma/ib_user_mad.h:116 cannot understand function prototype: 'struct ib_user_mad_hdr' Warning: ../include/uapi/rdma/ib_user_mad.h:143 cannot understand function prototype: 'struct ib_user_mad' Warning: ../include/uapi/rdma/ib_user_mad.h:184 cannot understand function prototype: 'struct ib_user_mad_reg_req' Warning: ../include/uapi/rdma/ib_user_mad.h:216 cannot understand function prototype: 'enum' One enum and #define are moved out of line so that the struct description and the struct block are not split (separated). When this is done, more warnings are exposed: Warning: include/uapi/rdma/ib_user_mad.h:89 struct member 'length' not described in 'ib_user_mad_hdr_old' Warning: include/uapi/rdma/ib_user_mad.h:135 struct member 'length' not described in 'ib_user_mad_hdr' Warning: include/uapi/rdma/ib_user_mad.h:135 struct member 'reserved' not described in 'ib_user_mad_hdr' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'id' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'qpn' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'mgmt_class' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'mgmt_class_version' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'res' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'flags' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'method_mask' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'oui' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'rmpp_version' not described in 'ib_user_mad_reg_req2' Warning: include/uapi/rdma/ib_user_mad.h:227 struct member 'reserved' not described in 'ib_user_mad_reg_req2' These are all repaired by using ':' as the separator between the struct member @name and its description (instead of '-'). Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260728183403.1136827-1-rdunlap@infradead.org Signed-off-by: Leon Romanovsky --- include/uapi/rdma/ib_user_mad.h | 95 +++++++++++++++++---------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/include/uapi/rdma/ib_user_mad.h b/include/uapi/rdma/ib_user_mad.h index 10b5f6a4c677..cd038bc0ef1a 100644 --- a/include/uapi/rdma/ib_user_mad.h +++ b/include/uapi/rdma/ib_user_mad.h @@ -51,24 +51,24 @@ */ /** - * ib_user_mad_hdr_old - Old version of MAD packet header without pkey_index - * @id - ID of agent MAD received with/to be sent with - * @status - 0 on successful receive, ETIMEDOUT if no response + * struct ib_user_mad_hdr_old - Old version of MAD packet header without pkey_index + * @id: ID of agent MAD received with/to be sent with + * @status: 0 on successful receive, ETIMEDOUT if no response * received (transaction ID in data[] will be set to TID of original * request) (ignored on send) - * @timeout_ms - Milliseconds to wait for response (unset on receive) - * @retries - Number of automatic retries to attempt - * @qpn - Remote QP number received from/to be sent to - * @qkey - Remote Q_Key to be sent with (unset on receive) - * @lid - Remote lid received from/to be sent to - * @sl - Service level received with/to be sent with - * @path_bits - Local path bits received with/to be sent with - * @grh_present - If set, GRH was received/should be sent - * @gid_index - Local GID index to send with (unset on receive) - * @hop_limit - Hop limit in GRH - * @traffic_class - Traffic class in GRH - * @gid - Remote GID in GRH - * @flow_label - Flow label in GRH + * @timeout_ms: Milliseconds to wait for response (unset on receive) + * @retries: Number of automatic retries to attempt + * @qpn: Remote QP number received from/to be sent to + * @qkey: Remote Q_Key to be sent with (unset on receive) + * @lid: Remote lid received from/to be sent to + * @sl: Service level received with/to be sent with + * @path_bits: Local path bits received with/to be sent with + * @grh_present: If set, GRH was received/should be sent + * @gid_index: Local GID index to send with (unset on receive) + * @hop_limit: Hop limit in GRH + * @traffic_class: Traffic class in GRH + * @gid: Remote GID in GRH + * @flow_label: Flow label in GRH */ struct ib_user_mad_hdr_old { __u32 id; @@ -90,29 +90,29 @@ struct ib_user_mad_hdr_old { }; /** - * ib_user_mad_hdr - MAD packet header + * struct ib_user_mad_hdr - MAD packet header * This layout allows specifying/receiving the P_Key index. To use * this capability, an application must call the * IB_USER_MAD_ENABLE_PKEY ioctl on the user MAD file handle before * any other actions with the file handle. - * @id - ID of agent MAD received with/to be sent with - * @status - 0 on successful receive, ETIMEDOUT if no response + * @id: ID of agent MAD received with/to be sent with + * @status: 0 on successful receive, ETIMEDOUT if no response * received (transaction ID in data[] will be set to TID of original * request) (ignored on send) - * @timeout_ms - Milliseconds to wait for response (unset on receive) - * @retries - Number of automatic retries to attempt - * @qpn - Remote QP number received from/to be sent to - * @qkey - Remote Q_Key to be sent with (unset on receive) - * @lid - Remote lid received from/to be sent to - * @sl - Service level received with/to be sent with - * @path_bits - Local path bits received with/to be sent with - * @grh_present - If set, GRH was received/should be sent - * @gid_index - Local GID index to send with (unset on receive) - * @hop_limit - Hop limit in GRH - * @traffic_class - Traffic class in GRH - * @gid - Remote GID in GRH - * @flow_label - Flow label in GRH - * @pkey_index - P_Key index + * @timeout_ms: Milliseconds to wait for response (unset on receive) + * @retries: Number of automatic retries to attempt + * @qpn: Remote QP number received from/to be sent to + * @qkey: Remote Q_Key to be sent with (unset on receive) + * @lid: Remote lid received from/to be sent to + * @sl: Service level received with/to be sent with + * @path_bits: Local path bits received with/to be sent with + * @grh_present: If set, GRH was received/should be sent + * @gid_index: Local GID index to send with (unset on receive) + * @hop_limit: Hop limit in GRH + * @traffic_class: Traffic class in GRH + * @gid: Remote GID in GRH + * @flow_label: Flow label in GRH + * @pkey_index: P_Key index */ struct ib_user_mad_hdr { __u32 id; @@ -136,9 +136,9 @@ struct ib_user_mad_hdr { }; /** - * ib_user_mad - MAD packet - * @hdr - MAD packet header - * @data - Contents of MAD + * struct ib_user_mad - MAD packet + * @hdr: MAD packet header + * @data: Contents of MAD * */ struct ib_user_mad { @@ -167,15 +167,15 @@ typedef unsigned long __attribute__((aligned(4))) packed_ulong; #define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof (long))) /** - * ib_user_mad_reg_req - MAD registration request - * @id - Set by the kernel; used to identify agent in future requests. - * @qpn - Queue pair number; must be 0 or 1. - * @method_mask - The caller will receive unsolicited MADs for any method + * struct ib_user_mad_reg_req - MAD registration request + * @id: Set by the kernel; used to identify agent in future requests. + * @qpn: Queue pair number; must be 0 or 1. + * @method_mask: The caller will receive unsolicited MADs for any method * where @method_mask = 1. - * @mgmt_class - Indicates which management class of MADs should be receive + * @mgmt_class: Indicates which management class of MADs should be receive * by the caller. This field is only required if the user wishes to * receive unsolicited MADs, otherwise it should be 0. - * @mgmt_class_version - Indicates which version of MADs for the given + * @mgmt_class_version: Indicates which version of MADs for the given * management class to receive. * @oui: Indicates IEEE OUI when mgmt_class is a vendor class * in the range from 0x30 to 0x4f. Otherwise not used. @@ -193,7 +193,7 @@ struct ib_user_mad_reg_req { }; /** - * ib_user_mad_reg_req2 - MAD registration request + * struct ib_user_mad_reg_req2 - MAD registration request * * @id - Set by the _kernel_; used by userspace to identify the * registered agent in future requests. @@ -214,10 +214,6 @@ struct ib_user_mad_reg_req { * used. * @rmpp_version - If set, indicates the RMPP version to use. */ -enum { - IB_USER_MAD_USER_RMPP = (1 << 0), -}; -#define IB_USER_MAD_REG_FLAGS_CAP (IB_USER_MAD_USER_RMPP) struct ib_user_mad_reg_req2 { __u32 id; __u32 qpn; @@ -231,4 +227,9 @@ struct ib_user_mad_reg_req2 { __u8 reserved[3]; }; +enum { + IB_USER_MAD_USER_RMPP = (1 << 0), +}; +#define IB_USER_MAD_REG_FLAGS_CAP (IB_USER_MAD_USER_RMPP) + #endif /* IB_USER_MAD_H */ From 09d09e5d79f3174bc082fcea85743a6ce4b69016 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 28 Jul 2026 14:15:00 +0000 Subject: [PATCH 137/160] RDMA/core: Prevent rereg_mr for dmabuf umems The rereg_mr method has always been explicitly blocked in rdma-core for dmabuf MRs anyway, so add a check to the ib_umem_check_rereg helper so that each driver doesn't need to handle it. Depending on how the driver handled rereg_mr, this also has the benefit of preventing rereg_mr from being used to add the IB_ACCESS_MW_BIND flag to a dmabuf MR. This flag is not allowed during registration, so it seems sensible to prevent it from being added back with rereg_mr. Preventing IB_ACCESS_MW_BIND is important for drivers that support revocable dmabufs and implement "revoke" by issuing a dereg_mr command to the HW because most(?) HW will reject this command if the MR has windows bound to it, and a failure to revoke is supposed to trigger a function reset. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260728141501.1425737-1-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/umem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 81f44dadfa52..88110b9661f5 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -677,6 +677,9 @@ int ib_umem_check_rereg(struct ib_umem *umem, int flags, int new_access_flags) if (!umem) return 0; + if (umem->is_dmabuf) + return -EOPNOTSUPP; + if ((flags & IB_MR_REREG_ACCESS) && !(flags & IB_MR_REREG_TRANS)) if (ib_access_writable(new_access_flags) && !umem->writable) return -EACCES; From d24ce259185e76be64ac9a895057371be548cd39 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 28 Jul 2026 14:15:01 +0000 Subject: [PATCH 138/160] RDMA/irdma: Remove dmabuf checks in rereg_mr Now that ib_umem_check_rereg entirely blocks rereg for dmabuf umems, the extra logic can be removed from irdma. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260728141501.1425737-2-jmoroni@google.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/irdma/verbs.c | 31 +---------------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 16c80367270f..5e03cf39fa6e 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3885,8 +3885,6 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, struct irdma_device *iwdev = to_iwdev(ib_mr->device); struct irdma_mr *iwmr = to_iwmr(ib_mr); struct irdma_pbl *iwpbl = &iwmr->iwpbl; - bool dmabuf_revocable = iwmr->region && iwmr->region->is_dmabuf; - struct ib_umem_dmabuf *umem_dmabuf; int ret; ret = ib_no_udata_io(udata); @@ -3903,26 +3901,9 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (ret) return ERR_PTR(ret); - if (dmabuf_revocable) { - umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); - - ib_umem_dmabuf_revoke_lock(umem_dmabuf); - - /* If the dmabuf has been revoked, it means that the region has - * been invalidated in HW. We must not allow it to become valid - * again unless the user is requesting a change in translation - * which will end up dropping the umem dmabuf and allocating an - * entirely new umem anyway. - */ - if (umem_dmabuf->revoked && !(flags & IB_MR_REREG_TRANS)) { - ret = -EINVAL; - goto err_unlock; - } - } - ret = irdma_hwdereg_mr(ib_mr); if (ret) - goto err_unlock; + return ERR_PTR(ret); if (flags & IB_MR_REREG_ACCESS) iwmr->access = new_access; @@ -3939,12 +3920,6 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, iwpbl->pbl_allocated = false; } - if (dmabuf_revocable) { - /* Must unlock before release to prevent deadlock */ - ib_umem_dmabuf_revoke_unlock(umem_dmabuf); - dmabuf_revocable = false; - } - if (iwmr->region) { ib_umem_release(iwmr->region); iwmr->region = NULL; @@ -3955,10 +3930,6 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, ret = irdma_hwreg_mr(iwdev, iwmr, iwmr->access); } -err_unlock: - if (dmabuf_revocable) - ib_umem_dmabuf_revoke_unlock(umem_dmabuf); - return ret ? ERR_PTR(ret) : NULL; } From a489ab44ab73a6dfab8bf2556f647364ee0e0940 Mon Sep 17 00:00:00 2001 From: Konstantin Taranov Date: Thu, 23 Jul 2026 04:59:54 -0700 Subject: [PATCH 139/160] RDMA/mana_ib: unify QP lookup table Add helpers to retrieve the send and receive queues of a QP. Use these helpers when storing queue IDs in the lookup table. MANA queue IDs are 2-bit aligned, allowing the two least significant bits to be omitted when storing and looking up queue IDs. Signed-off-by: Konstantin Taranov Link: https://patch.msgid.link/20260723115955.1859519-2-kotaranov@linux.microsoft.com Reviewed-by: Long Li Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mana/mana_ib.h | 30 ++++++++ drivers/infiniband/hw/mana/qp.c | 103 ++++++++++++--------------- 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h index da05966aff19..18688072fd89 100644 --- a/drivers/infiniband/hw/mana/mana_ib.h +++ b/drivers/infiniband/hw/mana/mana_ib.h @@ -26,6 +26,8 @@ /* Send queue ID mask */ #define MANA_SENDQ_MASK BIT(31) +/* Queue ID encodes type in the lower 2 bits */ +#define MANA_QID_SUBTYPE_MASK 0x3 /* * The hardware limit of number of MRs is greater than maximum number of MRs @@ -582,12 +584,40 @@ static inline struct gdma_context *mdev_to_gc(struct mana_ib_dev *mdev) return mdev->gdma_dev->gdma_context; } +static inline struct mana_ib_queue *mana_qp_get_sq(struct mana_ib_qp *qp) +{ + switch (qp->ibqp.qp_type) { + case IB_QPT_RC: + return &qp->rc_qp.queues[MANA_RC_SEND_QUEUE_REQUESTER]; + case IB_QPT_UD: + case IB_QPT_GSI: + return &qp->ud_qp.queues[MANA_UD_SEND_QUEUE]; + default: + return NULL; + } +} + +static inline struct mana_ib_queue *mana_qp_get_rq(struct mana_ib_qp *qp) +{ + switch (qp->ibqp.qp_type) { + case IB_QPT_RC: + return &qp->rc_qp.queues[MANA_RC_RECV_QUEUE_RESPONDER]; + case IB_QPT_UD: + case IB_QPT_GSI: + return &qp->ud_qp.queues[MANA_UD_RECV_QUEUE]; + default: + return NULL; + } +} + static inline struct mana_ib_qp *mana_get_qp_ref(struct mana_ib_dev *mdev, u32 qid, bool is_sq) { struct mana_ib_qp *qp; unsigned long flag; + /* Remove subtype bits */ + qid &= ~MANA_QID_SUBTYPE_MASK; if (is_sq) qid |= MANA_SENDQ_MASK; diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index c52f3ec14032..47e1fadbda05 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -461,89 +461,74 @@ static enum gdma_queue_type mana_ib_queue_type(struct ib_qp_init_attr *attr, u32 return type; } -static int mana_table_store_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) -{ - return xa_insert_irq(&mdev->qp_table_wq, qp->ibqp.qp_num, qp, - GFP_KERNEL); -} - -static void mana_table_remove_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) -{ - xa_erase_irq(&mdev->qp_table_wq, qp->ibqp.qp_num); -} - static void mana_table_drain_qp_ref(struct mana_ib_qp *qp) { mana_put_qp_ref(qp); wait_for_completion(&qp->free); } -static int mana_table_store_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) +static int mana_table_store_qp_qid(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, + u32 qid, bool is_sq) { - u32 qids = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].id | MANA_SENDQ_MASK; - u32 qidr = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].id; - int err; + /* Remove subtype bits */ + qid &= ~MANA_QID_SUBTYPE_MASK; + if (is_sq) + qid |= MANA_SENDQ_MASK; - err = xa_insert_irq(&mdev->qp_table_wq, qids, qp, GFP_KERNEL); - if (err) - return err; - - err = xa_insert_irq(&mdev->qp_table_wq, qidr, qp, GFP_KERNEL); - if (err) - goto remove_sq; - - return 0; - -remove_sq: - xa_erase_irq(&mdev->qp_table_wq, qids); - mana_table_drain_qp_ref(qp); - return err; + return xa_insert_irq(&mdev->qp_table_wq, qid, qp, GFP_KERNEL); } -static void mana_table_remove_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) +static void mana_table_remove_qp_qid(struct mana_ib_dev *mdev, u32 qid, bool is_sq) { - u32 qids = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].id | MANA_SENDQ_MASK; - u32 qidr = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].id; + /* Remove subtype bits */ + qid &= ~MANA_QID_SUBTYPE_MASK; + if (is_sq) + qid |= MANA_SENDQ_MASK; - xa_erase_irq(&mdev->qp_table_wq, qids); - xa_erase_irq(&mdev->qp_table_wq, qidr); + xa_erase_irq(&mdev->qp_table_wq, qid); } static int mana_table_store_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) { + struct mana_ib_queue *sq, *rq; + int err; + refcount_set(&qp->refcount, 1); init_completion(&qp->free); + sq = mana_qp_get_sq(qp); + rq = mana_qp_get_rq(qp); - switch (qp->ibqp.qp_type) { - case IB_QPT_RC: - return mana_table_store_rc_qp(mdev, qp); - case IB_QPT_UD: - case IB_QPT_GSI: - return mana_table_store_ud_qp(mdev, qp); - default: - ibdev_dbg(&mdev->ib_dev, "Unknown QP type for storing in mana table, %d\n", - qp->ibqp.qp_type); - } + if (!sq || !rq) + return -EINVAL; - return -EINVAL; + err = mana_table_store_qp_qid(mdev, qp, sq->id, true); + if (err) + return err; + + err = mana_table_store_qp_qid(mdev, qp, rq->id, false); + if (err) + goto err_remove_sq; + + return 0; + +err_remove_sq: + mana_table_remove_qp_qid(mdev, sq->id, true); + mana_table_drain_qp_ref(qp); + return err; } -static void mana_table_remove_qp(struct mana_ib_dev *mdev, - struct mana_ib_qp *qp) +static void mana_table_remove_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) { - switch (qp->ibqp.qp_type) { - case IB_QPT_RC: - mana_table_remove_rc_qp(mdev, qp); - break; - case IB_QPT_UD: - case IB_QPT_GSI: - mana_table_remove_ud_qp(mdev, qp); - break; - default: - ibdev_dbg(&mdev->ib_dev, "Unknown QP type for removing from mana table, %d\n", - qp->ibqp.qp_type); + struct mana_ib_queue *sq, *rq; + + sq = mana_qp_get_sq(qp); + rq = mana_qp_get_rq(qp); + + if (!sq || !rq) return; - } + + mana_table_remove_qp_qid(mdev, sq->id, true); + mana_table_remove_qp_qid(mdev, rq->id, false); mana_table_drain_qp_ref(qp); } From 6aad80a1d4e70cf6adac73e12e15abee3100dbe7 Mon Sep 17 00:00:00 2001 From: Konstantin Taranov Date: Thu, 23 Jul 2026 04:59:55 -0700 Subject: [PATCH 140/160] RDMA/mana_ib: UC QP support for UAPI Implement UC QP creation in the RNIC HW for user API. An UC QP is exposed as three work queues: send, receive, and memory management. The latter is used for bind and invalidate WQEs to support memory windows. Signed-off-by: Konstantin Taranov Link: https://patch.msgid.link/20260723115955.1859519-3-kotaranov@linux.microsoft.com Reviewed-by: Long Li Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mana/main.c | 49 ++++++++++++++-- drivers/infiniband/hw/mana/mana_ib.h | 53 ++++++++++++++++-- drivers/infiniband/hw/mana/qp.c | 84 +++++++++++++++++++++++++++- include/uapi/rdma/mana-abi.h | 11 ++++ 4 files changed, 184 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index 151874529b56..e4414d208d4a 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -1072,16 +1072,55 @@ int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, return 0; } -int mana_ib_gd_destroy_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) +int mana_ib_gd_create_uc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, + struct ib_qp_init_attr *attr, u32 doorbell, u64 flags) { - struct mana_rnic_destroy_rc_qp_resp resp = {0}; - struct mana_rnic_destroy_rc_qp_req req = {0}; + struct mana_ib_cq *send_cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq); + struct mana_ib_cq *recv_cq = container_of(qp->ibqp.recv_cq, struct mana_ib_cq, ibcq); + struct mana_ib_pd *pd = container_of(qp->ibqp.pd, struct mana_ib_pd, ibpd); struct gdma_context *gc = mdev_to_gc(mdev); + struct mana_rnic_create_uc_qp_resp resp = {}; + struct mana_rnic_create_uc_qp_req req = {}; + int err, i; - mana_gd_init_req_hdr(&req.hdr, MANA_IB_DESTROY_RC_QP, sizeof(req), sizeof(resp)); + mana_gd_init_req_hdr(&req.hdr, MANA_IB_CREATE_UC_QP, sizeof(req), sizeof(resp)); req.hdr.dev_id = mdev->gdma_dev->dev_id; req.adapter = mdev->adapter_handle; - req.rc_qp_handle = qp->qp_handle; + req.pd_handle = pd->pd_handle; + req.send_cq_handle = send_cq->cq_handle; + req.recv_cq_handle = recv_cq->cq_handle; + for (i = 0; i < MANA_UC_QUEUE_TYPE_MAX; i++) + req.dma_region[i] = qp->uc_qp.queues[i].gdma_region; + req.doorbell_page = doorbell; + req.max_send_wr = attr->cap.max_send_wr; + req.max_recv_wr = attr->cap.max_recv_wr; + req.max_send_sge = attr->cap.max_send_sge; + req.max_recv_sge = attr->cap.max_recv_sge; + req.flags = flags; + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err) + return err; + + qp->qp_handle = resp.qp_handle; + for (i = 0; i < MANA_UC_QUEUE_TYPE_MAX; i++) { + qp->uc_qp.queues[i].id = resp.queue_ids[i]; + /* The GDMA regions are now owned by the RNIC QP handle */ + qp->uc_qp.queues[i].gdma_region = GDMA_INVALID_DMA_REGION; + } + return 0; +} + +int mana_ib_gd_destroy_rnic_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) +{ + struct mana_rnic_destroy_rnic_qp_resp resp = {0}; + struct mana_rnic_destroy_rnic_qp_req req = {0}; + struct gdma_context *gc = mdev_to_gc(mdev); + + mana_gd_init_req_hdr(&req.hdr, MANA_IB_DESTROY_RNIC_QP, sizeof(req), sizeof(resp)); + req.hdr.dev_id = mdev->gdma_dev->dev_id; + req.adapter = mdev->adapter_handle; + req.qp_handle = qp->qp_handle; return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h index 18688072fd89..f69866696776 100644 --- a/drivers/infiniband/hw/mana/mana_ib.h +++ b/drivers/infiniband/hw/mana/mana_ib.h @@ -182,6 +182,17 @@ struct mana_ib_rc_qp { struct mana_ib_queue queues[MANA_RC_QUEUE_TYPE_MAX]; }; +enum mana_uc_queue_type { + MANA_UC_SEND_QUEUE_REQUESTER = 0, + MANA_UC_RECV_QUEUE_RESPONDER, + MANA_UC_SEND_QUEUE_MMQ, + MANA_UC_QUEUE_TYPE_MAX, +}; + +struct mana_ib_uc_qp { + struct mana_ib_queue queues[MANA_UC_QUEUE_TYPE_MAX]; +}; + enum mana_ud_queue_type { MANA_UD_SEND_QUEUE = 0, MANA_UD_RECV_QUEUE, @@ -200,6 +211,7 @@ struct mana_ib_qp { union { struct mana_ib_queue raw_sq; struct mana_ib_rc_qp rc_qp; + struct mana_ib_uc_qp uc_qp; struct mana_ib_ud_qp ud_qp; }; @@ -235,8 +247,9 @@ enum mana_ib_command_code { MANA_IB_CREATE_CQ = 0x30008, MANA_IB_DESTROY_CQ = 0x30009, MANA_IB_CREATE_RC_QP = 0x3000a, - MANA_IB_DESTROY_RC_QP = 0x3000b, + MANA_IB_DESTROY_RNIC_QP = 0x3000b, MANA_IB_SET_QP_STATE = 0x3000d, + MANA_IB_CREATE_UC_QP = 0x30020, MANA_IB_QUERY_VF_COUNTERS = 0x30022, MANA_IB_QUERY_DEVICE_COUNTERS = 0x30023, }; @@ -386,16 +399,39 @@ struct mana_rnic_create_qp_resp { u32 reserved; }; /* HW Data*/ -struct mana_rnic_destroy_rc_qp_req { +struct mana_rnic_destroy_rnic_qp_req { struct gdma_req_hdr hdr; mana_handle_t adapter; - mana_handle_t rc_qp_handle; + mana_handle_t qp_handle; }; /* HW Data */ -struct mana_rnic_destroy_rc_qp_resp { +struct mana_rnic_destroy_rnic_qp_resp { struct gdma_resp_hdr hdr; }; /* HW Data */ +struct mana_rnic_create_uc_qp_req { + struct gdma_req_hdr hdr; + mana_handle_t adapter; + mana_handle_t pd_handle; + mana_handle_t send_cq_handle; + mana_handle_t recv_cq_handle; + u64 dma_region[MANA_UC_QUEUE_TYPE_MAX]; + u64 flags; + u32 doorbell_page; + u32 max_send_wr; + u32 max_recv_wr; + u32 max_send_sge; + u32 max_recv_sge; + u32 reserved; +}; /* HW Data */ + +struct mana_rnic_create_uc_qp_resp { + struct gdma_resp_hdr hdr; + mana_handle_t qp_handle; + u32 queue_ids[MANA_UC_QUEUE_TYPE_MAX]; + u32 reserved; +}; /* HW Data*/ + struct mana_rnic_create_udqp_req { struct gdma_req_hdr hdr; mana_handle_t adapter; @@ -589,6 +625,8 @@ static inline struct mana_ib_queue *mana_qp_get_sq(struct mana_ib_qp *qp) switch (qp->ibqp.qp_type) { case IB_QPT_RC: return &qp->rc_qp.queues[MANA_RC_SEND_QUEUE_REQUESTER]; + case IB_QPT_UC: + return &qp->uc_qp.queues[MANA_UC_SEND_QUEUE_REQUESTER]; case IB_QPT_UD: case IB_QPT_GSI: return &qp->ud_qp.queues[MANA_UD_SEND_QUEUE]; @@ -602,6 +640,8 @@ static inline struct mana_ib_queue *mana_qp_get_rq(struct mana_ib_qp *qp) switch (qp->ibqp.qp_type) { case IB_QPT_RC: return &qp->rc_qp.queues[MANA_RC_RECV_QUEUE_RESPONDER]; + case IB_QPT_UC: + return &qp->uc_qp.queues[MANA_UC_RECV_QUEUE_RESPONDER]; case IB_QPT_UD: case IB_QPT_GSI: return &qp->ud_qp.queues[MANA_UD_RECV_QUEUE]; @@ -766,8 +806,9 @@ int mana_ib_gd_destroy_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq); int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, struct ib_qp_init_attr *attr, u32 doorbell, u64 flags); -int mana_ib_gd_destroy_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp); - +int mana_ib_gd_destroy_rnic_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp); +int mana_ib_gd_create_uc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, + struct ib_qp_init_attr *attr, u32 doorbell, u64 flags); int mana_ib_gd_create_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, struct ib_qp_init_attr *attr, u32 doorbell, u32 type); int mana_ib_gd_destroy_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp); diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index 47e1fadbda05..8b7be1255c0d 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -597,13 +597,67 @@ static int mana_ib_create_rc_qp(struct ib_qp *ibqp, struct ib_pd *ibpd, return 0; destroy_qp: - mana_ib_gd_destroy_rc_qp(mdev, qp); + mana_ib_gd_destroy_rnic_qp(mdev, qp); destroy_queues: while (i-- > 0) mana_ib_destroy_queue(mdev, &qp->rc_qp.queues[i]); return err; } +static int mana_ib_create_uc_qp(struct ib_qp *ibqp, struct ib_pd *ibpd, + struct ib_qp_init_attr *attr, struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = container_of(ibpd->device, struct mana_ib_dev, ib_dev); + struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); + struct mana_ib_create_uc_qp_resp resp = {}; + struct mana_ib_ucontext *mana_ucontext; + struct mana_ib_create_uc_qp ucmd; + u64 flags = 0; + u32 doorbell; + int err, i; + + if (!udata) + return -EINVAL; + + mana_ucontext = rdma_udata_to_drv_context(udata, struct mana_ib_ucontext, ibucontext); + doorbell = mana_ucontext->doorbell; + err = ib_copy_validate_udata_in_cm(udata, ucmd, comp_mask, 0); + if (err) + return err; + + for (i = 0; i < MANA_UC_QUEUE_TYPE_MAX; ++i) { + err = mana_ib_create_queue(mdev, ucmd.queue_buf[i], ucmd.queue_size[i], + &qp->uc_qp.queues[i]); + if (err) + goto destroy_queues; + } + + err = mana_ib_gd_create_uc_qp(mdev, qp, attr, doorbell, flags); + if (err) + goto destroy_queues; + + qp->ibqp.qp_num = qp->uc_qp.queues[MANA_UC_RECV_QUEUE_RESPONDER].id; + qp->port = attr->port_num; + + for (i = 0; i < MANA_UC_QUEUE_TYPE_MAX; ++i) + resp.queue_id[i] = qp->uc_qp.queues[i].id; + + err = ib_respond_udata(udata, resp); + if (err) + goto destroy_qp; + + err = mana_table_store_qp(mdev, qp); + if (err) + goto destroy_qp; + return 0; +destroy_qp: + mana_ib_gd_destroy_rnic_qp(mdev, qp); +destroy_queues: + while (i-- > 0) + mana_ib_destroy_queue(mdev, &qp->uc_qp.queues[i]); + return err; +} + static void mana_add_qp_to_cqs(struct mana_ib_qp *qp) { struct mana_ib_cq *send_cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq); @@ -713,6 +767,8 @@ int mana_ib_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr, return mana_ib_create_qp_raw(ibqp, ibqp->pd, attr, udata); case IB_QPT_RC: return mana_ib_create_rc_qp(ibqp, ibqp->pd, attr, udata); + case IB_QPT_UC: + return mana_ib_create_uc_qp(ibqp, ibqp->pd, attr, udata); case IB_QPT_UD: case IB_QPT_GSI: return mana_ib_create_ud_qp(ibqp, ibqp->pd, attr, udata); @@ -803,6 +859,7 @@ int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, { switch (ibqp->qp_type) { case IB_QPT_RC: + case IB_QPT_UC: case IB_QPT_UD: case IB_QPT_GSI: return mana_ib_gd_modify_qp(ibqp, attr, attr_mask, udata); @@ -908,13 +965,34 @@ static int mana_ib_destroy_rc_qp(struct mana_ib_qp *qp, struct ib_udata *udata) /* Ignore return code as there is not much we can do about it. * The error message is printed inside. */ - mana_ib_gd_destroy_rc_qp(mdev, qp); + mana_ib_gd_destroy_rnic_qp(mdev, qp); for (i = 0; i < MANA_RC_QUEUE_TYPE_MAX; ++i) mana_ib_destroy_queue(mdev, &qp->rc_qp.queues[i]); return 0; } +static int mana_ib_destroy_uc_qp(struct mana_ib_qp *qp, struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = + container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); + int i, err; + + err = ib_no_udata_io(udata); + if (err) + return err; + + mana_table_remove_qp(mdev, qp); + /* Ignore return code as there is not much we can do about it. + * The error message is printed inside. + */ + mana_ib_gd_destroy_rnic_qp(mdev, qp); + for (i = 0; i < MANA_UC_QUEUE_TYPE_MAX; ++i) + mana_ib_destroy_queue(mdev, &qp->uc_qp.queues[i]); + + return 0; +} + static int mana_ib_destroy_ud_qp(struct mana_ib_qp *qp, struct ib_udata *udata) { struct mana_ib_dev *mdev = @@ -954,6 +1032,8 @@ int mana_ib_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) return mana_ib_destroy_qp_raw(qp, udata); case IB_QPT_RC: return mana_ib_destroy_rc_qp(qp, udata); + case IB_QPT_UC: + return mana_ib_destroy_uc_qp(qp, udata); case IB_QPT_UD: case IB_QPT_GSI: return mana_ib_destroy_ud_qp(qp, udata); diff --git a/include/uapi/rdma/mana-abi.h b/include/uapi/rdma/mana-abi.h index 410f0ddc8c89..32cbbfc80f99 100644 --- a/include/uapi/rdma/mana-abi.h +++ b/include/uapi/rdma/mana-abi.h @@ -57,6 +57,17 @@ struct mana_ib_create_rc_qp_resp { __u32 queue_id[4]; }; +struct mana_ib_create_uc_qp { + __aligned_u64 queue_buf[3]; + __u32 queue_size[3]; + __u32 comp_mask; +}; + +struct mana_ib_create_uc_qp_resp { + __u32 queue_id[3]; + __u32 reserved; +}; + struct mana_ib_create_wq { __aligned_u64 wq_buf_addr; __u32 wq_buf_size; From 98df2aee1459ee1c62c70cbe9b370d2a532aea36 Mon Sep 17 00:00:00 2001 From: Cheng Xu Date: Thu, 30 Jul 2026 20:43:53 +0800 Subject: [PATCH 141/160] RDMA/erdma: Hold CQ references when processing EQ events EQ handlers look up CQs from dev->cq_xa and invoke CQ completion or error callbacks outside the xarray lock. erdma_destroy_cq() can erase the CQ from the xarray and free its queue buffer and doorbell record while a previously scheduled EQ handler is still using the CQ. Add a CQ refcount and take a reference under the xarray lock with refcount_inc_not_zero(). Remove the CQ from the xarray before dropping the destroy-path reference, then wait for in-flight EQ users before releasing CQ resources. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Signed-off-by: Cheng Xu Link: https://patch.msgid.link/20260730124357.12976-1-chengyou@linux.alibaba.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/erdma/erdma_eq.c | 6 ++++-- drivers/infiniband/hw/erdma/erdma_verbs.c | 12 +++++++++-- drivers/infiniband/hw/erdma/erdma_verbs.h | 25 +++++++++++++++++++++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c index a8784e07acd6..96e74fcf417c 100644 --- a/drivers/infiniband/hw/erdma/erdma_eq.c +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -52,7 +52,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) if (FIELD_GET(ERDMA_AEQE_HDR_TYPE_MASK, le32_to_cpu(aeqe->hdr)) == ERDMA_AE_TYPE_CQ_ERR) { cqn = le32_to_cpu(aeqe->event_data0); - cq = find_cq_by_cqn(dev, cqn); + cq = erdma_cq_get_by_cqn(dev, cqn); if (!cq) continue; @@ -62,6 +62,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) if (cq->ibcq.event_handler) cq->ibcq.event_handler(&event, cq->ibcq.cq_context); + erdma_cq_put(cq); } else { qpn = le32_to_cpu(aeqe->event_data0); qp = find_qp_by_qpn(dev, qpn); @@ -157,7 +158,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb) poll_cnt++; cqn = FIELD_GET(ERDMA_CEQE_HDR_CQN_MASK, READ_ONCE(*ceqe)); - cq = find_cq_by_cqn(dev, cqn); + cq = erdma_cq_get_by_cqn(dev, cqn); if (!cq) continue; @@ -166,6 +167,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb) if (cq->ibcq.comp_handler) cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context); + erdma_cq_put(cq); } notify_eq(&ceq_cb->eq); diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index ab6abbab029e..2adcd00c4199 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -1326,6 +1326,7 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) struct erdma_dev *dev = to_edev(ibcq->device); struct erdma_ucontext *ctx = rdma_udata_to_drv_context( udata, struct erdma_ucontext, ibucontext); + unsigned long flags; int err; struct erdma_cmdq_destroy_cq_req req; @@ -1340,6 +1341,13 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) "failed to destroy CQ %u: %d\n", cq->cqn, err); + xa_lock_irqsave(&dev->cq_xa, flags); + __xa_erase(&dev->cq_xa, cq->cqn); + xa_unlock_irqrestore(&dev->cq_xa, flags); + + erdma_cq_put(cq); + wait_for_completion(&cq->free); + if (rdma_is_kernel_res(&cq->ibcq.res)) { dma_free_coherent(&dev->pdev->dev, cq->depth << CQE_SHIFT, cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr); @@ -1350,8 +1358,6 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) put_mtt_entries(dev, &cq->user_cq.qbuf_mem); } - xa_erase(&dev->cq_xa, cq->cqn); - return 0; } @@ -1980,6 +1986,8 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->ibcq.cqe = depth; cq->depth = depth; cq->assoc_eqn = attr->comp_vector + 1; + refcount_set(&cq->refcount, 1); + init_completion(&cq->free); ret = xa_alloc_cyclic(&dev->cq_xa, &cq->cqn, cq, XA_LIMIT(1, dev->attrs.max_cq - 1), diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index 7d8d3fe501d5..894e080435fb 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -7,6 +7,9 @@ #ifndef __ERDMA_VERBS_H__ #define __ERDMA_VERBS_H__ +#include +#include + #include "erdma.h" /* RDMA Capability. */ @@ -341,6 +344,8 @@ struct erdma_cq { u32 depth; u32 assoc_eqn; + refcount_t refcount; + struct completion free; union { struct erdma_kcq_info kern_cq; @@ -355,9 +360,25 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id) return (struct erdma_qp *)xa_load(&dev->qp_xa, id); } -static inline struct erdma_cq *find_cq_by_cqn(struct erdma_dev *dev, int id) +static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev, + int id) { - return (struct erdma_cq *)xa_load(&dev->cq_xa, id); + struct erdma_cq *cq; + unsigned long flags; + + xa_lock_irqsave(&dev->cq_xa, flags); + cq = xa_load(&dev->cq_xa, id); + if (cq && !refcount_inc_not_zero(&cq->refcount)) + cq = NULL; + xa_unlock_irqrestore(&dev->cq_xa, flags); + + return cq; +} + +static inline void erdma_cq_put(struct erdma_cq *cq) +{ + if (refcount_dec_and_test(&cq->refcount)) + complete(&cq->free); } void erdma_qp_get(struct erdma_qp *qp); From a52eeff32024f190b3bdc99088c7becccd4fa60b Mon Sep 17 00:00:00 2001 From: Cheng Xu Date: Thu, 30 Jul 2026 20:43:54 +0800 Subject: [PATCH 142/160] RDMA/erdma: Hold QP references for AE and CM processing AE QP fatal events and iWARP CM paths load QPs from dev->qp_xa and then use or reference them outside the xarray lock. erdma_destroy_qp() can drop the destroy-path reference and free QP resources while such a lookup is in flight. Add erdma_qp_get_by_qpn() to acquire a kref under the xarray lock with kref_get_unless_zero(). Remove the QP from the xarray before dropping the destroy-path reference so no new lookup can acquire it while destruction waits for existing users. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Signed-off-by: Cheng Xu Link: https://patch.msgid.link/20260730124357.12976-2-chengyou@linux.alibaba.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/erdma/erdma_cm.c | 6 ++---- drivers/infiniband/hw/erdma/erdma_eq.c | 3 ++- drivers/infiniband/hw/erdma/erdma_verbs.c | 6 +++++- drivers/infiniband/hw/erdma/erdma_verbs.h | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/erdma/erdma_cm.c b/drivers/infiniband/hw/erdma/erdma_cm.c index 5c7d3a8f8038..70c5df566d9d 100644 --- a/drivers/infiniband/hw/erdma/erdma_cm.c +++ b/drivers/infiniband/hw/erdma/erdma_cm.c @@ -1021,10 +1021,9 @@ int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (laddr->sa_family != AF_INET || raddr->sa_family != AF_INET) return -EAFNOSUPPORT; - qp = find_qp_by_qpn(dev, params->qpn); + qp = erdma_qp_get_by_qpn(dev, params->qpn); if (!qp) return -ENOENT; - erdma_qp_get(qp); ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &s); if (ret < 0) @@ -1154,10 +1153,9 @@ int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) return -ECONNRESET; } - qp = find_qp_by_qpn(dev, params->qpn); + qp = erdma_qp_get_by_qpn(dev, params->qpn); if (!qp) return -ENOENT; - erdma_qp_get(qp); down_write(&qp->state_lock); if (qp->attrs.iwarp.state > ERDMA_QPS_IWARP_RTR) { diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c index 96e74fcf417c..d5d1704cb57c 100644 --- a/drivers/infiniband/hw/erdma/erdma_eq.c +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -65,7 +65,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) erdma_cq_put(cq); } else { qpn = le32_to_cpu(aeqe->event_data0); - qp = find_qp_by_qpn(dev, qpn); + qp = erdma_qp_get_by_qpn(dev, qpn); if (!qp) continue; @@ -75,6 +75,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) if (qp->ibqp.event_handler) qp->ibqp.event_handler(&event, qp->ibqp.qp_context); + erdma_qp_put(qp); } } diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 2adcd00c4199..65b1af1e6623 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -1369,6 +1369,7 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) udata, struct erdma_ucontext, ibucontext); struct erdma_cmdq_destroy_qp_req req; union erdma_mod_qp_params params; + unsigned long flags; int err; down_write(&qp->state_lock); @@ -1396,6 +1397,10 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) "failed to destroy QP %u: %d\n", QP_ID(qp), err); + xa_lock_irqsave(&dev->qp_xa, flags); + __xa_erase(&dev->qp_xa, QP_ID(qp)); + xa_unlock_irqrestore(&dev->qp_xa, flags); + erdma_qp_put(qp); wait_for_completion(&qp->safe_free); @@ -1409,7 +1414,6 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (qp->cep) erdma_cep_put(qp->cep); - xa_erase(&dev->qp_xa, QP_ID(qp)); return 0; } diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index 894e080435fb..c73cecf92f61 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -360,6 +360,21 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id) return (struct erdma_qp *)xa_load(&dev->qp_xa, id); } +static inline struct erdma_qp *erdma_qp_get_by_qpn(struct erdma_dev *dev, + int id) +{ + struct erdma_qp *qp; + unsigned long flags; + + xa_lock_irqsave(&dev->qp_xa, flags); + qp = xa_load(&dev->qp_xa, id); + if (qp && !kref_get_unless_zero(&qp->ref)) + qp = NULL; + xa_unlock_irqrestore(&dev->qp_xa, flags); + + return qp; +} + static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev, int id) { From 961ac0f0c5e414abdd6b33fae84b311d9fde0bd0 Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Wed, 29 Jul 2026 18:32:03 +0900 Subject: [PATCH 143/160] RDMA/srp: fix heap information leak on a truncated SRP_CRED_REQ srp_recv_done() passes wc->byte_len to srp_process_rsp(). It passes nothing to srp_process_cred_req() and srp_process_aer_req(), which read fixed-size fields from the receive buffer without checking that those fields were received. The buffer size is max_ti_iu_len, which comes from the login response and is not validated. A target that advertises 8 and then sends an 8-byte SRP_CRED_REQ makes the initiator read req->tag from beyond the end of the buffer. req->tag is copied into the SRP_CRED_RSP and sent back, so those bytes reach the target. SRP_AER_REQ behaves the same way and also reads req->lun. The leak is 8 bytes per response. max_ti_iu_len also decides which slab cache the buffer comes from. With 8 the buffer is a kmalloc-8 object and the read is entirely outside it: BUG: KASAN: slab-out-of-bounds in srp_recv_done+0x172b/0x1aa0 Read of size 8 at addr ffff888104714da8 by task kworker/u8:3/50 which belongs to the cache kmalloc-8 of size 8 The buggy address is located 0 bytes to the right of allocated 8-byte region [ffff888104714da0, ffff888104714da8) Without KASAN the returned bytes are whatever is next in the slab. One run returned ".strtab". rsp->data[3] in srp_process_rsp() has the same problem: only resp_data_len is checked before it is read. Drop a request that is shorter than the structure being parsed, and check byte_len before the tsk_mgmt read. Fixes: bb12588a38e6 ("IB/srp: Implement SRP_CRED_REQ and SRP_AER_REQ") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260729093203.1503201-1-yhlee@isslab.korea.ac.kr Reviewed-by: Bart Van Assche Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/srp/ib_srp.c | 45 +++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 2fc8e133c70f..6b429ef63f8f 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1945,7 +1945,8 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp, ch->req_lim += be32_to_cpu(rsp->req_lim_delta); if (rsp->tag == ch->tsk_mgmt_tag) { ch->tsk_mgmt_status = -1; - if (be32_to_cpu(rsp->resp_data_len) >= 4) + if (be32_to_cpu(rsp->resp_data_len) >= 4 && + byte_len >= sizeof(*rsp) + 4) ch->tsk_mgmt_status = rsp->data[3]; complete(&ch->tsk_mgmt_done); } else { @@ -2045,13 +2046,20 @@ static int srp_response_common(struct srp_rdma_ch *ch, s32 req_delta, } static void srp_process_cred_req(struct srp_rdma_ch *ch, - struct srp_cred_req *req) + struct srp_cred_req *req, u32 byte_len) { - struct srp_cred_rsp rsp = { - .opcode = SRP_CRED_RSP, - .tag = req->tag, - }; - s32 delta = be32_to_cpu(req->req_lim_delta); + struct srp_cred_rsp rsp = { .opcode = SRP_CRED_RSP }; + s32 delta; + + if (byte_len < sizeof(*req)) { + shost_printk(KERN_ERR, ch->target->scsi_host, PFX + "dropping truncated SRP_CRED_REQ (%u bytes received, %zu expected)\n", + byte_len, sizeof(*req)); + return; + } + + rsp.tag = req->tag; + delta = be32_to_cpu(req->req_lim_delta); if (srp_response_common(ch, delta, &rsp, sizeof(rsp))) shost_printk(KERN_ERR, ch->target->scsi_host, PFX @@ -2059,14 +2067,21 @@ static void srp_process_cred_req(struct srp_rdma_ch *ch, } static void srp_process_aer_req(struct srp_rdma_ch *ch, - struct srp_aer_req *req) + struct srp_aer_req *req, u32 byte_len) { struct srp_target_port *target = ch->target; - struct srp_aer_rsp rsp = { - .opcode = SRP_AER_RSP, - .tag = req->tag, - }; - s32 delta = be32_to_cpu(req->req_lim_delta); + struct srp_aer_rsp rsp = { .opcode = SRP_AER_RSP }; + s32 delta; + + if (byte_len < sizeof(*req)) { + shost_printk(KERN_ERR, target->scsi_host, PFX + "dropping truncated SRP_AER_REQ (%u bytes received, %zu expected)\n", + byte_len, sizeof(*req)); + return; + } + + rsp.tag = req->tag; + delta = be32_to_cpu(req->req_lim_delta); shost_printk(KERN_ERR, target->scsi_host, PFX "ignoring AER for LUN %llu\n", scsilun_to_int(&req->lun)); @@ -2108,11 +2123,11 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc) break; case SRP_CRED_REQ: - srp_process_cred_req(ch, iu->buf); + srp_process_cred_req(ch, iu->buf, wc->byte_len); break; case SRP_AER_REQ: - srp_process_aer_req(ch, iu->buf); + srp_process_aer_req(ch, iu->buf, wc->byte_len); break; case SRP_T_LOGOUT: From 464f5afa92d071a226f88424803b0fcf88093ede Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Fri, 31 Jul 2026 13:12:11 +0900 Subject: [PATCH 144/160] IB/isert: delay the final Login Response until the session is registered isert_put_login_tx() puts the final Login Response on the wire before __transport_register_session(), which iscsi_post_login_handler() reaches only after iscsi_target_do_login() returns. An initiator that issues a SCSI command as soon as it sees that response can have it executed against an se_session whose se_tpg is still NULL, and the ib-comp-wq worker oopses on the NULL dereference. Oops: general protection fault, probably for non-canonical address 0xdffffc000000000f: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000078-0x000000000000007f] CPU: 0 UID: 0 PID: 178 Comm: kworker/0:1H Not tainted 7.2.0-rc5-V2CTL-gf5098b6bae76 #10 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: ib-comp-wq ib_cq_poll_work RIP: 0010:target_submit+0xbe/0x390 Code: fa 48 c1 ea 03 80 3c 02 00 0f 85 89 02 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 64 24 18 49 8d 7c 24 78 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 5a 02 00 00 48 8d 7b 78 4d 8b 6c 24 78 48 b8 00 RSP: 0018:ffff8881058cfa78 EFLAGS: 00010206 RAX: dffffc0000000000 RBX: ffff88810c78c6f0 RCX: ffffffff964bb363 RDX: 000000000000000f RSI: 00000000fffffe00 RDI: 0000000000000078 RBP: 1ffff11020b19f52 R08: 0000000000000001 R09: ffffed1020b19f52 R10: 0000000000000003 R11: ffff88810596c000 R12: 0000000000000000 R13: ffff88810c61b000 R14: ffff88810c6a3400 R15: ffff88810c61b044 FS: 0000000000000000(0000) GS:ffff8881822b2000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f1f1b83c000 CR3: 000000006fe72001 CR4: 0000000000770ef0 PKRU: 55555554 Call Trace: ? __pfx__raw_spin_lock_bh+0x10/0x10 ? __pfx_target_submit+0x10/0x10 ? mutex_lock+0x81/0xe0 ? __pfx_mutex_lock+0x10/0x10 ? iscsit_execute_cmd+0x650/0x850 iscsit_sequence_cmd+0x186/0x3d0 iscsit_process_scsi_cmd+0x87/0x300 isert_recv_done+0x1002/0x2390 ? __pfx_isert_recv_done+0x10/0x10 ? rxe_poll_cq+0x253/0x3d0 ? finish_task_switch.isra.0+0x1dc/0xa70 __ib_process_cq+0xe1/0x390 ib_cq_poll_work+0x46/0x150 process_one_work+0x633/0x1030 ? assign_work+0x11d/0x370 worker_thread+0x45b/0xd10 ? __pfx_worker_thread+0x10/0x10 ? __pfx_worker_thread+0x10/0x10 kthread+0x2c6/0x3b0 ? recalc_sigpending+0x15c/0x1e0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x36e/0x5a0 ? __pfx_ret_from_fork+0x10/0x10 ? __switch_to+0x572/0xdd0 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Modules linked in: ---[ end trace 0000000000000000 ]--- Delay the final Login Response instead. isert_get_rx_pdu() runs from iscsi_target_rx_thread() after conn->rx_login_comp, completed by iscsi_post_login_handler() after __transport_register_session(); iscsi-TCP and cxgbit already take PDUs from that thread, isert alone does not. The buffers are still posted first, so the initiator's first command does not meet an empty receive queue and nothing depends on RNR flow control, and the header and payload live in isert_conn, not in the struct iscsi_login that iscsi_target_nego_release() frees first. Over rxe, 400 login cycles per run, the oops appeared in 10 of 20 unpatched runs and in none of 20 runs with this patch. An initiator that never waits is handled by the next patch. Not tested: iWARP, discovery sessions over iSER, and real HCAs. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260731041212.1733364-1-yhlee@isslab.korea.ac.kr Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/isert/ib_isert.c | 20 ++++++++++++++++++-- drivers/infiniband/ulp/isert/ib_isert.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index e57545cf337a..080ded0e0b3d 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -59,6 +59,8 @@ static void isert_recv_done(struct ib_cq *cq, struct ib_wc *wc); static void isert_send_done(struct ib_cq *cq, struct ib_wc *wc); static void isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc); static void isert_login_send_done(struct ib_cq *cq, struct ib_wc *wc); +static void isert_unmap_tx_desc(struct iser_tx_desc *tx_desc, + struct ib_device *ib_dev); static int isert_sg_tablesize_set(const char *val, const struct kernel_param *kp) { @@ -495,6 +497,8 @@ isert_connect_release(struct isert_conn *isert_conn) if (isert_conn->qp) isert_destroy_qp(isert_conn); + isert_unmap_tx_desc(&isert_conn->login_tx_desc, device->ib_device); + if (isert_conn->login_desc) isert_free_login_buf(isert_conn); @@ -955,14 +959,17 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login, mutex_lock(&isert_conn->mutex); isert_conn->state = ISER_CONN_FULL_FEATURE; mutex_unlock(&isert_conn->mutex); - goto post_send; + + /* Sent from isert_get_rx_pdu() after registration. */ + isert_conn->login_rsp_pending = true; + return 0; } ret = isert_login_post_recv(isert_conn); if (ret) return ret; } -post_send: + ret = isert_login_post_send(isert_conn, tx_desc); if (ret) return ret; @@ -2622,8 +2629,17 @@ static void isert_free_conn(struct iscsit_conn *conn) static void isert_get_rx_pdu(struct iscsit_conn *conn) { + struct isert_conn *isert_conn = conn->context; struct completion comp; + /* The session is registered by now; see isert_put_login_tx(). */ + if (isert_conn->login_rsp_pending) { + isert_conn->login_rsp_pending = false; + if (isert_login_post_send(isert_conn, + &isert_conn->login_tx_desc)) + return; + } + init_completion(&comp); wait_for_completion_interruptible(&comp); diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 0b2dfd6e7e27..0bac5aa66c80 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -178,6 +178,7 @@ struct isert_conn { struct completion login_comp; struct completion login_req_comp; struct iser_tx_desc login_tx_desc; + bool login_rsp_pending; struct rdma_cm_id *cm_id; struct ib_qp *qp; struct ib_cq *cq; From 5247dde9daac7e107853b6fea043f7f47be033f7 Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Fri, 31 Jul 2026 13:12:12 +0900 Subject: [PATCH 145/160] IB/isert: post the full-feature receive buffers after session registration isert_put_login_tx() posts the full-feature receive buffers before __transport_register_session() runs, so an initiator that does not wait for the final Login Response can still have a SCSI command executed against an se_session whose se_tpg is NULL - the same oops as the previous patch, at target_submit+0xbe. Post them from isert_get_rx_pdu(), which the previous patch already uses to send that response, and post them before that send: the receive queue is filled at the moment the initiator is told it may use it. Allocating there keeps the existing property that a memory allocation failure cannot happen once the final Login Response is on the wire. The receive queue is already empty between the final Login Request and isert_post_recvm(); this moves the second point later, from a median of 92 us to 172 us over 1200 logins. Only an initiator that sends before it has been told to can reach that window, and on IB and RoCE its send is retried there until the buffers appear - isert_rdma_accept() asks for rnr_retry_count = 7. iWARP has no RNR flow control, so there the same send terminates the connection instead. Measured over rxe, 400 login cycles per run, with an initiator that does not wait: an instrumented build counted no entries to isert_recv_done() before the buffers are posted in 10 runs, where that initiator oopsed 8 of 10 unpatched runs and 5 of 10 with only the previous patch. Not tested: iWARP, discovery sessions over iSER, and real HCAs. Fixes: b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260731041212.1733364-2-yhlee@isslab.korea.ac.kr Signed-off-by: Leon Romanovsky --- drivers/infiniband/ulp/isert/ib_isert.c | 41 ++++++++++++------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 080ded0e0b3d..5087ea983071 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -946,21 +946,7 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login, } if (!login->login_failed) { if (login->login_complete) { - ret = isert_alloc_rx_descriptors(isert_conn); - if (ret) - return ret; - - ret = isert_post_recvm(isert_conn, - ISERT_QP_MAX_RECV_DTOS); - if (ret) - return ret; - - /* Now we are in FULL_FEATURE phase */ - mutex_lock(&isert_conn->mutex); - isert_conn->state = ISER_CONN_FULL_FEATURE; - mutex_unlock(&isert_conn->mutex); - - /* Sent from isert_get_rx_pdu() after registration. */ + /* Posted and sent from isert_get_rx_pdu(). */ isert_conn->login_rsp_pending = true; return 0; } @@ -2632,13 +2618,26 @@ static void isert_get_rx_pdu(struct iscsit_conn *conn) struct isert_conn *isert_conn = conn->context; struct completion comp; + /* The login timeout timer can fail the login after isert_put_login_tx(). */ + if (!isert_conn->login_rsp_pending) + return; + + isert_conn->login_rsp_pending = false; + /* The session is registered by now; see isert_put_login_tx(). */ - if (isert_conn->login_rsp_pending) { - isert_conn->login_rsp_pending = false; - if (isert_login_post_send(isert_conn, - &isert_conn->login_tx_desc)) - return; - } + if (isert_alloc_rx_descriptors(isert_conn)) + return; + + if (isert_post_recvm(isert_conn, ISERT_QP_MAX_RECV_DTOS)) + return; + + /* Now we are in FULL_FEATURE phase */ + mutex_lock(&isert_conn->mutex); + isert_conn->state = ISER_CONN_FULL_FEATURE; + mutex_unlock(&isert_conn->mutex); + + if (isert_login_post_send(isert_conn, &isert_conn->login_tx_desc)) + return; init_completion(&comp); From a9394971825933074032794a5feee5211509c774 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Sat, 1 Aug 2026 17:36:32 -0400 Subject: [PATCH 146/160] RDMA/siw: Fix use-after-free in siw_accept() siw_accept() looks up the QP supplied by userspace. If that QP is already in RTS, the function jumps to error cleanup before associating the incoming CEP with it. The cleanup tests whether qp->cep is non-NULL and assumes the current call installed the association. However, qp->cep can point to the CEP of an existing connection. The cleanup then drops a reference from the incoming cep, not qp->cep. Once the incoming endpoint loses its remaining references, this can free it before the subsequent cep->qp store, causing a use-after-free. It also clears the existing QP association. Only release the association reference when qp->cep is the incoming CEP. This preserves an existing association and avoids accessing the freed endpoint. Fixes: 6c52fdc244b5 ("rdma/siw: connection management") Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260801213632.1086548-1-shuangpeng.kernel@gmail.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/siw/siw_cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index 87c79527ac09..0245b25e7271 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -1751,7 +1751,7 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) cep->state = SIW_EPSTATE_CLOSED; siw_free_cm_id(cep); - if (qp->cep) { + if (qp->cep == cep) { siw_cep_put(cep); qp->cep = NULL; } From 1d0f877d593438a494ca5b05cc8699150409005a Mon Sep 17 00:00:00 2001 From: Brett Creeley Date: Wed, 5 Aug 2026 11:02:53 +0530 Subject: [PATCH 147/160] RDMA/ionic: Cap eq_count to the eth driver's interrupt vector budget ionic_fill_lif_cfg() reads eq_count from firmware uncapped, but the eth driver only reserves ionic->neqs_per_lif MSI-X vectors for RDMA event queues. Since ionic_rdma probes via the auxiliary bus before the netdev is brought up, it can exhaust the shared interrupt bitmap, causing ionic_open() to fail with -ENOSPC when allocating rx/tx interrupts. Cap RDMA eq_count to neqs_per_lif, which is populated by ionic_lif_size() at PCI probe before the RDMA aux device registers. Fixes: 8d765af51a09 ("RDMA/ionic: Register auxiliary module for ionic ethernet adapter") Cc: stable@vger.kernel.org Signed-off-by: Brett Creeley Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260805053254.4023262-1-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/ionic/ionic_lif_cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c index f827dce59973..8e48c00d4959 100644 --- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c +++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c @@ -72,7 +72,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg) * eq_count is tunable; see ionic_eq_count */ cfg->aq_count = le32_to_cpu(ident->rdma.aq_qtype.qid_count); - cfg->eq_count = le32_to_cpu(ident->rdma.eq_qtype.qid_count); + cfg->eq_count = lif->ionic->neqs_per_lif; cfg->cq_count = le32_to_cpu(ident->rdma.cq_qtype.qid_count); cfg->qp_count = le32_to_cpu(ident->rdma.sq_qtype.qid_count); cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif); From cf3ebd89e754015625fee90aa938f6bc79a2c974 Mon Sep 17 00:00:00 2001 From: Abhijit Gangurde Date: Wed, 5 Aug 2026 11:02:54 +0530 Subject: [PATCH 148/160] RDMA/ionic: Embed counter driver data in rdma_counter allocation Commit 7e53b31acc7f ("RDMA/core: Create and destroy rdma_counter using rdma_zalloc_drv_obj()") requires drivers implementing counter ops to embed struct rdma_counter in a driver-specific struct, register its size via INIT_RDMA_OBJ_SIZE, and provide a counter_init callback. The ionic driver was merged without this adaptation, causing a NULL pointer dereference in alloc_and_bind() since rdma_zalloc_drv_obj() allocates zero bytes when size_rdma_counter is unset. Consolidate struct ionic_counter into a new struct ionic_rdma_counter that embeds struct rdma_counter, replace the xarray with a lightweight ida for ID allocation, and add the required counter_init and INIT_RDMA_OBJ_SIZE declarations. Fixes: ea4c399642b8 ("RDMA/ionic: Implement device stats ops") Cc: stable@vger.kernel.org # 6.18 Signed-off-by: Abhijit Gangurde Link: https://patch.msgid.link/20260805053254.4023262-2-abhijit.gangurde@amd.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/ionic/ionic_hw_stats.c | 95 +++++++++----------- drivers/infiniband/hw/ionic/ionic_ibdev.h | 11 ++- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/drivers/infiniband/hw/ionic/ionic_hw_stats.c b/drivers/infiniband/hw/ionic/ionic_hw_stats.c index f72c9837e135..4f0a2dedbdfe 100644 --- a/drivers/infiniband/hw/ionic/ionic_hw_stats.c +++ b/drivers/infiniband/hw/ionic/ionic_hw_stats.c @@ -235,35 +235,34 @@ static int ionic_get_hw_stats(struct ib_device *ibdev, static struct rdma_hw_stats * ionic_counter_alloc_stats(struct rdma_counter *counter) { + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); - struct ionic_counter *cntr; - int err; + struct rdma_hw_stats *stats; + int id; - cntr = kzalloc_obj(*cntr); - if (!cntr) - return NULL; - - /* buffer for current values from the device */ cntr->vals = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!cntr->vals) - goto err_vals; + return NULL; - err = xa_alloc(&dev->counter_stats->xa_counters, &counter->id, - cntr, - XA_LIMIT(0, IONIC_MAX_QPID), - GFP_KERNEL); - if (err) - goto err_xa; + id = ida_alloc_max(&dev->counter_stats->counter_ida, + IONIC_MAX_QPID, GFP_KERNEL); + if (id < 0) + goto err_ida; - INIT_LIST_HEAD(&cntr->qp_list); + counter->id = id; - return rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs, - dev->counter_stats->queue_stats_count, - RDMA_HW_STATS_DEFAULT_LIFESPAN); -err_xa: + stats = rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs, + dev->counter_stats->queue_stats_count, + RDMA_HW_STATS_DEFAULT_LIFESPAN); + if (!stats) + goto err_hw_stats; + + return stats; + +err_hw_stats: + ida_free(&dev->counter_stats->counter_ida, id); +err_ida: kfree(cntr->vals); -err_vals: - kfree(cntr); return NULL; } @@ -271,14 +270,10 @@ ionic_counter_alloc_stats(struct rdma_counter *counter) static int ionic_counter_dealloc(struct rdma_counter *counter) { struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); - struct ionic_counter *cntr; - - cntr = xa_erase(&dev->counter_stats->xa_counters, counter->id); - if (!cntr) - return -EINVAL; + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + ida_free(&dev->counter_stats->counter_ida, counter->id); kfree(cntr->vals); - kfree(cntr); return 0; } @@ -287,13 +282,8 @@ static int ionic_counter_bind_qp(struct rdma_counter *counter, struct ib_qp *ibqp, u32 port) { - struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); struct ionic_qp *qp = to_ionic_qp(ibqp); - struct ionic_counter *cntr; - - cntr = xa_load(&dev->counter_stats->xa_counters, counter->id); - if (!cntr) - return -EINVAL; list_add_tail(&qp->qp_list_counter, &cntr->qp_list); ibqp->counter = counter; @@ -313,29 +303,23 @@ static int ionic_counter_unbind_qp(struct ib_qp *ibqp, u32 port) return 0; } -static int ionic_get_qp_stats(struct ib_device *ibdev, - struct rdma_hw_stats *hw_stats, - u32 counter_id) +static int ionic_counter_update_stats(struct rdma_counter *counter) { - struct ionic_ibdev *dev = to_ionic_ibdev(ibdev); - struct ionic_counter_stats *cs; - struct ionic_counter *cntr; + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); + struct ionic_counter_stats *cs = dev->counter_stats; dma_addr_t hw_stats_dma; struct ionic_qp *qp; int rc, stat_i = 0; - cs = dev->counter_stats; - cntr = xa_load(&cs->xa_counters, counter_id); - if (!cntr) - return -EINVAL; - hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, cntr->vals, PAGE_SIZE, DMA_FROM_DEVICE); rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma); if (rc) return rc; - memset(hw_stats->value, 0, sizeof(u64) * hw_stats->num_counters); + memset(counter->stats->value, 0, + sizeof(u64) * counter->stats->num_counters); list_for_each_entry(qp, &cntr->qp_list, qp_list_counter) { rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE, @@ -345,7 +329,7 @@ static int ionic_get_qp_stats(struct ib_device *ibdev, goto err_cmd; for (stat_i = 0; stat_i < cs->queue_stats_count; ++stat_i) - hw_stats->value[stat_i] += + counter->stats->value[stat_i] += ionic_v1_stat_val(&cs->hdr[stat_i], cntr->vals, PAGE_SIZE); @@ -360,11 +344,6 @@ static int ionic_get_qp_stats(struct ib_device *ibdev, return rc; } -static int ionic_counter_update_stats(struct rdma_counter *counter) -{ - return ionic_get_qp_stats(counter->device, counter->stats, counter->id); -} - static int ionic_alloc_counters(struct ionic_ibdev *dev) { struct ionic_counter_stats *cs = dev->counter_stats; @@ -424,12 +403,22 @@ static const struct ib_device_ops ionic_hw_stats_ops = { .get_hw_stats = ionic_get_hw_stats, }; +static void ionic_counter_init(struct rdma_counter *counter) +{ + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + + INIT_LIST_HEAD(&cntr->qp_list); +} + static const struct ib_device_ops ionic_counter_stats_ops = { .counter_alloc_stats = ionic_counter_alloc_stats, .counter_dealloc = ionic_counter_dealloc, .counter_bind_qp = ionic_counter_bind_qp, .counter_unbind_qp = ionic_counter_unbind_qp, .counter_update_stats = ionic_counter_update_stats, + .counter_init = ionic_counter_init, + + INIT_RDMA_OBJ_SIZE(rdma_counter, ionic_rdma_counter, rdma_counter), }; void ionic_stats_init(struct ionic_ibdev *dev) @@ -458,7 +447,7 @@ void ionic_stats_init(struct ionic_ibdev *dev) return; } - xa_init_flags(&dev->counter_stats->xa_counters, XA_FLAGS_ALLOC); + ida_init(&dev->counter_stats->counter_ida); ib_set_device_ops(&dev->ibdev, &ionic_counter_stats_ops); } @@ -467,7 +456,7 @@ void ionic_stats_init(struct ionic_ibdev *dev) void ionic_stats_cleanup(struct ionic_ibdev *dev) { if (dev->counter_stats) { - xa_destroy(&dev->counter_stats->xa_counters); + ida_destroy(&dev->counter_stats->counter_ida); kfree(dev->counter_stats->hdr); kfree(dev->counter_stats->stats_hdrs); kfree(dev->counter_stats); diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index d7adaa161e6f..32b6a8a45fa2 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -332,14 +332,21 @@ struct ionic_counter_stats { int queue_stats_count; struct ionic_v1_stat *hdr; struct rdma_stat_desc *stats_hdrs; - struct xarray xa_counters; + struct ida counter_ida; }; -struct ionic_counter { +struct ionic_rdma_counter { + struct rdma_counter rdma_counter; void *vals; struct list_head qp_list; }; +static inline struct ionic_rdma_counter * +to_ionic_rdma_counter(struct rdma_counter *counter) +{ + return container_of(counter, struct ionic_rdma_counter, rdma_counter); +} + static inline struct ionic_ibdev *to_ionic_ibdev(struct ib_device *ibdev) { return container_of(ibdev, struct ionic_ibdev, ibdev); From a12d9145145b21c50531afb6e3f711b1f34e1465 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 6 Aug 2026 11:48:56 +0300 Subject: [PATCH 149/160] RDMA/erdma: restrict the driver to little-endian systems The eRDMA device interface requires explicit byte ordering, but several DMA-visible values that should be little-endian remain native-endian. Command request payloads are copied verbatim, data-path SQE headers are written without cpu_to_le64(), and kernel doorbell records are assigned plain u64 values. The command completion path also reads a little-endian SQE header without conversion. These paths are byte-swapped on big-endian kernels and can break command processing during probe. Since complete big-endian support requires converting every device-visible structure, depend on !CPU_BIG_ENDIAN. Fixes: ca7fd6cff3b8 ("RDMA/erdma: Add driver to kernel build environment") Link: https://patch.msgid.link/20260806-missing-endianness-conversion-for-64-v1-1-896327c1aff1@nvidia.com Acked-by: Cheng Xu Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/erdma/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/erdma/Kconfig b/drivers/infiniband/hw/erdma/Kconfig index 267fc1f3c42a..745e5551773f 100644 --- a/drivers/infiniband/hw/erdma/Kconfig +++ b/drivers/infiniband/hw/erdma/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config INFINIBAND_ERDMA tristate "Alibaba Elastic RDMA Adapter (ERDMA) support" - depends on PCI_MSI && 64BIT + depends on PCI_MSI && 64BIT && !CPU_BIG_ENDIAN depends on INFINIBAND_ADDR_TRANS depends on INFINIBAND_USER_ACCESS help From f4cc21c6a8e9d392871477f9fd98d68e5ad80272 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 27 Jul 2026 10:06:12 +0200 Subject: [PATCH 150/160] RDMA/ucma: Lock the handler in ucma_write_cm_event() ctx->file may only be changed under the handler lock and the xa_lock, which is what stops uevents being queued for a ctx while ucma_migrate_id() moves it to another file. The CM core takes that lock before invoking ucma_event_handler(), but the write() paths that queue uevents themselves do not. ucma_write_cm_event() re-reads ctx->file for each of its four dereferences, so ucma_migrate_id() can swap it mid-sequence: mutex_lock(&ctx->file->mut); /* file A */ list_add_tail(&uevent->list, &ctx->file->event_list); /* file B */ mutex_unlock(&ctx->file->mut); /* file B */ wake_up_interruptible(&ctx->file->poll_wait); /* file B */ The window is the mutex_lock() itself: the writer sleeps in it while the migration reassigns ctx->file. The list_add_tail() then runs on file B's event_list holding only file A's mutex: list_add corruption. prev->next should be next (ffff888101320f30), but was ffff88814a08c418. (prev=ffff88814a075c18). kernel BUG at lib/list_debug.c:32! Call Trace: ucma_write_cm_event+0x36e/0x5e0 and file A's mut is left held forever, wedging its next writer in D state. The uevent is also stranded on a list ucma_cleanup_ctx_events() will not walk, so it outlives its context. /dev/infiniband/rdma_cm is 0666 and no RDMA device is involved, so an unprivileged user reaches all of this. Take the handler lock, as ucma_cleanup_mc_events() does; ctx->cm_id is pinned by the ucma_get_ctx() reference. Fixes: a3c9d0fcd371 ("RDMA/ucma: Support write an event into a CM") Link: https://patch.msgid.link/r/60544A67-EFD6-4D5D-974C-D983445F1070@doyensec.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ucma.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 878561fa1cb5..a29f2d4b8d14 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1776,6 +1776,13 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file, goto out; } + rdma_lock_handler(ctx->cm_id); + if (!ctx->uid) { + kfree(uevent); + ret = -EINVAL; + goto err_unlock; + } + uevent->ctx = ctx; uevent->resp.uid = ctx->uid; uevent->resp.id = ctx->id; @@ -1789,6 +1796,8 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file, mutex_unlock(&ctx->file->mut); wake_up_interruptible(&ctx->file->poll_wait); +err_unlock: + rdma_unlock_handler(ctx->cm_id); out: ucma_put_ctx(ctx); return ret; From ecbe7d36dc2de07e5dfbb4a8ff5b315ab43de820 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 27 Jul 2026 10:08:36 +0200 Subject: [PATCH 151/160] RDMA/ucma: Lock the handler in ucma_set_ib_path() ucma_set_ib_path() calls ucma_event_handler() straight from the write() path, without the handler lock that keeps ctx->file stable while a uevent is queued. The handler re-reads ctx->file for every dereference: mutex_lock(&ctx->file->mut); /* file A */ list_add_tail(&uevent->list, &ctx->file->event_list); /* file B */ mutex_unlock(&ctx->file->mut); /* file B */ wake_up_interruptible(&ctx->file->poll_wait); /* file B */ A concurrent ucma_migrate_id() reassigns ctx->file while the SET_OPTION caller sleeps in mutex_lock(), so the list_add_tail() lands on file B's event_list while only file A's mutex is held, racing every other user of that list: BUG: KASAN: slab-use-after-free in __list_add_valid_or_report+0x1aa/0x1c0 Read of size 8 at addr ffff888153c6a418 by task poc_corr/486 Call Trace: __list_add_valid_or_report+0x1aa/0x1c0 ucma_event_handler+0x1be/0xc00 ucma_set_ib_path+0x45e/0x710 ucma_set_option+0x32e/0x590 ucma_write+0x1f9/0x330 Allocated by task 505: ucma_write_cm_event+0x1a1/0x660 Freed by task 505: kfree+0x1da/0x4c0 ucma_get_event+0x5d5/0x7e0 The freed object is a ucma_event that another thread dequeued from file B's list under file B's mutex. File A's mut is left held on top of that, wedging its next writer in uninterruptible sleep. This path needs a bound and address-resolved cm_id, so it requires an RDMA device to be present. Take the handler lock around the call. Fixes: 09e328e47a69 ("RDMA/ucma: Fix the locking of ctx->file") Link: https://patch.msgid.link/r/2823D190-92D5-4714-8769-4FB643C64FF3@doyensec.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ucma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index a29f2d4b8d14..ac29dfa69bb3 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1404,7 +1404,10 @@ static int ucma_set_ib_path(struct ucma_context *ctx, memset(&event, 0, sizeof event); event.event = RDMA_CM_EVENT_ROUTE_RESOLVED; - return ucma_event_handler(ctx->cm_id, &event); + rdma_lock_handler(ctx->cm_id); + ret = ucma_event_handler(ctx->cm_id, &event); + rdma_unlock_handler(ctx->cm_id); + return ret; } static int ucma_set_option_ib(struct ucma_context *ctx, int optname, From a7100601aa1a39f799a566acce10db20eaf4b7f2 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 6 Aug 2026 13:01:27 +0000 Subject: [PATCH 152/160] RDMA/cxgb4: Cancel reg_work before freeing device on remove c4iw_uld_state_change() queues reg_work to register the RDMA device. c4iw_remove() can free ctx->dev while this work is pending or running, leaving c4iw_register_device() accessing the freed device. Cancel reg_work before removing the device. The registration work can tear down ctx->dev when registration fails, so do not unregister or deallocate it again in that case. This issue was found by an in-house static analysis tool. Fixes: 1c8f1da5d851 ("iw_cxgb4: Fix possible circular dependency locking warning") Link: https://patch.msgid.link/r/20260806130128.465460-1-fanwu01@zju.edu.cn Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/device.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 102c5646b9ed..c1815972aecf 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -951,6 +951,12 @@ void c4iw_dealloc(struct uld_ctx *ctx) static void c4iw_remove(struct uld_ctx *ctx) { pr_debug("c4iw_dev %p\n", ctx->dev); + + /* c4iw_register_device() may still be using ctx->dev. */ + cancel_work_sync(&ctx->reg_work); + if (!ctx->dev) + return; + debugfs_remove_recursive(ctx->dev->debugfs_root); c4iw_unregister_device(ctx->dev); c4iw_dealloc(ctx); From fe5c16bb6252dea6025b748257ddc3b2665495b0 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 6 Aug 2026 13:01:28 +0000 Subject: [PATCH 153/160] RDMA/cxgb4: Free debugfs on registration failure c4iw_alloc() creates the per-device debugfs tree (dev->debugfs_root via setup_debugfs()), but it is removed only in c4iw_remove(), not in c4iw_dealloc(). When RDMA device registration fails, the registration worker's err_dealloc_ctx path calls c4iw_dealloc() directly, bypassing c4iw_remove(), so the debugfs dentries leak and outlive the freed c4iw_dev. Move debugfs_remove_recursive() into c4iw_dealloc() so every path that frees ctx->dev also removes its debugfs tree. Fixes: 49ea0c036ede ("RDMA/iw_cxgb4: cleanup device debugfs entries on ULD remove") Link: https://patch.msgid.link/r/20260806130128.465460-2-fanwu01@zju.edu.cn Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index c1815972aecf..24ac9871ce67 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -933,6 +933,7 @@ static void c4iw_rdev_close(struct c4iw_rdev *rdev) void c4iw_dealloc(struct uld_ctx *ctx) { + debugfs_remove_recursive(ctx->dev->debugfs_root); c4iw_rdev_close(&ctx->dev->rdev); WARN_ON(!xa_empty(&ctx->dev->cqs)); WARN_ON(!xa_empty(&ctx->dev->qps)); @@ -957,7 +958,6 @@ static void c4iw_remove(struct uld_ctx *ctx) if (!ctx->dev) return; - debugfs_remove_recursive(ctx->dev->debugfs_root); c4iw_unregister_device(ctx->dev); c4iw_dealloc(ctx); } From c6d1ec4fbe56492bb88987d577f04a5fb6955f26 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Tue, 11 Aug 2026 00:01:23 +0200 Subject: [PATCH 154/160] RDMA/cma: Fix WARNING in res_to_rt syzbot reported a WARN_ON(!res->dev) in res_to_rt() triggered via addr_handler() during asynchronous address resolution: " WARNING: drivers/infiniband/core/restrack.c:138 at res_to_rt+0x1c4/0x230 CPU#1: kworker/u8:4/59 Modules linked in: CPU: 1 UID: 0 PID: 59 Comm: kworker/u8:4 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Compute Engine, BIOS Google 07/24/2026 Workqueue: ib_addr process_one_req RIP: 0010:res_to_rt+0x1c4/0x230 drivers/infiniband/core/restrack.c:138 RSP: 0018:ffffc9000201f850 EFLAGS: 00010293 RAX: ffffffff88d00ce5 RBX: ffff88807f0fd4f8 RCX: ffff88801e6e0000 RDX: 0000000000000000 RSI: ffffffff8fd996f0 RDI: 0000000000000003 RBP: 0000000000000000 R08: ffff88801e6e0000 R09: 000000000000000a R10: 0000000000000009 R11: 0000000000000000 R12: dffffc0000000000 R13: 1ffff1100fe1fa9f R14: 0000000000000000 R15: 0000000000000003 FS: 0000000000000000(0000) GS:ffff888125012000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00001d559c3d2000 CR3: 0000000077c4c000 CR4: 00000000003526f0 Call Trace: rdma_restrack_add+0x5a/0x8a0 drivers/infiniband/core/restrack.c:236 addr_handler+0x41a/0x5a0 drivers/infiniband/core/cma.c:3534 process_one_req+0x2eb/0x540 drivers/infiniband/core/addr.c:624 process_one_work kernel/workqueue.c:3375 [inline] process_scheduled_works+0xc4e/0x1630 kernel/workqueue.c:3458 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3539 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 " In addr_handler(), cma_acquire_dev_by_src_ip() is called to populate id_priv->cma_dev and bind the associated ib_device to id_priv->id.device. If cma_acquire_dev_by_src_ip() returns an error (non-zero status), the ID remains unassociated with any RDMA device. Previously, rdma_restrack_add(&id_priv->res) was invoked unconditionally even when cma_acquire_dev_by_src_ip() failed, passing a resource with a NULL dev pointer and triggering the WARN_ON assertion in res_to_rt(). Fix this by only adding the resource to restrack when acquiring the device succeeds. Reported-by: syzbot+72eddfbadda3e3928e72@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=72eddfbadda3e3928e72 Tested-by: syzbot+72eddfbadda3e3928e72@syzkaller.appspotmail.com Fixes: cb5cd0ea4eb3 ("RDMA/core: Add CM to restrack after successful attachment to a device") Link: https://patch.msgid.link/r/20260810220123.191696-1-yanjun.zhu@linux.dev Signed-off-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 3fbbf8d2f960..8c8313bd3625 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -3528,10 +3528,12 @@ static void addr_handler(int status, struct sockaddr *src_addr, memcpy(addr, src_addr, rdma_addr_size(src_addr)); if (!status && !id_priv->cma_dev) { status = cma_acquire_dev_by_src_ip(id_priv); - if (status) + if (status) { pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to acquire device. status %d\n", status); - rdma_restrack_add(&id_priv->res); + } else { + rdma_restrack_add(&id_priv->res); + } } else if (status) { pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to resolve IP. status %d\n", status); } From de329533792a373186d79dca1ca120f8fa0afd05 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Thu, 30 Jul 2026 10:28:27 +0800 Subject: [PATCH 155/160] RDMA/rxe: Fix OOB in free_rd_atomic_resources() free_rd_atomic_resources() iterates using qp->attr.max_dest_rd_atomic. Updating max_dest_rd_atomic before freeing the old array can make the free path walk past the old allocation and trigger a slab out-of-bounds write catched by KASAN: ================================================================== BUG: KASAN: slab-out-of-bounds in free_rd_atomic_resource drivers/infiniband/sw/rxe/rxe_qp.c:180 [inline] BUG: KASAN: slab-out-of-bounds in free_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:171 [inline] BUG: KASAN: slab-out-of-bounds in free_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:163 [inline] BUG: KASAN: slab-out-of-bounds in rxe_qp_from_attr+0x1e88/0x2150 drivers/infiniband/sw/rxe/rxe_qp.c:712 Write of size 4 at addr ffff88802b8dddb8 by task syz.3.451/11063 CPU: 0 UID: 0 PID: 11063 Comm: syz.3.451 Not tainted 7.1.0 #2 PREEMPT(full) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x10e/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xf7/0x600 mm/kasan/report.c:482 kasan_report+0xe4/0x120 mm/kasan/report.c:595 free_rd_atomic_resource drivers/infiniband/sw/rxe/rxe_qp.c:180 [inline] free_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:171 [inline] free_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:163 [inline] rxe_qp_from_attr+0x1e88/0x2150 drivers/infiniband/sw/rxe/rxe_qp.c:712 rxe_modify_qp+0x1e2/0x530 drivers/infiniband/sw/rxe/rxe_verbs.c:623 ib_security_modify_qp+0x223/0xfa0 drivers/infiniband/core/security.c:625 _ib_modify_qp+0x333/0xec0 drivers/infiniband/core/verbs.c:1915 modify_qp+0x13ca/0x1940 drivers/infiniband/core/uverbs_cmd.c:1932 ib_uverbs_modify_qp+0xcb/0x120 drivers/infiniband/core/uverbs_cmd.c:1958 ib_uverbs_write+0xb86/0x1030 drivers/infiniband/core/uverbs_main.c:680 vfs_write+0x2aa/0x1070 fs/read_write.c:686 ksys_write+0x1f8/0x250 fs/read_write.c:740 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fefc75a70cd Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fefc8495018 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007fefc7835fa0 RCX: 00007fefc75a70cd RDX: 0000000000000078 RSI: 0000200000000240 RDI: 0000000000000007 RBP: 00007fefc764f10f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007fefc7836038 R14: 00007fefc7835fa0 R15: 00007ffcf0586aa0 Allocated by task 11063: kasan_save_stack+0x33/0x60 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __do_kmalloc_node mm/slub.c:5296 [inline] __kmalloc_noprof+0x32a/0x850 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] alloc_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:155 [inline] rxe_qp_from_attr+0x3f8/0x2150 drivers/infiniband/sw/rxe/rxe_qp.c:714 rxe_modify_qp+0x1e2/0x530 drivers/infiniband/sw/rxe/rxe_verbs.c:623 ib_security_modify_qp+0x223/0xfa0 drivers/infiniband/core/security.c:625 _ib_modify_qp+0x333/0xec0 drivers/infiniband/core/verbs.c:1915 modify_qp+0x13ca/0x1940 drivers/infiniband/core/uverbs_cmd.c:1932 ib_uverbs_modify_qp+0xcb/0x120 drivers/infiniband/core/uverbs_cmd.c:1958 ib_uverbs_write+0xb86/0x1030 drivers/infiniband/core/uverbs_main.c:680 vfs_write+0x2aa/0x1070 fs/read_write.c:686 ksys_write+0x1f8/0x250 fs/read_write.c:740 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff88802b8ddd80 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 0 bytes to the right of allocated 56-byte region [ffff88802b8ddd80, ffff88802b8dddb8) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x2b8dd flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff) page_type: f5(slab) raw: 00fff00000000000 ffff888015c418c0 dead000000000100 dead000000000122 raw: 0000000000000000 0000000800200020 00000000f5000000 0000000000000000 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2c40(GFP_NOFS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 4651, tgid 4651 ((udev-worker)), ts 123427165316, free_ts 123425874255 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0xfc/0x120 mm/page_alloc.c:1853 prep_new_page mm/page_alloc.c:1861 [inline] get_page_from_freelist+0x75b/0x3220 mm/page_alloc.c:3941 __alloc_frozen_pages_noprof+0x27e/0x2b00 mm/page_alloc.c:5221 alloc_slab_page mm/slub.c:3278 [inline] allocate_slab mm/slub.c:3467 [inline] new_slab+0xa6/0x670 mm/slub.c:3525 refill_objects+0x278/0x420 mm/slub.c:7272 refill_sheaf mm/slub.c:2816 [inline] __pcs_replace_empty_main+0x2ed/0x640 mm/slub.c:4652 alloc_from_pcs mm/slub.c:4750 [inline] slab_alloc_node mm/slub.c:4884 [inline] __do_kmalloc_node mm/slub.c:5295 [inline] __kmalloc_noprof+0x68d/0x850 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] tomoyo_encode2+0x100/0x3e0 security/tomoyo/realpath.c:45 tomoyo_encode+0x29/0x50 security/tomoyo/realpath.c:80 tomoyo_realpath_from_path+0x18c/0x690 security/tomoyo/realpath.c:283 tomoyo_get_realpath security/tomoyo/file.c:151 [inline] tomoyo_check_open_permission+0x2ab/0x3c0 security/tomoyo/file.c:776 tomoyo_file_open+0x6b/0x90 security/tomoyo/tomoyo.c:334 security_file_open+0x7a/0x1b0 security/security.c:2739 do_dentry_open+0x57e/0x1690 fs/open.c:924 vfs_open+0x82/0x3f0 fs/open.c:1079 do_open fs/namei.c:4699 [inline] path_openat+0x218a/0x3190 fs/namei.c:4858 page last free pid 1 tgid 1 stack trace: reset_page_owner include/linux/page_owner.h:25 [inline] __free_pages_prepare mm/page_alloc.c:1397 [inline] __free_frozen_pages+0x763/0xfc0 mm/page_alloc.c:2938 selinux_genfs_get_sid security/selinux/hooks.c:1364 [inline] inode_doinit_with_dentry+0x903/0x1320 security/selinux/hooks.c:1563 selinux_d_instantiate+0x26/0x30 security/selinux/hooks.c:6658 security_d_instantiate+0x123/0x190 security/security.c:3704 d_splice_alias_ops+0x92/0x850 fs/dcache.c:3141 kernfs_iop_lookup+0x23f/0x2d0 fs/kernfs/dir.c:1289 lookup_open.isra.0+0x659/0x1080 fs/namei.c:4484 open_last_lookups fs/namei.c:4611 [inline] path_openat+0x17dd/0x3190 fs/namei.c:4855 do_file_open+0x20c/0x430 fs/namei.c:4887 do_sys_openat2+0x101/0x1d0 fs/open.c:1364 do_sys_open fs/open.c:1370 [inline] __do_sys_openat fs/open.c:1386 [inline] __se_sys_openat fs/open.c:1381 [inline] __x64_sys_openat+0x141/0x200 fs/open.c:1381 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Memory state around the buggy address: ffff88802b8ddc80: 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc ffff88802b8ddd00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc >ffff88802b8ddd80: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc ^ ffff88802b8dde00: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc ffff88802b8dde80: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc Fix the OOB by moving the assignment after free_rd_atomic_resources() so the old array is freed using the old bound. This matches the original ordering in commit 8700e3e7c485 ("Soft RoCE driver"). Closes: https://lore.kernel.org/all/365C68B4923F8214+30195a67-0b90-4b92-ab96-2ce41517793c@smail.nju.edu.cn/ Fixes: b6bbee0d2438 ("IB/rxe: Properly honor max IRD value for rd/atomic.") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Zhu Yanjun Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_qp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index ec2cfb12559a..311f285d78a6 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -708,8 +708,6 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, int max_dest_rd_atomic = attr->max_dest_rd_atomic ? roundup_pow_of_two(attr->max_dest_rd_atomic) : 0; - qp->attr.max_dest_rd_atomic = max_dest_rd_atomic; - /* * Not gated by IB_QP_STATE, so the responder task is live. * Quiesce recv_task like rxe_qp_reset() before swapping the @@ -718,6 +716,7 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, */ rxe_disable_task(&qp->recv_task); free_rd_atomic_resources(qp); + qp->attr.max_dest_rd_atomic = max_dest_rd_atomic; err = alloc_rd_atomic_resources(qp, max_dest_rd_atomic); /* * On ENOMEM leave recv_task quiesced: qp->resp.resources is From 67f47faddbda247bc08ff17278c0e3978f56816a Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Wed, 12 Aug 2026 12:17:16 +0000 Subject: [PATCH 156/160] RDMA/efa: Decouple admin command payload from admin header Remove aq_common_desc from all EFA admin command descriptors so that command structs represent pure payloads. This allows attaching different headers to the same command by copying the payload into the generic header payload field. The admin header is now constructed in a single place which improves separability. Link: https://patch.msgid.link/r/20260812121718.2904349-2-ynachum@amazon.com Reviewed-by: Daniel Kranzdorf Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe --- .../infiniband/hw/efa/efa_admin_cmds_defs.h | 116 +++--------- drivers/infiniband/hw/efa/efa_admin_defs.h | 8 +- drivers/infiniband/hw/efa/efa_com.c | 53 +++--- drivers/infiniband/hw/efa/efa_com.h | 7 +- drivers/infiniband/hw/efa/efa_com_cmd.c | 170 ++++++------------ 5 files changed, 112 insertions(+), 242 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 95d1493153cd..ab830764e3b4 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -102,9 +102,6 @@ struct efa_admin_qp_alloc_size { }; struct efa_admin_create_qp_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* Protection Domain associated with this QP */ u16 pd; @@ -167,7 +164,7 @@ struct efa_admin_create_qp_cmd { /* MBZ */ u32 reserved2; -}; +} __packed; struct efa_admin_create_qp_resp { /* Common Admin Queue completion descriptor */ @@ -208,9 +205,6 @@ struct efa_admin_create_qp_resp { }; struct efa_admin_modify_qp_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* * Mask indicating which fields should be updated * 0 : qp_state @@ -246,7 +240,7 @@ struct efa_admin_modify_qp_cmd { /* MBZ */ u16 reserved2; -}; +} __packed; struct efa_admin_modify_qp_resp { /* Common Admin Queue completion descriptor */ @@ -254,12 +248,9 @@ struct efa_admin_modify_qp_resp { }; struct efa_admin_query_qp_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* QP handle returned by create_qp command */ u32 qp_handle; -}; +} __packed; struct efa_admin_query_qp_resp { /* Common Admin Queue completion descriptor */ @@ -285,12 +276,9 @@ struct efa_admin_query_qp_resp { }; struct efa_admin_destroy_qp_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* QP handle returned by create_qp command */ u32 qp_handle; -}; +} __packed; struct efa_admin_destroy_qp_resp { /* Common Admin Queue completion descriptor */ @@ -302,9 +290,6 @@ struct efa_admin_destroy_qp_resp { * once for the same destination */ struct efa_admin_create_ah_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* Destination address in network byte order */ u8 dest_addr[16]; @@ -313,7 +298,7 @@ struct efa_admin_create_ah_cmd { /* MBZ */ u16 reserved; -}; +} __packed; struct efa_admin_create_ah_resp { /* Common Admin Queue completion descriptor */ @@ -327,15 +312,12 @@ struct efa_admin_create_ah_resp { }; struct efa_admin_destroy_ah_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* Target interface address handle (opaque) */ u16 ah; /* PD number */ u16 pd; -}; +} __packed; struct efa_admin_destroy_ah_resp { /* Common Admin Queue completion descriptor */ @@ -349,9 +331,6 @@ struct efa_admin_destroy_ah_resp { * on users working with very large datasets (i.e. full GPU memory mapping). */ struct efa_admin_reg_mr_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* Protection Domain */ u16 pd; @@ -413,7 +392,7 @@ struct efa_admin_reg_mr_cmd { * the region. */ u64 iova; -}; +} __packed; struct efa_admin_reg_mr_resp { /* Common Admin Queue completion descriptor */ @@ -459,12 +438,9 @@ struct efa_admin_reg_mr_resp { }; struct efa_admin_dereg_mr_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* L_Key, memory region's l_key */ u32 l_key; -}; +} __packed; struct efa_admin_dereg_mr_resp { /* Common Admin Queue completion descriptor */ @@ -476,9 +452,6 @@ struct efa_admin_dereg_mr_resp { * Addresses in kernel verbs semantics, ready for fast registration use. */ struct efa_admin_alloc_mr_cmd { - /* Common Admin Queue descriptor */ - struct efa_admin_aq_common_desc aq_common_desc; - /* Protection Domain */ u16 pd; @@ -487,7 +460,7 @@ struct efa_admin_alloc_mr_cmd { /* Maximum number of pages this MR supports. */ u32 max_pages; -}; +} __packed; struct efa_admin_alloc_mr_resp { /* Common Admin Queue completion descriptor */ @@ -507,8 +480,6 @@ struct efa_admin_alloc_mr_resp { }; struct efa_admin_create_cq_cmd { - struct efa_admin_aq_common_desc aq_common_desc; - /* * 4:0 : reserved5 - MBZ * 5 : interrupt_mode_enabled - if set, cq operates @@ -561,7 +532,7 @@ struct efa_admin_create_cq_cmd { /* UAR number */ u16 uar; -}; +} __packed; struct efa_admin_create_cq_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -582,13 +553,11 @@ struct efa_admin_create_cq_resp { }; struct efa_admin_destroy_cq_cmd { - struct efa_admin_aq_common_desc aq_common_desc; - u16 cq_idx; /* MBZ */ u16 reserved1; -}; +} __packed; struct efa_admin_destroy_cq_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -599,14 +568,7 @@ struct efa_admin_destroy_cq_resp { * buffer pointed by AQ entry */ struct efa_admin_aq_get_stats_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - - union { - /* command specific inline data */ - u32 inline_data_w1[3]; - - struct efa_admin_ctrl_buff_info control_buffer; - } u; + struct efa_admin_ctrl_buff_info control_buffer; /* stats type as defined in enum efa_admin_get_stats_type */ u8 type; @@ -615,7 +577,7 @@ struct efa_admin_aq_get_stats_cmd { u8 scope; u16 scope_modifier; -}; +} __packed; struct efa_admin_basic_stats { u64 tx_bytes; @@ -903,14 +865,12 @@ struct efa_admin_hw_hints { }; struct efa_admin_get_feature_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - struct efa_admin_ctrl_buff_info control_buffer; struct efa_admin_get_set_feature_common_desc feature_common; u32 raw[11]; -}; +} __packed; struct efa_admin_get_feature_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -935,8 +895,6 @@ struct efa_admin_get_feature_resp { }; struct efa_admin_set_feature_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - struct efa_admin_ctrl_buff_info control_buffer; struct efa_admin_get_set_feature_common_desc feature_common; @@ -947,7 +905,7 @@ struct efa_admin_set_feature_cmd { /* AENQ configuration */ struct efa_admin_feature_aenq_desc aenq; } u; -}; +} __packed; struct efa_admin_set_feature_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -957,10 +915,6 @@ struct efa_admin_set_feature_resp { } u; }; -struct efa_admin_alloc_pd_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; -}; - struct efa_admin_alloc_pd_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -972,23 +926,17 @@ struct efa_admin_alloc_pd_resp { }; struct efa_admin_dealloc_pd_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* PD number */ u16 pd; /* MBZ */ u16 reserved; -}; +} __packed; struct efa_admin_dealloc_pd_resp { struct efa_admin_acq_common_desc acq_common_desc; }; -struct efa_admin_alloc_uar_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; -}; - struct efa_admin_alloc_uar_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1000,22 +948,18 @@ struct efa_admin_alloc_uar_resp { }; struct efa_admin_dealloc_uar_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* UAR number */ u16 uar; /* MBZ */ u16 reserved; -}; +} __packed; struct efa_admin_dealloc_uar_resp { struct efa_admin_acq_common_desc acq_common_desc; }; struct efa_admin_create_eq_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* Size of the EQ in entries, must be power of 2 */ u16 depth; @@ -1041,7 +985,7 @@ struct efa_admin_create_eq_cmd { /* MBZ */ u32 reserved; -}; +} __packed; struct efa_admin_create_eq_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1054,14 +998,12 @@ struct efa_admin_create_eq_resp { }; struct efa_admin_destroy_eq_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* EQ number */ u16 eqn; /* MBZ */ u16 reserved; -}; +} __packed; struct efa_admin_destroy_eq_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1134,10 +1076,8 @@ struct efa_admin_host_info { }; struct efa_admin_service_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - u8 buffer[60]; -}; +} __packed; struct efa_admin_service_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1147,8 +1087,6 @@ struct efa_admin_service_resp { /* Create Counter command */ struct efa_admin_create_event_counter_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* UAR number */ u16 uar; @@ -1157,7 +1095,7 @@ struct efa_admin_create_event_counter_cmd { /* Counter physical address */ u64 paddr; -}; +} __packed; struct efa_admin_create_event_counter_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1170,11 +1108,9 @@ struct efa_admin_create_event_counter_resp { }; struct efa_admin_destroy_event_counter_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* Counter handle */ u32 cntr_handle; -}; +} __packed; struct efa_admin_destroy_event_counter_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1206,8 +1142,6 @@ struct efa_admin_event_counter_attach_qp_events { }; struct efa_admin_attach_detach_event_counter_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* Counter handle */ u32 cntr_handle; @@ -1220,7 +1154,7 @@ struct efa_admin_attach_detach_event_counter_cmd { union { struct efa_admin_event_counter_attach_qp_events qp_events; } u; -}; +} __packed; struct efa_admin_attach_detach_event_counter_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -1235,8 +1169,6 @@ enum efa_admin_event_counter_modify_ops { }; struct efa_admin_modify_event_counter_cmd { - struct efa_admin_aq_common_desc aq_common_descriptor; - /* Counter handle */ u32 cntr_handle; @@ -1248,7 +1180,7 @@ struct efa_admin_modify_event_counter_cmd { /* Value for SET or ADD */ u64 value; -}; +} __packed; struct efa_admin_modify_event_counter_resp { struct efa_admin_acq_common_desc acq_common_desc; diff --git a/drivers/infiniband/hw/efa/efa_admin_defs.h b/drivers/infiniband/hw/efa/efa_admin_defs.h index 02f86edabed8..0a14b3abe6f9 100644 --- a/drivers/infiniband/hw/efa/efa_admin_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_defs.h @@ -55,13 +55,7 @@ struct efa_admin_ctrl_buff_info { struct efa_admin_aq_entry { struct efa_admin_aq_common_desc aq_common_descriptor; - union { - u32 inline_data_w1[3]; - - struct efa_admin_ctrl_buff_info control_buffer; - } u; - - u32 inline_data_w4[12]; + u32 request_payload[15]; }; struct efa_admin_acq_common_desc { diff --git a/drivers/infiniband/hw/efa/efa_com.c b/drivers/infiniband/hw/efa/efa_com.c index 7097d1c2f23d..8d8be22b76c8 100644 --- a/drivers/infiniband/hw/efa/efa_com.c +++ b/drivers/infiniband/hw/efa/efa_com.c @@ -634,8 +634,10 @@ static int efa_com_wait_and_process_admin_cq(struct efa_comp_ctx *comp_ctx, /** * efa_com_cmd_exec - Execute admin command * @aq: admin queue. - * @cmd: the admin command to execute. - * @cmd_size: the command size. + * @opcode: the admin command opcode. + * @flags: the admin command header flags. + * @payload: the admin command payload. + * @payload_size: the payload size. * @comp: command completion return entry. * @comp_size: command completion size. * Submit an admin command and then wait until the device will return a @@ -645,22 +647,24 @@ static int efa_com_wait_and_process_admin_cq(struct efa_comp_ctx *comp_ctx, * @return - 0 on success, negative value on failure. */ int efa_com_cmd_exec(struct efa_com_admin_queue *aq, - struct efa_admin_aq_entry *cmd, - size_t cmd_size, - struct efa_admin_acq_entry *comp, - size_t comp_size) + u8 opcode, u8 flags, + void *payload, size_t payload_size, + struct efa_admin_acq_entry *comp, size_t comp_size) { + struct efa_admin_aq_entry aq_entry = {}; struct efa_comp_ctx *comp_ctx; int err; + if (payload_size > sizeof(aq_entry.request_payload)) + return -EINVAL; + might_sleep(); /* In case of queue FULL */ down(&aq->avail_cmds); - ibdev_dbg(aq->efa_dev, "%s (opcode %d)\n", - efa_com_cmd_str(cmd->aq_common_descriptor.opcode), - cmd->aq_common_descriptor.opcode); + ibdev_dbg(aq->efa_dev, "%s (opcode %d)\n", efa_com_cmd_str(opcode), + opcode); comp_ctx = efa_com_alloc_comp_ctx(aq); if (!comp_ctx) { @@ -669,13 +673,17 @@ int efa_com_cmd_exec(struct efa_com_admin_queue *aq, return -EINVAL; } - err = efa_com_submit_admin_cmd(aq, comp_ctx, cmd, cmd_size, comp, comp_size); + aq_entry.aq_common_descriptor.opcode = opcode; + aq_entry.aq_common_descriptor.flags = flags; + if (payload) + memcpy(aq_entry.request_payload, payload, payload_size); + + err = efa_com_submit_admin_cmd(aq, comp_ctx, &aq_entry, sizeof(aq_entry), comp, comp_size); if (err) { ibdev_err_ratelimited( aq->efa_dev, "Failed to submit command %s (opcode %u) err %d\n", - efa_com_cmd_str(cmd->aq_common_descriptor.opcode), - cmd->aq_common_descriptor.opcode, err); + efa_com_cmd_str(opcode), opcode, err); efa_com_dealloc_comp_ctx(aq, comp_ctx); up(&aq->avail_cmds); @@ -688,8 +696,7 @@ int efa_com_cmd_exec(struct efa_com_admin_queue *aq, ibdev_err_ratelimited( aq->efa_dev, "Failed to process command %s (opcode %u) err %d\n", - efa_com_cmd_str(cmd->aq_common_descriptor.opcode), - cmd->aq_common_descriptor.opcode, err); + efa_com_cmd_str(opcode), opcode, err); atomic64_inc(&aq->stats.cmd_err); } @@ -1156,7 +1163,6 @@ static int efa_com_create_eq(struct efa_com_dev *edev, struct efa_admin_create_eq_cmd cmd = {}; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_CREATE_EQ; EFA_SET(&cmd.caps, EFA_ADMIN_CREATE_EQ_CMD_ENTRY_SIZE_WORDS, params->entry_size_in_bytes / 4); cmd.depth = params->depth; @@ -1166,11 +1172,9 @@ static int efa_com_create_eq(struct efa_com_dev *edev, efa_com_set_dma_addr(params->dma_addr, &cmd.ba.mem_addr_high, &cmd.ba.mem_addr_low); - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_CREATE_EQ, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to create eq[%d]\n", err); @@ -1190,14 +1194,11 @@ static void efa_com_destroy_eq(struct efa_com_dev *edev, struct efa_admin_destroy_eq_cmd cmd = {}; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_DESTROY_EQ; cmd.eqn = params->eqn; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_DESTROY_EQ, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) ibdev_err_ratelimited(edev->efa_dev, "Failed to destroy EQ-%u [%d]\n", cmd.eqn, diff --git a/drivers/infiniband/hw/efa/efa_com.h b/drivers/infiniband/hw/efa/efa_com.h index 599db9d583bf..f979e36ec158 100644 --- a/drivers/infiniband/hw/efa/efa_com.h +++ b/drivers/infiniband/hw/efa/efa_com.h @@ -174,10 +174,9 @@ int efa_com_validate_version(struct efa_com_dev *edev); int efa_com_get_dma_width(struct efa_com_dev *edev); int efa_com_cmd_exec(struct efa_com_admin_queue *aq, - struct efa_admin_aq_entry *cmd, - size_t cmd_size, - struct efa_admin_acq_entry *comp, - size_t comp_size); + u8 opcode, u8 flags, + void *payload, size_t payload_size, + struct efa_admin_acq_entry *comp, size_t comp_size); void efa_com_aenq_intr_handler(struct efa_com_dev *edev, void *data); void efa_com_eq_comp_intr_handler(struct efa_com_dev *edev, struct efa_com_eq *eeq); diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 198a27d02b18..1b00f16b8ea8 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -17,8 +17,6 @@ int efa_com_create_qp(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - create_qp_cmd.aq_common_desc.opcode = EFA_ADMIN_CREATE_QP; - create_qp_cmd.pd = params->pd; create_qp_cmd.qp_type = params->qp_type; create_qp_cmd.rq_base_addr = params->rq_base_addr; @@ -41,9 +39,8 @@ int efa_com_create_qp(struct efa_com_dev *edev, if (params->sq_64_bit_req_id) EFA_SET(&create_qp_cmd.flags, EFA_ADMIN_CREATE_QP_CMD_SQ_64_BIT_REQ_ID, 1); - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&create_qp_cmd, - sizeof(create_qp_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_CREATE_QP, 0, + &create_qp_cmd, sizeof(create_qp_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -71,7 +68,6 @@ int efa_com_modify_qp(struct efa_com_dev *edev, struct efa_admin_modify_qp_resp resp; int err; - cmd.aq_common_desc.opcode = EFA_ADMIN_MODIFY_QP; cmd.modify_mask = params->modify_mask; cmd.qp_handle = params->qp_handle; cmd.qp_state = params->qp_state; @@ -81,11 +77,9 @@ int efa_com_modify_qp(struct efa_com_dev *edev, cmd.sq_drained_async_notify = params->sq_drained_async_notify; cmd.rnr_retry = params->rnr_retry; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_MODIFY_QP, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited( edev->efa_dev, @@ -106,14 +100,11 @@ int efa_com_query_qp(struct efa_com_dev *edev, struct efa_admin_query_qp_resp resp; int err; - cmd.aq_common_desc.opcode = EFA_ADMIN_QUERY_QP; cmd.qp_handle = params->qp_handle; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_QUERY_QP, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to query qp-%u [%d]\n", @@ -138,12 +129,10 @@ int efa_com_destroy_qp(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - qp_cmd.aq_common_desc.opcode = EFA_ADMIN_DESTROY_QP; qp_cmd.qp_handle = params->qp_handle; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&qp_cmd, - sizeof(qp_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_DESTROY_QP, 0, + &qp_cmd, sizeof(qp_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -165,7 +154,6 @@ int efa_com_create_cq(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - create_cmd.aq_common_desc.opcode = EFA_ADMIN_CREATE_CQ; EFA_SET(&create_cmd.cq_caps_2, EFA_ADMIN_CREATE_CQ_CMD_CQ_ENTRY_SIZE_WORDS, params->entry_size_in_bytes / 4); @@ -190,9 +178,8 @@ int efa_com_create_cq(struct efa_com_dev *edev, &create_cmd.cq_ba.mem_addr_high, &create_cmd.cq_ba.mem_addr_low); - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&create_cmd, - sizeof(create_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_CREATE_CQ, 0, + &create_cmd, sizeof(create_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -219,11 +206,9 @@ int efa_com_destroy_cq(struct efa_com_dev *edev, int err; destroy_cmd.cq_idx = params->cq_idx; - destroy_cmd.aq_common_desc.opcode = EFA_ADMIN_DESTROY_CQ; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&destroy_cmd, - sizeof(destroy_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_DESTROY_CQ, 0, + &destroy_cmd, sizeof(destroy_cmd), (struct efa_admin_acq_entry *)&destroy_resp, sizeof(destroy_resp)); @@ -244,9 +229,9 @@ int efa_com_register_mr(struct efa_com_dev *edev, struct efa_admin_reg_mr_resp cmd_completion; struct efa_com_admin_queue *aq = &edev->aq; struct efa_admin_reg_mr_cmd mr_cmd = {}; + u8 flags = 0; int err; - mr_cmd.aq_common_desc.opcode = EFA_ADMIN_REG_MR; mr_cmd.pd = params->pd; mr_cmd.mr_length = params->mr_length_in_bytes; EFA_SET(&mr_cmd.flags, EFA_ADMIN_REG_MR_CMD_PHYS_PAGE_SIZE_SHIFT, @@ -264,16 +249,13 @@ int efa_com_register_mr(struct efa_com_dev *edev, params->pbl.pbl.address.mem_addr_low; mr_cmd.pbl.pbl.address.mem_addr_high = params->pbl.pbl.address.mem_addr_high; - EFA_SET(&mr_cmd.aq_common_desc.flags, - EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); + EFA_SET(&flags, EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); if (params->indirect) - EFA_SET(&mr_cmd.aq_common_desc.flags, - EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA_INDIRECT, 1); + EFA_SET(&flags, EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA_INDIRECT, 1); } - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&mr_cmd, - sizeof(mr_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_REG_MR, flags, + &mr_cmd, sizeof(mr_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -305,12 +287,10 @@ int efa_com_dereg_mr(struct efa_com_dev *edev, struct efa_admin_dereg_mr_cmd mr_cmd = {}; int err; - mr_cmd.aq_common_desc.opcode = EFA_ADMIN_DEREG_MR; mr_cmd.l_key = params->l_key; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&mr_cmd, - sizeof(mr_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_DEREG_MR, 0, + &mr_cmd, sizeof(mr_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -345,14 +325,11 @@ int efa_com_create_ah(struct efa_com_dev *edev, return 0; } - ah_cmd.aq_common_desc.opcode = EFA_ADMIN_CREATE_AH; - memcpy(ah_cmd.dest_addr, params->dest_addr, sizeof(ah_cmd.dest_addr)); ah_cmd.pd = params->pdn; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&ah_cmd, - sizeof(ah_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_CREATE_AH, 0, + &ah_cmd, sizeof(ah_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -389,13 +366,11 @@ int efa_com_destroy_ah(struct efa_com_dev *edev, if (entry->usecnt > 1) goto out_put; - ah_cmd.aq_common_desc.opcode = EFA_ADMIN_DESTROY_AH; ah_cmd.ah = entry->ah; ah_cmd.pd = entry->key.pd; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&ah_cmd, - sizeof(ah_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_DESTROY_AH, 0, + &ah_cmd, sizeof(ah_cmd), (struct efa_admin_acq_entry *)&cmd_completion, sizeof(cmd_completion)); if (err) { @@ -436,6 +411,7 @@ static int efa_com_get_feature_ex(struct efa_com_dev *edev, { struct efa_admin_get_feature_cmd get_cmd = {}; struct efa_com_admin_queue *aq; + u8 flags = 0; int err; if (!efa_com_check_supported_feature_id(edev, feature_id)) { @@ -447,11 +423,8 @@ static int efa_com_get_feature_ex(struct efa_com_dev *edev, aq = &edev->aq; - get_cmd.aq_common_descriptor.opcode = EFA_ADMIN_GET_FEATURE; - if (control_buff_size) - EFA_SET(&get_cmd.aq_common_descriptor.flags, - EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); + EFA_SET(&flags, EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); efa_com_set_dma_addr(control_buf_dma_addr, &get_cmd.control_buffer.address.mem_addr_high, @@ -459,12 +432,9 @@ static int efa_com_get_feature_ex(struct efa_com_dev *edev, get_cmd.control_buffer.length = control_buff_size; get_cmd.feature_common.feature_id = feature_id; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *) - &get_cmd, - sizeof(get_cmd), - (struct efa_admin_acq_entry *) - get_resp, + err = efa_com_cmd_exec(aq, EFA_ADMIN_GET_FEATURE, flags, + &get_cmd, sizeof(get_cmd), + (struct efa_admin_acq_entry *)get_resp, sizeof(*get_resp)); if (err) { @@ -630,6 +600,7 @@ int efa_com_set_feature_ex(struct efa_com_dev *edev, u32 control_buff_size) { struct efa_com_admin_queue *aq; + u8 flags = 0; int err; if (!efa_com_check_supported_feature_id(edev, feature_id)) { @@ -641,11 +612,8 @@ int efa_com_set_feature_ex(struct efa_com_dev *edev, aq = &edev->aq; - set_cmd->aq_common_descriptor.opcode = EFA_ADMIN_SET_FEATURE; if (control_buff_size) { - set_cmd->aq_common_descriptor.flags = 0; - EFA_SET(&set_cmd->aq_common_descriptor.flags, - EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); + EFA_SET(&flags, EFA_ADMIN_AQ_COMMON_DESC_CTRL_DATA, 1); efa_com_set_dma_addr(control_buf_dma_addr, &set_cmd->control_buffer.address.mem_addr_high, &set_cmd->control_buffer.address.mem_addr_low); @@ -653,9 +621,8 @@ int efa_com_set_feature_ex(struct efa_com_dev *edev, set_cmd->control_buffer.length = control_buff_size; set_cmd->feature_common.feature_id = feature_id; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)set_cmd, - sizeof(*set_cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_SET_FEATURE, flags, + set_cmd, sizeof(*set_cmd), (struct efa_admin_acq_entry *)set_resp, sizeof(*set_resp)); @@ -726,17 +693,12 @@ int efa_com_alloc_pd(struct efa_com_dev *edev, struct efa_com_alloc_pd_result *result) { struct efa_com_admin_queue *aq = &edev->aq; - struct efa_admin_alloc_pd_cmd cmd = {}; struct efa_admin_alloc_pd_resp resp; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_ALLOC_PD; - - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_ALLOC_PD, 0, + NULL, 0, + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to allocate pd[%d]\n", err); @@ -756,14 +718,11 @@ int efa_com_dealloc_pd(struct efa_com_dev *edev, struct efa_admin_dealloc_pd_resp resp; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_DEALLOC_PD; cmd.pd = params->pdn; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_DEALLOC_PD, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to deallocate pd-%u [%d]\n", @@ -778,17 +737,12 @@ int efa_com_alloc_uar(struct efa_com_dev *edev, struct efa_com_alloc_uar_result *result) { struct efa_com_admin_queue *aq = &edev->aq; - struct efa_admin_alloc_uar_cmd cmd = {}; struct efa_admin_alloc_uar_resp resp; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_ALLOC_UAR; - - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_ALLOC_UAR, 0, + NULL, 0, + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to allocate uar[%d]\n", err); @@ -808,14 +762,11 @@ int efa_com_dealloc_uar(struct efa_com_dev *edev, struct efa_admin_dealloc_uar_resp resp; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_DEALLOC_UAR; cmd.uar = params->uarn; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_DEALLOC_UAR, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited(edev->efa_dev, "Failed to deallocate uar-%u [%d]\n", @@ -840,16 +791,13 @@ int efa_com_get_stats(struct efa_com_dev *edev, struct efa_admin_basic_stats *bs; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_GET_STATS; cmd.type = params->type; cmd.scope = params->scope; cmd.scope_modifier = params->scope_modifier; - err = efa_com_cmd_exec(aq, - (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), - (struct efa_admin_acq_entry *)&resp, - sizeof(resp)); + err = efa_com_cmd_exec(aq, EFA_ADMIN_GET_STATS, 0, + &cmd, sizeof(cmd), + (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { ibdev_err_ratelimited( edev->efa_dev, @@ -910,12 +858,11 @@ int efa_com_create_event_counter(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_CREATE_EVENT_COUNTER; cmd.uar = params->uarn; cmd.paddr = params->dma_addr; - err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_CREATE_EVENT_COUNTER, 0, + &cmd, sizeof(cmd), (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { @@ -937,11 +884,10 @@ int efa_com_destroy_event_counter(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_DESTROY_EVENT_COUNTER; cmd.cntr_handle = params->cntr_handle; - err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_DESTROY_EVENT_COUNTER, 0, + &cmd, sizeof(cmd), (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { @@ -963,14 +909,13 @@ static int efa_com_attach_detach_event_counter(struct efa_com_dev *edev, u8 opco struct efa_com_admin_queue *aq = &edev->aq; int err; - cmd.aq_common_descriptor.opcode = opcode; cmd.cntr_handle = cntr_handle; cmd.attach_type = EFA_ADMIN_EVENT_COUNTER_ATTACH_QP_EVENTS; cmd.u.qp_events.qp_handle = qp_handle; cmd.u.qp_events.events = events; - err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), + err = efa_com_cmd_exec(aq, opcode, 0, + &cmd, sizeof(cmd), (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { @@ -1014,13 +959,12 @@ int efa_com_modify_event_counter(struct efa_com_dev *edev, struct efa_com_admin_queue *aq = &edev->aq; int err; - cmd.aq_common_descriptor.opcode = EFA_ADMIN_MODIFY_EVENT_COUNTER; cmd.cntr_handle = params->cntr_handle; cmd.operation = params->operation; cmd.value = params->value; - err = efa_com_cmd_exec(aq, (struct efa_admin_aq_entry *)&cmd, - sizeof(cmd), + err = efa_com_cmd_exec(aq, EFA_ADMIN_MODIFY_EVENT_COUNTER, 0, + &cmd, sizeof(cmd), (struct efa_admin_acq_entry *)&resp, sizeof(resp)); if (err) { From bbbc5fe12d9da71ee6220e850a946b9551e7f483 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Wed, 12 Aug 2026 12:17:17 +0000 Subject: [PATCH 157/160] RDMA/efa: Generalize the admin SQ As preparation for admin v2 entry size which is 128B, generalize the SQ ring to use a generic buffer and use the right offset into it using the configured entry size. This will allow us to choose different entry size on SQ init with minimal changes. Link: https://patch.msgid.link/r/20260812121718.2904349-3-ynachum@amazon.com Reviewed-by: Michael Margolin Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/efa/efa_com.c | 48 +++++++++++++---------------- drivers/infiniband/hw/efa/efa_com.h | 3 +- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_com.c b/drivers/infiniband/hw/efa/efa_com.c index 8d8be22b76c8..72b87f8cd086 100644 --- a/drivers/infiniband/hw/efa/efa_com.c +++ b/drivers/infiniband/hw/efa/efa_com.c @@ -138,14 +138,13 @@ static int efa_com_admin_init_sq(struct efa_com_dev *edev) { struct efa_com_admin_queue *aq = &edev->aq; struct efa_com_admin_sq *sq = &aq->sq; - u16 size = aq->depth * sizeof(*sq->entries); + u32 addr_high, addr_low; u32 aq_caps = 0; - u32 addr_high; - u32 addr_low; - sq->entries = - dma_alloc_coherent(aq->dmadev, size, &sq->dma_addr, GFP_KERNEL); - if (!sq->entries) + sq->entry_size = sizeof(struct efa_admin_aq_entry); + sq->buffer = dma_alloc_coherent(aq->dmadev, aq->depth * sq->entry_size, + &sq->dma_addr, GFP_KERNEL); + if (!sq->buffer) return -ENOMEM; spin_lock_init(&sq->lock); @@ -163,8 +162,7 @@ static int efa_com_admin_init_sq(struct efa_com_dev *edev) writel(addr_high, edev->reg_bar + EFA_REGS_AQ_BASE_HI_OFF); EFA_SET(&aq_caps, EFA_REGS_AQ_CAPS_AQ_DEPTH, aq->depth); - EFA_SET(&aq_caps, EFA_REGS_AQ_CAPS_AQ_ENTRY_SIZE, - sizeof(struct efa_admin_aq_entry)); + EFA_SET(&aq_caps, EFA_REGS_AQ_CAPS_AQ_ENTRY_SIZE, sq->entry_size); writel(aq_caps, edev->reg_bar + EFA_REGS_AQ_CAPS_OFF); @@ -330,24 +328,22 @@ static void __efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, struct efa_admin_acq_entry *comp, size_t comp_size_in_bytes) { - struct efa_admin_aq_entry *aqe; - u16 queue_size_mask; - u16 cmd_id; - u16 ctx_id; - u16 pi; + u16 queue_size_mask, cmd_id, ctx_id, pi; + struct efa_com_admin_sq *sq = &aq->sq; + u8 *aqe; queue_size_mask = aq->depth - 1; - pi = aq->sq.pc & queue_size_mask; + pi = sq->pc & queue_size_mask; ctx_id = efa_com_get_comp_ctx_id(aq, comp_ctx); /* cmd_id LSBs are the ctx_id and MSBs are entropy bits from pc */ cmd_id = ctx_id & queue_size_mask; - cmd_id |= aq->sq.pc << ilog2(aq->depth); + cmd_id |= sq->pc << ilog2(aq->depth); cmd_id &= EFA_ADMIN_AQ_COMMON_DESC_COMMAND_ID_MASK; cmd->aq_common_descriptor.command_id = cmd_id; EFA_SET(&cmd->aq_common_descriptor.flags, - EFA_ADMIN_AQ_COMMON_DESC_PHASE, aq->sq.phase); + EFA_ADMIN_AQ_COMMON_DESC_PHASE, sq->phase); comp_ctx->status = EFA_CMD_SUBMITTED; comp_ctx->comp_size = comp_size_in_bytes; @@ -357,18 +353,18 @@ static void __efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, reinit_completion(&comp_ctx->wait_event); - aqe = &aq->sq.entries[pi]; - memset(aqe, 0, sizeof(*aqe)); + aqe = sq->buffer + sq->entry_size * pi; + memset(aqe, 0, sq->entry_size); memcpy(aqe, cmd, cmd_size_in_bytes); - aq->sq.pc++; + sq->pc++; atomic64_inc(&aq->stats.submitted_cmd); - if ((aq->sq.pc & queue_size_mask) == 0) - aq->sq.phase = !aq->sq.phase; + if ((sq->pc & queue_size_mask) == 0) + sq->phase = !sq->phase; /* barrier not needed in case of writel */ - writel(aq->sq.pc, aq->sq.db_addr); + writel(sq->pc, sq->db_addr); } static inline int efa_com_init_comp_ctxt(struct efa_com_admin_queue *aq) @@ -723,8 +719,8 @@ void efa_com_admin_destroy(struct efa_com_dev *edev) devm_kfree(edev->dmadev, aq->comp_ctx_pool); devm_kfree(edev->dmadev, aq->comp_ctx); - size = aq->depth * sizeof(*sq->entries); - dma_free_coherent(edev->dmadev, size, sq->entries, sq->dma_addr); + size = aq->depth * sq->entry_size; + dma_free_coherent(edev->dmadev, size, sq->buffer, sq->dma_addr); size = aq->depth * sizeof(*cq->entries); dma_free_coherent(edev->dmadev, size, cq->entries, cq->dma_addr); @@ -843,8 +839,8 @@ int efa_com_admin_init(struct efa_com_dev *edev, dma_free_coherent(edev->dmadev, aq->depth * sizeof(*aq->cq.entries), aq->cq.entries, aq->cq.dma_addr); err_destroy_sq: - dma_free_coherent(edev->dmadev, aq->depth * sizeof(*aq->sq.entries), - aq->sq.entries, aq->sq.dma_addr); + dma_free_coherent(edev->dmadev, aq->depth * aq->sq.entry_size, + aq->sq.buffer, aq->sq.dma_addr); err_destroy_comp_ctxt: devm_kfree(edev->dmadev, aq->comp_ctx); err_destroy_ah_cache: diff --git a/drivers/infiniband/hw/efa/efa_com.h b/drivers/infiniband/hw/efa/efa_com.h index f979e36ec158..069c9dd98328 100644 --- a/drivers/infiniband/hw/efa/efa_com.h +++ b/drivers/infiniband/hw/efa/efa_com.h @@ -33,7 +33,8 @@ struct efa_com_admin_cq { }; struct efa_com_admin_sq { - struct efa_admin_aq_entry *entries; + u8 *buffer; + u16 entry_size; dma_addr_t dma_addr; spinlock_t lock; /* Protects ASQ */ From d87924b995ab6d33e1c102a705837afdb8fff618 Mon Sep 17 00:00:00 2001 From: Yonatan Nachum Date: Wed, 12 Aug 2026 12:17:18 +0000 Subject: [PATCH 158/160] RDMA/efa: Add support for 128B admin v2 SQ entry Add the new admin v2 format which is 128B in size and its header extends the v1 header with checksum and payload version. On admin SQ init check if the API version reported by the device supports the admin v2 SQ entry and if so use it. Store the payload offset and max size in the SQ for quick access in admin command execution flow. Using the admin SQ v2 entry implicitly enable the checksum in its header so set it for device to validate against. Link: https://patch.msgid.link/r/20260812121718.2904349-4-ynachum@amazon.com Reviewed-by: Michael Margolin Reviewed-by: Tom Sela Signed-off-by: Yonatan Nachum Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/efa/efa_admin_defs.h | 29 +++++- drivers/infiniband/hw/efa/efa_com.c | 113 +++++++++++++++------ drivers/infiniband/hw/efa/efa_com.h | 6 ++ 3 files changed, 116 insertions(+), 32 deletions(-) diff --git a/drivers/infiniband/hw/efa/efa_admin_defs.h b/drivers/infiniband/hw/efa/efa_admin_defs.h index 0a14b3abe6f9..bf1721e31a70 100644 --- a/drivers/infiniband/hw/efa/efa_admin_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_defs.h @@ -7,7 +7,7 @@ #define _EFA_ADMIN_H_ #define EFA_ADMIN_API_VERSION_MAJOR 0 -#define EFA_ADMIN_API_VERSION_MINOR 2 +#define EFA_ADMIN_API_VERSION_MINOR 3 enum efa_admin_aq_completion_status { EFA_ADMIN_SUCCESS = 0, @@ -41,6 +41,21 @@ struct efa_admin_aq_common_desc { u8 flags; }; +struct efa_admin_aq_common_desc_v2 { + struct efa_admin_aq_common_desc common; + + /* + * Poly 0x8005 CRC16 with initial value 0xFFFF and final XOR of + * 0xFFFF. The checksum covers the entire admin command entry + * including the zeroed checksum field. + */ + u16 checksum; + + u8 payload_ver; + + u8 reserved[5]; +}; + /* * used in efa_admin_aq_entry. Can point directly to control data, or to a * page list chunk. Used also at the end of indirect mode page list chunks, @@ -58,6 +73,12 @@ struct efa_admin_aq_entry { u32 request_payload[15]; }; +struct efa_admin_aq_entry_v2 { + struct efa_admin_aq_common_desc_v2 aq_common_descriptor; + + u32 request_payload[29]; +}; + struct efa_admin_acq_common_desc { /* * command identifier to associate it with the aq descriptor @@ -74,7 +95,11 @@ struct efa_admin_acq_common_desc { */ u8 flags; - /* Poly 0x8005 CRC16 with initial value 0xFFFF and final XOR of 0xFFFF */ + /* + * Poly 0x8005 CRC16 with initial value 0xFFFF and final XOR of 0xFFFF. + * The checksum covers the entire admin completion entry including the + * zeroed checksum field. + */ u16 checksum; u16 reserved; diff --git a/drivers/infiniband/hw/efa/efa_com.c b/drivers/infiniband/hw/efa/efa_com.c index 72b87f8cd086..583b1cf0d721 100644 --- a/drivers/infiniband/hw/efa/efa_com.c +++ b/drivers/infiniband/hw/efa/efa_com.c @@ -25,11 +25,16 @@ #define EFA_CRC16_INIT_VAL 0xffff -#define EFA_CRC_MIN_ADMIN_API_VERSION_MAJOR 0 -#define EFA_CRC_MIN_ADMIN_API_VERSION_MINOR 2 +#define EFA_ADMIN_SQ_MAX_ENT_SIZE sizeof(struct efa_admin_aq_entry_v2) -#define EFA_MIN_ADMIN_API_VERSION_MAJOR 0 -#define EFA_MIN_ADMIN_API_VERSION_MINOR 1 +#define EFA_CRC_MIN_API_VERSION_MAJOR 0 +#define EFA_CRC_MIN_API_VERSION_MINOR 2 + +#define EFA_ADMIN_V2_MIN_API_VERSION_MAJOR 0 +#define EFA_ADMIN_V2_MIN_API_VERSION_MINOR 3 + +#define EFA_MIN_API_VERSION_MAJOR 0 +#define EFA_MIN_API_VERSION_MINOR 1 enum efa_cmd_status { EFA_CMD_UNUSED, @@ -82,6 +87,16 @@ void efa_com_set_dma_addr(dma_addr_t addr, u32 *addr_high, u32 *addr_low) *addr_high = upper_32_bits(addr); } +static u32 efa_com_construct_ver(u32 major, u32 minor) +{ + u32 ver = 0; + + EFA_SET(&ver, EFA_REGS_VERSION_MAJOR_VERSION, major); + EFA_SET(&ver, EFA_REGS_VERSION_MINOR_VERSION, minor); + + return ver; +} + static u32 efa_com_reg_read32(struct efa_com_dev *edev, u16 offset) { struct efa_com_mmio_read *mmio_read = &edev->mmio_read; @@ -138,10 +153,23 @@ static int efa_com_admin_init_sq(struct efa_com_dev *edev) { struct efa_com_admin_queue *aq = &edev->aq; struct efa_com_admin_sq *sq = &aq->sq; + u32 aq_caps = 0, admin_v2_min_ver = 0; u32 addr_high, addr_low; - u32 aq_caps = 0; - sq->entry_size = sizeof(struct efa_admin_aq_entry); + admin_v2_min_ver = efa_com_construct_ver(EFA_ADMIN_V2_MIN_API_VERSION_MAJOR, + EFA_ADMIN_V2_MIN_API_VERSION_MINOR); + if (edev->dev_api_ver >= admin_v2_min_ver) { + sq->entry_size = sizeof(struct efa_admin_aq_entry_v2); + sq->payload_offset = offsetof(struct efa_admin_aq_entry_v2, request_payload); + sq->proto_ver = EFA_ADMIN_V2_PROTO_VER; + } else { + sq->entry_size = sizeof(struct efa_admin_aq_entry); + sq->payload_offset = offsetof(struct efa_admin_aq_entry, request_payload); + sq->proto_ver = EFA_ADMIN_V1_PROTO_VER; + } + + sq->max_payload_size = sq->entry_size - sq->payload_offset; + sq->buffer = dma_alloc_coherent(aq->dmadev, aq->depth * sq->entry_size, &sq->dma_addr, GFP_KERNEL); if (!sq->buffer) @@ -184,8 +212,8 @@ static int efa_com_admin_init_cq(struct efa_com_dev *edev) spin_lock_init(&cq->lock); - EFA_SET(&crc_min_ver, EFA_REGS_VERSION_MAJOR_VERSION, EFA_CRC_MIN_ADMIN_API_VERSION_MAJOR); - EFA_SET(&crc_min_ver, EFA_REGS_VERSION_MINOR_VERSION, EFA_CRC_MIN_ADMIN_API_VERSION_MINOR); + crc_min_ver = efa_com_construct_ver(EFA_CRC_MIN_API_VERSION_MAJOR, + EFA_CRC_MIN_API_VERSION_MINOR); if (edev->dev_api_ver >= crc_min_ver) cq->validate_checksum = true; @@ -321,13 +349,45 @@ static inline struct efa_comp_ctx *efa_com_get_comp_ctx_by_cmd_id(struct efa_com return &aq->comp_ctx[ctx_id]; } +static u16 efa_com_calc_crc16_checksum(u8 *buff, u32 buff_size) +{ + return crc16(EFA_CRC16_INIT_VAL, buff, buff_size) ^ EFA_CRC16_INIT_VAL; +} + +static void efa_com_construct_aq_entry(struct efa_com_admin_queue *aq, u8 *aq_entry, u16 cmd_id, + u8 opcode, u8 flags, void *payload, size_t payload_size) +{ + struct efa_admin_aq_common_desc_v2 *common_v2 = NULL; + struct efa_admin_aq_common_desc *common; + struct efa_com_admin_sq *sq = &aq->sq; + + if (sq->proto_ver == EFA_ADMIN_V1_PROTO_VER) { + common = (struct efa_admin_aq_common_desc *)aq_entry; + } else { + common_v2 = (struct efa_admin_aq_common_desc_v2 *)aq_entry; + common = &common_v2->common; + } + + common->command_id = cmd_id; + common->opcode = opcode; + common->flags = flags; + EFA_SET(&common->flags, EFA_ADMIN_AQ_COMMON_DESC_PHASE, sq->phase); + + if (payload) + memcpy(aq_entry + sq->payload_offset, payload, payload_size); + + if (common_v2) + common_v2->checksum = efa_com_calc_crc16_checksum(aq_entry, sq->entry_size); +} + static void __efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, struct efa_comp_ctx *comp_ctx, - struct efa_admin_aq_entry *cmd, - size_t cmd_size_in_bytes, + u8 opcode, u8 flags, + void *payload, size_t payload_size, struct efa_admin_acq_entry *comp, size_t comp_size_in_bytes) { + u8 aq_entry[EFA_ADMIN_SQ_MAX_ENT_SIZE] __aligned(sizeof(u64)) = {}; u16 queue_size_mask, cmd_id, ctx_id, pi; struct efa_com_admin_sq *sq = &aq->sq; u8 *aqe; @@ -341,21 +401,19 @@ static void __efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, cmd_id |= sq->pc << ilog2(aq->depth); cmd_id &= EFA_ADMIN_AQ_COMMON_DESC_COMMAND_ID_MASK; - cmd->aq_common_descriptor.command_id = cmd_id; - EFA_SET(&cmd->aq_common_descriptor.flags, - EFA_ADMIN_AQ_COMMON_DESC_PHASE, sq->phase); + efa_com_construct_aq_entry(aq, aq_entry, cmd_id, opcode, flags, payload, payload_size); comp_ctx->status = EFA_CMD_SUBMITTED; comp_ctx->comp_size = comp_size_in_bytes; comp_ctx->user_cqe = comp; - comp_ctx->cmd_opcode = cmd->aq_common_descriptor.opcode; + comp_ctx->cmd_opcode = opcode; comp_ctx->cmd_id = cmd_id; reinit_completion(&comp_ctx->wait_event); aqe = sq->buffer + sq->entry_size * pi; memset(aqe, 0, sq->entry_size); - memcpy(aqe, cmd, cmd_size_in_bytes); + memcpy(aqe, aq_entry, sq->entry_size); sq->pc++; atomic64_inc(&aq->stats.submitted_cmd); @@ -399,8 +457,8 @@ static inline int efa_com_init_comp_ctxt(struct efa_com_admin_queue *aq) static int efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, struct efa_comp_ctx *comp_ctx, - struct efa_admin_aq_entry *cmd, - size_t cmd_size_in_bytes, + u8 opcode, u8 flags, + void *payload, size_t payload_size, struct efa_admin_acq_entry *comp, size_t comp_size_in_bytes) { @@ -411,8 +469,8 @@ static int efa_com_submit_admin_cmd(struct efa_com_admin_queue *aq, return -ENODEV; } - __efa_com_submit_admin_cmd(aq, comp_ctx, cmd, cmd_size_in_bytes, comp, - comp_size_in_bytes); + __efa_com_submit_admin_cmd(aq, comp_ctx, opcode, flags, payload, + payload_size, comp, comp_size_in_bytes); spin_unlock(&aq->sq.lock); return 0; @@ -426,7 +484,7 @@ static bool efa_com_cqe_checksum_valid(struct efa_com_admin_queue *aq, cqe->acq_common_descriptor.checksum = 0; - calc_checksum = crc16(EFA_CRC16_INIT_VAL, (u8 *)cqe, sizeof(*cqe)) ^ EFA_CRC16_INIT_VAL; + calc_checksum = efa_com_calc_crc16_checksum((u8 *)cqe, sizeof(*cqe)); if (calc_checksum != cqe_checksum) { ibdev_err(aq->efa_dev, "Received completion with invalid checksum, cqe[%u], calc[%u], sq producer[%d], sq consumer[%d], cq consumer[%d]\n", @@ -647,11 +705,10 @@ int efa_com_cmd_exec(struct efa_com_admin_queue *aq, void *payload, size_t payload_size, struct efa_admin_acq_entry *comp, size_t comp_size) { - struct efa_admin_aq_entry aq_entry = {}; struct efa_comp_ctx *comp_ctx; int err; - if (payload_size > sizeof(aq_entry.request_payload)) + if (payload_size > aq->sq.max_payload_size) return -EINVAL; might_sleep(); @@ -669,12 +726,8 @@ int efa_com_cmd_exec(struct efa_com_admin_queue *aq, return -EINVAL; } - aq_entry.aq_common_descriptor.opcode = opcode; - aq_entry.aq_common_descriptor.flags = flags; - if (payload) - memcpy(aq_entry.request_payload, payload, payload_size); - - err = efa_com_submit_admin_cmd(aq, comp_ctx, &aq_entry, sizeof(aq_entry), comp, comp_size); + err = efa_com_submit_admin_cmd(aq, comp_ctx, opcode, flags, payload, payload_size, comp, + comp_size); if (err) { ibdev_err_ratelimited( aq->efa_dev, @@ -1003,8 +1056,8 @@ int efa_com_validate_version(struct efa_com_dev *edev) EFA_GET(&ver, EFA_REGS_VERSION_MAJOR_VERSION), EFA_GET(&ver, EFA_REGS_VERSION_MINOR_VERSION)); - EFA_SET(&min_ver, EFA_REGS_VERSION_MAJOR_VERSION, EFA_MIN_ADMIN_API_VERSION_MAJOR); - EFA_SET(&min_ver, EFA_REGS_VERSION_MINOR_VERSION, EFA_MIN_ADMIN_API_VERSION_MINOR); + min_ver = efa_com_construct_ver(EFA_MIN_API_VERSION_MAJOR, + EFA_MIN_API_VERSION_MINOR); if (ver < min_ver) { ibdev_err(edev->efa_dev, "EFA version is lower than the minimal version the driver supports\n"); diff --git a/drivers/infiniband/hw/efa/efa_com.h b/drivers/infiniband/hw/efa/efa_com.h index 069c9dd98328..0341704d0921 100644 --- a/drivers/infiniband/hw/efa/efa_com.h +++ b/drivers/infiniband/hw/efa/efa_com.h @@ -22,6 +22,9 @@ #define EFA_MAX_HANDLERS 256 +#define EFA_ADMIN_V1_PROTO_VER 0 +#define EFA_ADMIN_V2_PROTO_VER 1 + struct efa_com_admin_cq { struct efa_admin_acq_entry *entries; dma_addr_t dma_addr; @@ -35,8 +38,11 @@ struct efa_com_admin_cq { struct efa_com_admin_sq { u8 *buffer; u16 entry_size; + u16 payload_offset; + u16 max_payload_size; dma_addr_t dma_addr; spinlock_t lock; /* Protects ASQ */ + u8 proto_ver; u32 __iomem *db_addr; From 60a42d510113f46de47e86a84bf5758597644487 Mon Sep 17 00:00:00 2001 From: Yuhang Pan <242270054@hdu.edu.cn> Date: Fri, 14 Aug 2026 17:27:23 +0800 Subject: [PATCH 159/160] RDMA/uverbs: Guard legacy bundles without method_elm The legacy write() path dispatches through a uverbs_api_write_method, but the uverbs_attr_bundle passed to provider code does not have an ioctl method element. If malformed provider input causes the common uverbs validation code to emit an error message, uverbs_get_handler_fn() dereferences the uninitialized method_elm pointer. Initialize method_elm explicitly for legacy bundles and make uverbs_get_handler_fn() return NULL when no ioctl method is present. The legacy dispatcher continues to use its local write method, while the ioctl path continues to use the registered ioctl handler. Cc: stable@vger.kernel.org Fixes: 7122ff96068a ("RDMA/core: Do not read wild stack memory in uverbs_get_handler_fn()") Link: https://patch.msgid.link/r/AOYAQgCQK3IXqJLr1TB5Qao9.1.1787036796115.Hmail.242270054@hdu.edu.cn Signed-off-by: Yuhang Pan <242270054@hdu.edu.cn> Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ib_core_uverbs.c | 3 +++ drivers/infiniband/core/uverbs_main.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index dbbc0875132a..41c84ffe8c09 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -424,6 +424,9 @@ static uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata) lockdep_assert_held(&bundle->ufile->device->disassociate_srcu); + if (!bundle->method_elm) + return NULL; + return srcu_dereference(bundle->method_elm->handler, &bundle->ufile->device->disassociate_srcu); } diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 3ccf58e96aed..0d88b2ee68ff 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -556,6 +556,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, bundle.ufile = file; bundle.context = NULL; /* only valid if bundle has uobject */ bundle.uobject = NULL; + bundle.method_elm = NULL; if (!method_elm->is_ex) { size_t in_len = hdr.in_words * 4 - sizeof(hdr); size_t out_len = hdr.out_words * 4; From 8049741ac93acd3a590dac070e12571fddf0e294 Mon Sep 17 00:00:00 2001 From: Serhat Kumral Date: Thu, 6 Aug 2026 23:13:58 +0300 Subject: [PATCH 160/160] RDMA/ucma: Allow path records to exactly fit the output buffer ucma_query_path() emits a path record only when the remaining output buffer is strictly larger than struct ib_path_rec_data. A buffer sized exactly for the response header and N complete records therefore gets only N - 1 records, while resp->num_paths still advertises N. A caller sizing its buffer for a single record gets a header claiming one path and no path data at all. ucma_query_ib_service() in the same file computes the record count with a plain division and so accepts an exact fit; make ucma_query_path() behave the same way. Current librdmacm is unaffected because it always sizes the response for six records while the kernel currently reports at most two paths. Other users of the UAPI that provide an exactly sized buffer can observe the truncated response. Fixes: ac53b264b2f3 ("RDMA/ucma: Support querying when IB paths are not reversible") Signed-off-by: Serhat Kumral Link: https://patch.msgid.link/20260806201358.147478-1-serhatkumral1@gmail.com Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/ucma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index ac29dfa69bb3..4929636f7c53 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -951,7 +951,7 @@ static ssize_t ucma_query_path(struct ucma_context *ctx, resp->num_paths = ctx->cm_id->route.num_pri_alt_paths; for (i = 0, out_len -= sizeof(*resp); - i < resp->num_paths && out_len > sizeof(struct ib_path_rec_data); + i < resp->num_paths && out_len >= sizeof(struct ib_path_rec_data); i++, out_len -= sizeof(struct ib_path_rec_data)) { struct sa_path_rec *rec = &ctx->cm_id->route.path_rec[i];