[PATCH rdma-next v5 08/10] RDMA/mana_ib: Flush and notify CQs when kernel QPs enter ERR

From: Konstantin Taranov

Date: Sat Oct 17 2026 - 02:47:07 EST


From: Konstantin Taranov <kotaranov@xxxxxxxxxxxxx>

Complete the RC software flush path by adding a kernel QP to the CQ
error lists when it transitions to the ERR state.

Invoke the CQ handlers after the transition to flush pending work
requests.

Signed-off-by: Konstantin Taranov <kotaranov@xxxxxxxxxxxxx>
---
v5:
- Remove the unused QP state field.
v3:
- Skip kernel state update handling for userspace QPs.
- Warn and refuse to add userspace QPs to the CQ error lists.
- Add mana_remove_qp_from_cqs() for UC
v2:
- Skip software CQ notifications for IB_POLL_DIRECT.

drivers/infiniband/hw/mana/cq.c | 2 +-
drivers/infiniband/hw/mana/qp.c | 53 ++++++++++++++++++++++++++++++---
2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/drivers/infiniband/hw/mana/cq.c b/drivers/infiniband/hw/mana/cq.c
index a4d886b9ecc6..90a43c7feb31 100644
--- a/drivers/infiniband/hw/mana/cq.c
+++ b/drivers/infiniband/hw/mana/cq.c
@@ -589,7 +589,7 @@ static void mana_drain_gsi_sq(struct mana_ib_qp *qp)
list_add_tail(&qp->send_err_node, &cq->send_err_qp_list);
spin_unlock_irqrestore(&cq->cq_lock, flags);

- if (cq->ibcq.comp_handler)
+ if (cq->ibcq.poll_ctx != IB_POLL_DIRECT && cq->ibcq.comp_handler)
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
}

diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c
index 5d6f45b9f53d..18242e1a6360 100644
--- a/drivers/infiniband/hw/mana/qp.c
+++ b/drivers/infiniband/hw/mana/qp.c
@@ -770,6 +770,27 @@ destroy_queues:
return err;
}

+static void mana_add_qp_to_error_cqs(struct mana_ib_qp *qp)
+{
+ 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);
+ unsigned long flags;
+
+ if (WARN_ON_ONCE(qp->ibqp.uobject))
+ return;
+
+ /* Keep ERR QPs linked until reset/destroy, including later drain WRs. */
+ spin_lock_irqsave(&send_cq->cq_lock, flags);
+ if (list_empty(&qp->send_err_node))
+ list_add_tail(&qp->send_err_node, &send_cq->send_err_qp_list);
+ spin_unlock_irqrestore(&send_cq->cq_lock, flags);
+
+ spin_lock_irqsave(&recv_cq->cq_lock, flags);
+ if (list_empty(&qp->recv_err_node))
+ list_add_tail(&qp->recv_err_node, &recv_cq->recv_err_qp_list);
+ spin_unlock_irqrestore(&recv_cq->cq_lock, flags);
+}
+
static void mana_remove_qp_from_cqs(struct mana_ib_qp *qp, bool reset)
{
struct mana_ib_cq *send_cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq);
@@ -1070,15 +1091,16 @@ static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
return 0;
}

-static void mana_ib_modify_qp_state(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+static bool mana_ib_modify_qp_state(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibqp->device, struct mana_ib_dev, ib_dev);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct gdma_queue *rq;
+ bool notify = false;

- if (udata)
- return;
+ if (udata || ibqp->uobject)
+ return false;

if (attr_mask & IB_QP_PATH_MTU)
qp->mtu = attr->path_mtu;
@@ -1091,6 +1113,10 @@ static void mana_ib_modify_qp_state(struct ib_qp *ibqp, struct ib_qp_attr *attr,
default:
break;
}
+ if (attr->qp_state == IB_QPS_ERR) {
+ mana_add_qp_to_error_cqs(qp);
+ notify = true;
+ }
}

if (attr_mask & IB_QP_SQ_PSN) {
@@ -1106,12 +1132,27 @@ static void mana_ib_modify_qp_state(struct ib_qp *ibqp, struct ib_qp_attr *attr,
SET_ARM_BIT, MANA_PSN_CLIENT_OFFSET);
}
}
+
+ return notify;
+}
+
+static void mana_ib_notify_error_cqs(struct mana_ib_qp *qp)
+{
+ struct ib_cq *send_cq = qp->ibqp.send_cq;
+ struct ib_cq *recv_cq = qp->ibqp.recv_cq;
+
+ if (send_cq->poll_ctx != IB_POLL_DIRECT && send_cq->comp_handler)
+ send_cq->comp_handler(send_cq, send_cq->cq_context);
+ if (recv_cq != send_cq && recv_cq->poll_ctx != IB_POLL_DIRECT &&
+ recv_cq->comp_handler)
+ recv_cq->comp_handler(recv_cq, recv_cq->cq_context);
}

int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata)
{
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
+ bool notify = false;
int ret;

mutex_lock(&qp->modify_lock);
@@ -1130,11 +1171,14 @@ int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
goto out_unlock;
}

- mana_ib_modify_qp_state(ibqp, attr, attr_mask, udata);
+ notify = mana_ib_modify_qp_state(ibqp, attr, attr_mask, udata);

out_unlock:
mutex_unlock(&qp->modify_lock);

+ if (notify)
+ mana_ib_notify_error_cqs(qp);
+
return ret;
}

@@ -1256,6 +1300,7 @@ static int mana_ib_destroy_uc_qp(struct mana_ib_qp *qp, struct ib_udata *udata)
return err;

mana_table_remove_qp(mdev, qp);
+ mana_remove_qp_from_cqs(qp, false);
/* Ignore return code as there is not much we can do about it.
* The error message is printed inside.
*/
--
2.43.0