[PATCH rdma-next 13/15] RDMA/mlx5: Set QP event handler before firmware QPC insertion
From: Yishai Hadas <yishaih@nvidia.com>
Date: 2026-09-08 15:31:25
Subsystem:
infiniband subsystem, mellanox mlx5 ib driver, the rest · Maintainers:
Jason Gunthorpe, Leon Romanovsky, Linus Torvalds
Four QP create paths (create_xrc_tgt_qp, create_dci, create_qp's default
path for create_user_qp/create_kernel_qp, and the raw-packet QP SQ
object in create_raw_packet_qp) assigned base->container_mibqp and
base->mqp.event only after mlx5_qpc_create_qp() /
create_raw_packet_qp_sq() returned. Those functions insert the QPC into
dev->qp_table.tree, making it immediately visible to
rsc_event_notifier(). A hostile NIC can fire a QP error EQE for the new
QPN/SQN in that window, reaching qp->event() with a NULL function
pointer and causing a kernel oops. This is the same class of bug already
fixed for the raw-packet QP's RQ object in a previous commit.
Move both assignments before the firmware create call in each path so
the event handler is always valid by the time the resource is reachable
by events. to_mibqp() indirects through container_mibqp, so it must also
be set before insertion.
Add a WARN_ON_ONCE(!qp->event) guard in rsc_event_notifier() as a
belt-and-suspenders defense against any future code paths that may
re-introduce the same ordering hazard.
Also set ibqp.qp_num in mlx5_qpc_create_qp() before
create_resource_common() inserts the QP into the radix tree, so an EQE
arriving in that window does not observe qp_num == 0. create_qp() still
overrides this with 0/1 for QP0/QP1 afterwards, and DCT sets its own
identifier independently since it does not go through this function.
Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters")
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
---
drivers/infiniband/hw/mlx5/qp.c | 22 ++++++++++------------
drivers/infiniband/hw/mlx5/qpc.c | 10 ++++++++++
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 7f18ff364e62..2af599b81886 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c@@ -1624,6 +1624,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (err) return err; + sq->base.container_mibqp = qp; + sq->base.mqp.event = mlx5_ib_qp_event; err = create_raw_packet_qp_sq(dev, udata, attrs, sq, in, pd, to_mcq(init_attr->send_cq)); if (err)
@@ -1635,9 +1637,6 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, resp->sqn = sq->base.mqp.qpn; resp->comp_mask |= MLX5_IB_CREATE_QP_RESP_MASK_SQN; } - - sq->base.container_mibqp = qp; - sq->base.mqp.event = mlx5_ib_qp_event; } if (qp->rq.wqe_cnt) {
@@ -2093,13 +2092,13 @@ static int create_xrc_tgt_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, } base = &qp->trans_qp.base; + base->container_mibqp = qp; + base->mqp.event = mlx5_ib_qp_event; err = mlx5_qpc_create_qp(dev, &base->mqp, in, inlen, out); kvfree(in); if (err) return err; - base->container_mibqp = qp; - base->mqp.event = mlx5_ib_qp_event; if (MLX5_CAP_GEN(mdev, ece_support)) params->resp.ece_options = MLX5_GET(create_qp_out, out, ece);
@@ -2237,14 +2236,14 @@ static int create_dci(struct mlx5_ib_dev *dev, struct ib_pd *pd, qp->flags &= ~IB_QP_CREATE_PCI_WRITE_END_PADDING; } + base->container_mibqp = qp; + base->mqp.event = mlx5_ib_qp_event; err = mlx5_qpc_create_qp(dev, &base->mqp, in, inlen, out); kvfree(in); if (err) goto err_create; - base->container_mibqp = qp; - base->mqp.event = mlx5_ib_qp_event; if (MLX5_CAP_GEN(mdev, ece_support)) params->resp.ece_options = MLX5_GET(create_qp_out, out, ece);
@@ -2433,6 +2432,8 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, qp->flags &= ~IB_QP_CREATE_PCI_WRITE_END_PADDING; } + base->container_mibqp = qp; + base->mqp.event = mlx5_ib_qp_event; if (init_attr->qp_type == IB_QPT_RAW_PACKET || qp->flags & IB_QP_CREATE_SOURCE_QPN) { qp->raw_packet_qp.sq.ubuffer.buf_addr = ucmd->sq_buf_addr;
@@ -2447,8 +2448,6 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, if (err) goto err_create; - base->container_mibqp = qp; - base->mqp.event = mlx5_ib_qp_event; if (MLX5_CAP_GEN(mdev, ece_support)) params->resp.ece_options = MLX5_GET(create_qp_out, out, ece);
@@ -2578,14 +2577,13 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, MLX5_CAP_GEN(mdev, go_back_n)) MLX5_SET(qpc, qpc, retry_mode, MLX5_QP_RM_GO_BACK_N); + base->container_mibqp = qp; + base->mqp.event = mlx5_ib_qp_event; err = mlx5_qpc_create_qp(dev, &base->mqp, in, inlen, out); kvfree(in); if (err) goto err_create; - base->container_mibqp = qp; - base->mqp.event = mlx5_ib_qp_event; - get_cqs(qp->type, attr->send_cq, attr->recv_cq, &send_cq, &recv_cq); spin_lock_irqsave(&dev->reset_flow_resource_lock, flags);
diff --git a/drivers/infiniband/hw/mlx5/qpc.c b/drivers/infiniband/hw/mlx5/qpc.c
index a7a4f9420271..77ddab666ecd 100644
--- a/drivers/infiniband/hw/mlx5/qpc.c
+++ b/drivers/infiniband/hw/mlx5/qpc.c@@ -146,6 +146,8 @@ static int rsc_event_notifier(struct notifier_block *nb, case MLX5_RES_RQ: case MLX5_RES_SQ: qp = (struct mlx5_core_qp *)common; + if (WARN_ON_ONCE(!qp->event)) + goto out; qp->event(qp, event_type); /* Need to put resource in event handler */ return NOTIFY_OK;
@@ -258,6 +260,14 @@ int mlx5_qpc_create_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp, qp->uid = MLX5_GET(create_qp_in, in, uid); qp->qpn = MLX5_GET(create_qp_out, out, qpn); + /* Set ibqp.qp_num before create_resource_common() inserts the QP into + * the radix tree and makes it visible to EQE processing. The + * assignment in create_qp() happens only after this function returns, + * leaving a window where an arriving EQE would observe qp_num == 0. + * create_qp() still overrides this with 0/1 for QP0/QP1, and sets it + * independently for DCT (which does not go through this function). + */ + to_mibqp(qp)->ibqp.qp_num = qp->qpn; err = create_resource_common(dev, qp, MLX5_RES_QP); if (err)
--
2.18.1