Skip to content

Commit 82af6d1

Browse files
Michal Kalderonjgunthorpe
authored andcommitted
RDMA/qedr: Fix synchronization methods and memory leaks in qedr
Re-design of the iWARP CM related objects reference counting and synchronization methods, to ensure operations are synchronized correctly and that memory allocated for "ep" is properly released. Also makes sure QP memory is not released before ep is finished accessing it. Where as the QP object is created/destroyed by external operations, the ep is created/destroyed by internal operations and represents the tcp connection associated with the QP. QP destruction flow: - needs to wait for ep establishment to complete (either successfully or with error) - needs to wait for ep disconnect to be fully posted to avoid a race condition of disconnect being called after reset. - both the operations above don't always happen, so we use atomic flags to indicate whether the qp destruction flow needs to wait for these completions or not, if the destroy is called before these operations began, the flows will check the flags and not execute them ( connect / disconnect). We use completion structure for waiting for the completions mentioned above. The QP refcnt was modified to kref object. The EP has a kref added to it to handle additional worker thread accessing it. Memory Leaks - https://www.spinics.net/lists/linux-rdma/msg83762.html Concurrency not managed correctly - https://www.spinics.net/lists/linux-rdma/msg67949.html Fixes: de0089e ("RDMA/qedr: Add iWARP connection management qp related callbacks") Link: https://lore.kernel.org/r/[email protected] Reported-by: Chuck Lever <[email protected]> Reported-by: Jason Gunthorpe <[email protected]> Signed-off-by: Ariel Elior <[email protected]> Signed-off-by: Michal Kalderon <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 5fdff18 commit 82af6d1

File tree

3 files changed

+158
-75
lines changed

3 files changed

+158
-75
lines changed

drivers/infiniband/hw/qedr/qedr.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <linux/qed/qed_rdma_if.h>
4141
#include <linux/qed/qede_rdma.h>
4242
#include <linux/qed/roce_common.h>
43+
#include <linux/completion.h>
4344
#include "qedr_hsi_rdma.h"
4445

4546
#define QEDR_NODE_DESC "QLogic 579xx RoCE HCA"
@@ -377,10 +378,20 @@ enum qedr_qp_err_bitmap {
377378
QEDR_QP_ERR_RQ_PBL_FULL = 32,
378379
};
379380

