git: e29dccc64784 - main - iw_cxgbe: Fix qpid leak

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Tue, 15 Sep 2026 14:47:57 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=e29dccc64784bb572894c8b714ca212e38d8f8aa

commit e29dccc64784bb572894c8b714ca212e38d8f8aa
Author:     Nirranjan Kirubaharan <nirranjan@chelsio.com>
AuthorDate: 2019-05-23 07:05:39 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2026-09-15 14:22:40 +0000

    iw_cxgbe: Fix qpid leak
    
    Add await in destroy_qp() so that all references to qp are dereferenced
    and qp is freed in destroy_qp() itself.  This ensures freeing of all QPs
    before invocation of dealloc_ucontext(), which prevents loss of in use
    qpids stored in the ucontext.
    
    Obtained from:  Linux commit f70baa7ee3d1b5a9e66ac7549e31641a656f23c1
    Sponsored by:   Chelsio Communications
---
 sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h |  4 ++--
 sys/dev/cxgbe/iw_cxgbe/qp.c       | 50 ++++++++++++++-------------------------
 2 files changed, 20 insertions(+), 34 deletions(-)

diff --git a/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h b/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h
index 2112c4178591..ebe1b57f8660 100644
--- a/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h
+++ b/sys/dev/cxgbe/iw_cxgbe/iw_cxgbe.h
@@ -461,13 +461,13 @@ struct c4iw_qp {
 	struct t4_wq wq;
 	spinlock_t lock;
 	struct mutex mutex;
-	struct kref kref;
 	wait_queue_head_t wait;
 	struct timer_list timer;
 	int sq_sig_all;
-	struct work_struct free_work;
 	struct c4iw_ucontext *ucontext;
 	struct c4iw_wr_wait *wr_waitp;
+	struct completion qp_rel_comp;
+	refcount_t qp_refcnt;
 };
 
 static inline struct c4iw_qp *to_c4iw_qp(struct ib_qp *ibqp)
diff --git a/sys/dev/cxgbe/iw_cxgbe/qp.c b/sys/dev/cxgbe/iw_cxgbe/qp.c
index 931a5be7c54e..9a5fc64c624b 100644
--- a/sys/dev/cxgbe/iw_cxgbe/qp.c
+++ b/sys/dev/cxgbe/iw_cxgbe/qp.c
@@ -632,44 +632,17 @@ static int build_inv_stag(union t4_wr *wqe, const struct ib_send_wr *wr,
 	return 0;
 }
 
-static void free_qp_work(struct work_struct *work)
-{
-	struct c4iw_ucontext *ucontext;
-	struct c4iw_qp *qhp;
-	struct c4iw_dev *rhp;
-
-	qhp = container_of(work, struct c4iw_qp, free_work);
-	ucontext = qhp->ucontext;
-	rhp = qhp->rhp;
-
-	CTR3(KTR_IW_CXGBE, "%s qhp %p ucontext %p", __func__,
-			qhp, ucontext);
-	destroy_qp(&rhp->rdev, &qhp->wq,
-		   ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
-
-	c4iw_put_wr_wait(qhp->wr_waitp);
-	kfree(qhp);
-}
-
-static void queue_qp_free(struct kref *kref)
-{
-	struct c4iw_qp *qhp;
-
-	qhp = container_of(kref, struct c4iw_qp, kref);
-	CTR2(KTR_IW_CXGBE, "%s qhp %p", __func__, qhp);
-	queue_work(qhp->rhp->rdev.free_workq, &qhp->free_work);
-}
-
 void c4iw_qp_add_ref(struct ib_qp *qp)
 {
 	CTR2(KTR_IW_CXGBE, "%s ib_qp %p", __func__, qp);
-	kref_get(&to_c4iw_qp(qp)->kref);
+	refcount_inc(&to_c4iw_qp(qp)->qp_refcnt);
 }
 
 void c4iw_qp_rem_ref(struct ib_qp *qp)
 {
 	CTR2(KTR_IW_CXGBE, "%s ib_qp %p", __func__, qp);
-	kref_put(&to_c4iw_qp(qp)->kref, queue_qp_free);
+	if (refcount_dec_and_test(&to_c4iw_qp(qp)->qp_refcnt))
+		complete(&to_c4iw_qp(qp)->qp_rel_comp);
 }
 
 static int ib_to_fw_opcode(int ib_opcode)
@@ -1916,12 +1889,14 @@ out:
 
 int c4iw_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata)
 {
+	struct c4iw_ucontext *ucontext;
 	struct c4iw_dev *rhp;
 	struct c4iw_qp *qhp;
 	struct c4iw_qp_attributes attrs;
 
 	CTR2(KTR_IW_CXGBE, "%s ib_qp %p", __func__, ib_qp);
 	qhp = to_c4iw_qp(ib_qp);
+	ucontext = qhp->ucontext;
 	rhp = qhp->rhp;
 
 	attrs.next_state = C4IW_QP_STATE_ERROR;
@@ -1938,8 +1913,19 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata)
 	free_ird(rhp, qhp->attr.max_ird);
 	c4iw_qp_rem_ref(ib_qp);
 
+	wait_for_completion(&qhp->qp_rel_comp);
+
 	CTR3(KTR_IW_CXGBE, "%s ib_qp %p qpid 0x%0x", __func__, ib_qp,
 	    qhp->wq.sq.qid);
+	CTR3(KTR_IW_CXGBE, "%s qhp %p ucontext %p", __func__,
+			qhp, ucontext);
+	destroy_qp(&rhp->rdev, &qhp->wq,
+		   ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
+
+	c4iw_put_wr_wait(qhp->wr_waitp);
+
+	kfree(qhp);
+
 	return 0;
 }
 
@@ -2044,8 +2030,8 @@ c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
 	spin_lock_init(&qhp->lock);
 	mutex_init(&qhp->mutex);
 	init_waitqueue_head(&qhp->wait);
-	kref_init(&qhp->kref);
-	INIT_WORK(&qhp->free_work, free_qp_work);
+	init_completion(&qhp->qp_rel_comp);
+	refcount_set(&qhp->qp_refcnt, 1);
 
 	ret = xa_insert_irq(&rhp->qps, qhp->wq.sq.qid, qhp, GFP_KERNEL);
 	if (ret)