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: 1550557717 ("RDMA/erdma: Add verbs implementation")
Signed-off-by: Cheng Xu <chengyou@linux.alibaba.com>
Link: https://patch.msgid.link/20260730124357.12976-2-chengyou@linux.alibaba.com
Signed-off-by: Leon Romanovsky <leon@kernel.org>
This commit is contained in:
Cheng Xu 2026-07-30 20:43:54 +08:00 committed by Leon Romanovsky
parent 98df2aee14
commit a52eeff320
4 changed files with 24 additions and 6 deletions

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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)
{