381+
enum qedr_qp_create_type {
382+
QEDR_QP_CREATE_NONE,
383+
QEDR_QP_CREATE_USER,
384+
QEDR_QP_CREATE_KERNEL,
385+
};
386+
387+
enum qedr_iwarp_cm_flags {
388+
QEDR_IWARP_CM_WAIT_FOR_CONNECT = BIT(0),
389+
QEDR_IWARP_CM_WAIT_FOR_DISCONNECT = BIT(1),
390+
};
391+
380392
struct qedr_qp {
381393
struct ib_qp ibqp; /* must be first */
382394
struct qedr_dev *dev;
383-
struct qedr_iw_ep *ep;
384395
struct qedr_qp_hwq_info sq;
385396
struct qedr_qp_hwq_info rq;
386397

@@ -395,6 +406,7 @@ struct qedr_qp {
395406
u32 id;
396407
struct qedr_pd *pd;
397408
enum ib_qp_type qp_type;
409+
enum qedr_qp_create_type create_type;
398410
struct qed_rdma_qp *qed_qp;
399411
u32 qp_id;
400412
u16 icid;
@@ -437,8 +449,11 @@ struct qedr_qp {
437449
/* Relevant to qps created from user space only (applications) */
438450
struct qedr_userq usq;
439451
struct qedr_userq urq;
440-
atomic_t refcnt;
441-
bool destroyed;
452+
453+
/* synchronization objects used with iwarp ep */
454+
struct kref refcnt;
455+
struct completion iwarp_cm_comp;
456+
unsigned long iwarp_cm_flags; /* enum iwarp_cm_flags */
442457
};
443458

444459
struct qedr_ah {
@@ -531,7 +546,7 @@ struct qedr_iw_ep {
531546
struct iw_cm_id *cm_id;
532547
struct qedr_qp *qp;
533548
void *qed_context;
534-
u8 during_connect;
549+
struct kref refcnt;
535550
};
536551

537552
static inline

drivers/infiniband/hw/qedr/qedr_iw_cm.c

Lines changed: 100 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,27 @@ qedr_fill_sockaddr6(const struct qed_iwarp_cm_info *cm_info,
7979
}
8080
}
8181

82+
static void qedr_iw_free_qp(struct kref *ref)
83+
{
84+
struct qedr_qp *qp = container_of(ref, struct qedr_qp, refcnt);
85+
86+
kfree(qp);
87+
}
88+
89+
static void
90+
qedr_iw_free_ep(struct kref *ref)
91+
{
92+
struct qedr_iw_ep *ep = container_of(ref, struct qedr_iw_ep, refcnt);
93+
94+
if (ep->qp)
95+
kref_put(&ep->qp->refcnt, qedr_iw_free_qp);
96+
97+
if (ep->cm_id)
98+
ep->cm_id->rem_ref(ep->cm_id);
99+
100+
kfree(ep);
101+
}
102+
82103
static void
83104
qedr_iw_mpa_request(void *context, struct qed_iwarp_cm_event_params *params)
84105
{
@@ -93,6 +114,7 @@ qedr_iw_mpa_request(void *context, struct qed_iwarp_cm_event_params *params)
93114

94115
ep->dev = dev;
95116
ep->qed_context = params->ep_context;
117+
kref_init(&ep->refcnt);
96118

97119
memset(&event, 0, sizeof(event));
98120
event.event = IW_CM_EVENT_CONNECT_REQUEST;
@@ -141,12 +163,10 @@ qedr_iw_close_event(void *context, struct qed_iwarp_cm_event_params *params)
141163
{
142164
struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context;
143165

144-
if (ep->cm_id) {
166+
if (ep->cm_id)
145167
qedr_iw_issue_event(context, params, IW_CM_EVENT_CLOSE);
146168

147-
ep->cm_id->rem_ref(ep->cm_id);
148-
ep->cm_id = NULL;
149-
}
169+
kref_put(&ep->refcnt, qedr_iw_free_ep);
150170
}
151171

152172
static void
@@ -186,11 +206,13 @@ static void qedr_iw_disconnect_worker(struct work_struct *work)
186206
struct qedr_qp *qp = ep->qp;
187207
struct iw_cm_event event;
188208

189-
if (qp->destroyed) {
190-
kfree(dwork);
191-
qedr_iw_qp_rem_ref(&qp->ibqp);
192-
return;
193-
}
209+
/* The qp won't be released until we release the ep.
210+
* the ep's refcnt was increased before calling this
211+
* function, therefore it is safe to access qp
212+
*/
213+
if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_DISCONNECT,
214+
&qp->iwarp_cm_flags))
215+
goto out;
194216

195217
memset(&event, 0, sizeof(event));
196218
event.status = dwork->status;
@@ -204,7 +226,6 @@ static void qedr_iw_disconnect_worker(struct work_struct *work)
204226
else
205227
qp_params.new_state = QED_ROCE_QP_STATE_SQD;
206228

207-
kfree(dwork);
208229

209230
if (ep->cm_id)
210231
ep->cm_id->event_handler(ep->cm_id, &event);
@@ -214,7 +235,10 @@ static void qedr_iw_disconnect_worker(struct work_struct *work)
214235

215236
dev->ops->rdma_modify_qp(dev->rdma_ctx, qp->qed_qp, &qp_params);
216237

217-
qedr_iw_qp_rem_ref(&qp->ibqp);
238+
complete(&ep->qp->iwarp_cm_comp);
239+
out:
240+
kfree(dwork);
241+
kref_put(&ep->refcnt, qedr_iw_free_ep);
218242
}
219243

220244
static void
@@ -224,13 +248,17 @@ qedr_iw_disconnect_event(void *context,
224248
struct qedr_discon_work *work;
225249
struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context;
226250
struct qedr_dev *dev = ep->dev;
227-
struct qedr_qp *qp = ep->qp;
228251

229252
work = kzalloc(sizeof(*work), GFP_ATOMIC);
230253
if (!work)
231254
return;
232255

233-
qedr_iw_qp_add_ref(&qp->ibqp);
256+
/* We can't get a close event before disconnect, but since
257+
* we're scheduling a work queue we need to make sure close
258+
* won't delete the ep, so we increase the refcnt
259+
*/
260+
kref_get(&ep->refcnt);
261+
234262
work->ep = ep;
235263
work->event = params->event;
236264
work->status = params->status;
@@ -252,16 +280,30 @@ qedr_iw_passive_complete(void *context,
252280
if ((params->status == -ECONNREFUSED) && (!ep->qp)) {
253281
DP_DEBUG(dev, QEDR_MSG_IWARP,
254282
"PASSIVE connection refused releasing ep...\n");
255-
kfree(ep);
283+
kref_put(&ep->refcnt, qedr_iw_free_ep);
256284
return;
257285
}
258286

287+
complete(&ep->qp->iwarp_cm_comp);
259288
qedr_iw_issue_event(context, params, IW_CM_EVENT_ESTABLISHED);
260289

261290
if (params->status < 0)
262291
qedr_iw_close_event(context, params);
263292
}
264293

294+
static void
295+
qedr_iw_active_complete(void *context,
296+
struct qed_iwarp_cm_event_params *params)
297+
{
298+
struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context;
299+
300+
complete(&ep->qp->iwarp_cm_comp);
301+
qedr_iw_issue_event(context, params, IW_CM_EVENT_CONNECT_REPLY);
302+
303+
if (params->status < 0)
304+
kref_put(&ep->refcnt, qedr_iw_free_ep);
305+
}
306+
265307
static int
266308
qedr_iw_mpa_reply(void *context, struct qed_iwarp_cm_event_params *params)
267309
{
@@ -288,27 +330,15 @@ qedr_iw_event_handler(void *context, struct qed_iwarp_cm_event_params *params)
288330
qedr_iw_mpa_reply(context, params);
289331
break;
290332
case QED_IWARP_EVENT_PASSIVE_COMPLETE:
291-
ep->during_connect = 0;
292333
qedr_iw_passive_complete(context, params);
293334
break;
294-
295335
case QED_IWARP_EVENT_ACTIVE_COMPLETE:
296-
ep->during_connect = 0;
297-
qedr_iw_issue_event(context,
298-
params,
299-
IW_CM_EVENT_CONNECT_REPLY);
300-
if (params->status < 0) {
301-
struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context;
302-
303-
ep->cm_id->rem_ref(ep->cm_id);
304-
ep->cm_id = NULL;
305-
}
336+
qedr_iw_active_complete(context, params);
306337
break;
307338
case QED_IWARP_EVENT_DISCONNECT:
308339
qedr_iw_disconnect_event(context, params);
309340
break;
310341
case QED_IWARP_EVENT_CLOSE:
311-
ep->during_connect = 0;
312342
qedr_iw_close_event(context, params);
313343
break;
314344
case QED_IWARP_EVENT_RQ_EMPTY:
@@ -476,6 +506,19 @@ qedr_addr6_resolve(struct qedr_dev *dev,
476506
return rc;
477507
}
478508

509+
struct qedr_qp *qedr_iw_load_qp(struct qedr_dev *dev, u32 qpn)
510+
{
511+
struct qedr_qp *qp;
512+
513+
xa_lock(&dev->qps);
514+
qp = xa_load(&dev->qps, qpn);
515+
if (qp)
516+
kref_get(&qp->refcnt);
517+
xa_unlock(&dev->qps);
518+
519+
return qp;
520+
}
521+
479522
int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
480523
{
481524
struct qedr_dev *dev = get_qedr_dev(cm_id->device);
@@ -491,10 +534,6 @@ int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
491534
int rc = 0;
492535
int i;
493536

494-
qp = xa_load(&dev->qps, conn_param->qpn);
495-
if (unlikely(!qp))
496-
return -EINVAL;
497-
498537
laddr = (struct sockaddr_in *)&cm_id->m_local_addr;
499538
raddr = (struct sockaddr_in *)&cm_id->m_remote_addr;
500539
laddr6 = (struct sockaddr_in6 *)&cm_id->m_local_addr;
@@ -516,8 +555,15 @@ int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
516555
return -ENOMEM;
517556

518557
ep->dev = dev;
558+
kref_init(&ep->refcnt);
559+
560+
qp = qedr_iw_load_qp(dev, conn_param->qpn);
561+
if (!qp) {
562+
rc = -EINVAL;
563+
goto err;
564+
}
565+
519566
ep->qp = qp;
520-
qp->ep = ep;
521567
cm_id->add_ref(cm_id);
522568
ep->cm_id = cm_id;
523569

@@ -580,16 +626,20 @@ int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
580626
in_params.qp = qp->qed_qp;
581627
memcpy(in_params.local_mac_addr, dev->ndev->dev_addr, ETH_ALEN);
582628

583-
ep->during_connect = 1;
629+
if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_CONNECT,
630+
&qp->iwarp_cm_flags))
631+
goto err; /* QP already being destroyed */
632+
584633
rc = dev->ops->iwarp_connect(dev->rdma_ctx, &in_params, &out_params);
585-
if (rc)
634+
if (rc) {
635+
complete(&qp->iwarp_cm_comp);
586636
goto err;
637+
}
587638

588639
return rc;
589640

590641
err:
591-
cm_id->rem_ref(cm_id);
592-
kfree(ep);
642+
kref_put(&ep->refcnt, qedr_iw_free_ep);
593643
return rc;
594644
}
595645

@@ -677,18 +727,17 @@ int qedr_iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
677727
struct qedr_dev *dev = ep->dev;
678728
struct qedr_qp *qp;
679729
struct qed_iwarp_accept_in params;
680-
int rc;
730+
int rc = 0;
681731

682732
DP_DEBUG(dev, QEDR_MSG_IWARP, "Accept on qpid=%d\n", conn_param->qpn);
683733

684-
qp = xa_load(&dev->qps, conn_param->qpn);
734+
qp = qedr_iw_load_qp(dev, conn_param->qpn);
685735
if (!qp) {
686736
DP_ERR(dev, "Invalid QP number %d\n", conn_param->qpn);
687737
return -EINVAL;
688738
}
689739

690740
ep->qp = qp;
691-
qp->ep = ep;
692741
cm_id->add_ref(cm_id);
693742
ep->cm_id = cm_id;
694743

@@ -700,15 +749,21 @@ int qedr_iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
700749
params.ird = conn_param->ird;
701750
params.ord = conn_param->ord;
702751

703-
ep->during_connect = 1;
752+
if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_CONNECT,
753+
&qp->iwarp_cm_flags))
754+
goto err; /* QP already destroyed */
755+
704756
rc = dev->ops->iwarp_accept(dev->rdma_ctx, &params);
705-
if (rc)
757+
if (rc) {
758+
complete(&qp->iwarp_cm_comp);
706759
goto err;
760+
}
707761

708762
return rc;
763+
709764
err:
710-
ep->during_connect = 0;
711-
cm_id->rem_ref(cm_id);
765+
kref_put(&ep->refcnt, qedr_iw_free_ep);
766+
712767
return rc;
713768
}
714769

@@ -731,17 +786,14 @@ void qedr_iw_qp_add_ref(struct ib_qp *ibqp)
731786
{
732787
struct qedr_qp *qp = get_qedr_qp(ibqp);
733788

734-
atomic_inc(&qp->refcnt);
789+
kref_get(&qp->refcnt);
735790
}
736791

737792
void qedr_iw_qp_rem_ref(struct ib_qp *ibqp)
738793
{
739794
struct qedr_qp *qp = get_qedr_qp(ibqp);
740795

741-
if (atomic_dec_and_test(&qp->refcnt)) {
742-
xa_erase(&qp->dev->qps, qp->qp_id);
743-
kfree(qp);
744-
}
796+
kref_put(&qp->refcnt, qedr_iw_free_qp);
745797
}
746798

747799
struct ib_qp *qedr_iw_get_qp(struct ib_device *ibdev, int qpn)

0 commit comments

Comments
 (0)