| .. | .. |
|---|
| 79 | 79 | } |
|---|
| 80 | 80 | } |
|---|
| 81 | 81 | |
|---|
| 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 | + |
|---|
| 82 | 103 | static void |
|---|
| 83 | 104 | qedr_iw_mpa_request(void *context, struct qed_iwarp_cm_event_params *params) |
|---|
| 84 | 105 | { |
|---|
| .. | .. |
|---|
| 93 | 114 | |
|---|
| 94 | 115 | ep->dev = dev; |
|---|
| 95 | 116 | ep->qed_context = params->ep_context; |
|---|
| 117 | + kref_init(&ep->refcnt); |
|---|
| 96 | 118 | |
|---|
| 97 | 119 | memset(&event, 0, sizeof(event)); |
|---|
| 98 | 120 | event.event = IW_CM_EVENT_CONNECT_REQUEST; |
|---|
| .. | .. |
|---|
| 150 | 172 | { |
|---|
| 151 | 173 | struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context; |
|---|
| 152 | 174 | |
|---|
| 153 | | - if (ep->cm_id) { |
|---|
| 175 | + if (ep->cm_id) |
|---|
| 154 | 176 | qedr_iw_issue_event(context, params, IW_CM_EVENT_CLOSE); |
|---|
| 155 | 177 | |
|---|
| 156 | | - ep->cm_id->rem_ref(ep->cm_id); |
|---|
| 157 | | - ep->cm_id = NULL; |
|---|
| 158 | | - } |
|---|
| 178 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 159 | 179 | } |
|---|
| 160 | 180 | |
|---|
| 161 | 181 | static void |
|---|
| .. | .. |
|---|
| 195 | 215 | struct qedr_qp *qp = ep->qp; |
|---|
| 196 | 216 | struct iw_cm_event event; |
|---|
| 197 | 217 | |
|---|
| 198 | | - if (qp->destroyed) { |
|---|
| 199 | | - kfree(dwork); |
|---|
| 200 | | - qedr_iw_qp_rem_ref(&qp->ibqp); |
|---|
| 201 | | - return; |
|---|
| 202 | | - } |
|---|
| 218 | + /* The qp won't be released until we release the ep. |
|---|
| 219 | + * the ep's refcnt was increased before calling this |
|---|
| 220 | + * function, therefore it is safe to access qp |
|---|
| 221 | + */ |
|---|
| 222 | + if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_DISCONNECT, |
|---|
| 223 | + &qp->iwarp_cm_flags)) |
|---|
| 224 | + goto out; |
|---|
| 203 | 225 | |
|---|
| 204 | 226 | memset(&event, 0, sizeof(event)); |
|---|
| 205 | 227 | event.status = dwork->status; |
|---|
| .. | .. |
|---|
| 213 | 235 | else |
|---|
| 214 | 236 | qp_params.new_state = QED_ROCE_QP_STATE_SQD; |
|---|
| 215 | 237 | |
|---|
| 216 | | - kfree(dwork); |
|---|
| 217 | 238 | |
|---|
| 218 | 239 | if (ep->cm_id) |
|---|
| 219 | 240 | ep->cm_id->event_handler(ep->cm_id, &event); |
|---|
| .. | .. |
|---|
| 223 | 244 | |
|---|
| 224 | 245 | dev->ops->rdma_modify_qp(dev->rdma_ctx, qp->qed_qp, &qp_params); |
|---|
| 225 | 246 | |
|---|
| 226 | | - qedr_iw_qp_rem_ref(&qp->ibqp); |
|---|
| 247 | + complete(&ep->qp->iwarp_cm_comp); |
|---|
| 248 | +out: |
|---|
| 249 | + kfree(dwork); |
|---|
| 250 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 227 | 251 | } |
|---|
| 228 | 252 | |
|---|
| 229 | 253 | static void |
|---|
| .. | .. |
|---|
| 233 | 257 | struct qedr_discon_work *work; |
|---|
| 234 | 258 | struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context; |
|---|
| 235 | 259 | struct qedr_dev *dev = ep->dev; |
|---|
| 236 | | - struct qedr_qp *qp = ep->qp; |
|---|
| 237 | 260 | |
|---|
| 238 | 261 | work = kzalloc(sizeof(*work), GFP_ATOMIC); |
|---|
| 239 | 262 | if (!work) |
|---|
| 240 | 263 | return; |
|---|
| 241 | 264 | |
|---|
| 242 | | - qedr_iw_qp_add_ref(&qp->ibqp); |
|---|
| 265 | + /* We can't get a close event before disconnect, but since |
|---|
| 266 | + * we're scheduling a work queue we need to make sure close |
|---|
| 267 | + * won't delete the ep, so we increase the refcnt |
|---|
| 268 | + */ |
|---|
| 269 | + kref_get(&ep->refcnt); |
|---|
| 270 | + |
|---|
| 243 | 271 | work->ep = ep; |
|---|
| 244 | 272 | work->event = params->event; |
|---|
| 245 | 273 | work->status = params->status; |
|---|
| .. | .. |
|---|
| 261 | 289 | if ((params->status == -ECONNREFUSED) && (!ep->qp)) { |
|---|
| 262 | 290 | DP_DEBUG(dev, QEDR_MSG_IWARP, |
|---|
| 263 | 291 | "PASSIVE connection refused releasing ep...\n"); |
|---|
| 264 | | - kfree(ep); |
|---|
| 292 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 265 | 293 | return; |
|---|
| 266 | 294 | } |
|---|
| 267 | 295 | |
|---|
| 296 | + complete(&ep->qp->iwarp_cm_comp); |
|---|
| 268 | 297 | qedr_iw_issue_event(context, params, IW_CM_EVENT_ESTABLISHED); |
|---|
| 269 | 298 | |
|---|
| 270 | 299 | if (params->status < 0) |
|---|
| 271 | 300 | qedr_iw_close_event(context, params); |
|---|
| 301 | +} |
|---|
| 302 | + |
|---|
| 303 | +static void |
|---|
| 304 | +qedr_iw_active_complete(void *context, |
|---|
| 305 | + struct qed_iwarp_cm_event_params *params) |
|---|
| 306 | +{ |
|---|
| 307 | + struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context; |
|---|
| 308 | + |
|---|
| 309 | + complete(&ep->qp->iwarp_cm_comp); |
|---|
| 310 | + qedr_iw_issue_event(context, params, IW_CM_EVENT_CONNECT_REPLY); |
|---|
| 311 | + |
|---|
| 312 | + if (params->status < 0) |
|---|
| 313 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 272 | 314 | } |
|---|
| 273 | 315 | |
|---|
| 274 | 316 | static int |
|---|
| .. | .. |
|---|
| 297 | 339 | qedr_iw_mpa_reply(context, params); |
|---|
| 298 | 340 | break; |
|---|
| 299 | 341 | case QED_IWARP_EVENT_PASSIVE_COMPLETE: |
|---|
| 300 | | - ep->during_connect = 0; |
|---|
| 301 | 342 | qedr_iw_passive_complete(context, params); |
|---|
| 302 | 343 | break; |
|---|
| 303 | | - |
|---|
| 304 | 344 | case QED_IWARP_EVENT_ACTIVE_COMPLETE: |
|---|
| 305 | | - ep->during_connect = 0; |
|---|
| 306 | | - qedr_iw_issue_event(context, |
|---|
| 307 | | - params, |
|---|
| 308 | | - IW_CM_EVENT_CONNECT_REPLY); |
|---|
| 309 | | - if (params->status < 0) { |
|---|
| 310 | | - struct qedr_iw_ep *ep = (struct qedr_iw_ep *)context; |
|---|
| 311 | | - |
|---|
| 312 | | - ep->cm_id->rem_ref(ep->cm_id); |
|---|
| 313 | | - ep->cm_id = NULL; |
|---|
| 314 | | - } |
|---|
| 345 | + qedr_iw_active_complete(context, params); |
|---|
| 315 | 346 | break; |
|---|
| 316 | 347 | case QED_IWARP_EVENT_DISCONNECT: |
|---|
| 317 | 348 | qedr_iw_disconnect_event(context, params); |
|---|
| 318 | 349 | break; |
|---|
| 319 | 350 | case QED_IWARP_EVENT_CLOSE: |
|---|
| 320 | | - ep->during_connect = 0; |
|---|
| 321 | 351 | qedr_iw_close_event(context, params); |
|---|
| 322 | 352 | break; |
|---|
| 323 | 353 | case QED_IWARP_EVENT_RQ_EMPTY: |
|---|
| .. | .. |
|---|
| 358 | 388 | default: |
|---|
| 359 | 389 | DP_NOTICE(dev, "Unknown event received %d\n", params->event); |
|---|
| 360 | 390 | break; |
|---|
| 361 | | - }; |
|---|
| 391 | + } |
|---|
| 362 | 392 | return 0; |
|---|
| 363 | 393 | } |
|---|
| 364 | 394 | |
|---|
| .. | .. |
|---|
| 485 | 515 | return rc; |
|---|
| 486 | 516 | } |
|---|
| 487 | 517 | |
|---|
| 518 | +static struct qedr_qp *qedr_iw_load_qp(struct qedr_dev *dev, u32 qpn) |
|---|
| 519 | +{ |
|---|
| 520 | + struct qedr_qp *qp; |
|---|
| 521 | + |
|---|
| 522 | + xa_lock(&dev->qps); |
|---|
| 523 | + qp = xa_load(&dev->qps, qpn); |
|---|
| 524 | + if (qp) |
|---|
| 525 | + kref_get(&qp->refcnt); |
|---|
| 526 | + xa_unlock(&dev->qps); |
|---|
| 527 | + |
|---|
| 528 | + return qp; |
|---|
| 529 | +} |
|---|
| 530 | + |
|---|
| 488 | 531 | int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) |
|---|
| 489 | 532 | { |
|---|
| 490 | 533 | struct qedr_dev *dev = get_qedr_dev(cm_id->device); |
|---|
| .. | .. |
|---|
| 499 | 542 | struct qedr_qp *qp; |
|---|
| 500 | 543 | int rc = 0; |
|---|
| 501 | 544 | int i; |
|---|
| 502 | | - |
|---|
| 503 | | - qp = idr_find(&dev->qpidr.idr, conn_param->qpn); |
|---|
| 504 | | - if (unlikely(!qp)) |
|---|
| 505 | | - return -EINVAL; |
|---|
| 506 | 545 | |
|---|
| 507 | 546 | laddr = (struct sockaddr_in *)&cm_id->m_local_addr; |
|---|
| 508 | 547 | raddr = (struct sockaddr_in *)&cm_id->m_remote_addr; |
|---|
| .. | .. |
|---|
| 525 | 564 | return -ENOMEM; |
|---|
| 526 | 565 | |
|---|
| 527 | 566 | ep->dev = dev; |
|---|
| 567 | + kref_init(&ep->refcnt); |
|---|
| 568 | + |
|---|
| 569 | + qp = qedr_iw_load_qp(dev, conn_param->qpn); |
|---|
| 570 | + if (!qp) { |
|---|
| 571 | + rc = -EINVAL; |
|---|
| 572 | + goto err; |
|---|
| 573 | + } |
|---|
| 574 | + |
|---|
| 528 | 575 | ep->qp = qp; |
|---|
| 529 | | - qp->ep = ep; |
|---|
| 530 | 576 | cm_id->add_ref(cm_id); |
|---|
| 531 | 577 | ep->cm_id = cm_id; |
|---|
| 532 | 578 | |
|---|
| .. | .. |
|---|
| 589 | 635 | in_params.qp = qp->qed_qp; |
|---|
| 590 | 636 | memcpy(in_params.local_mac_addr, dev->ndev->dev_addr, ETH_ALEN); |
|---|
| 591 | 637 | |
|---|
| 592 | | - ep->during_connect = 1; |
|---|
| 638 | + if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_CONNECT, |
|---|
| 639 | + &qp->iwarp_cm_flags)) { |
|---|
| 640 | + rc = -ENODEV; |
|---|
| 641 | + goto err; /* QP already being destroyed */ |
|---|
| 642 | + } |
|---|
| 643 | + |
|---|
| 593 | 644 | rc = dev->ops->iwarp_connect(dev->rdma_ctx, &in_params, &out_params); |
|---|
| 594 | | - if (rc) |
|---|
| 645 | + if (rc) { |
|---|
| 646 | + complete(&qp->iwarp_cm_comp); |
|---|
| 595 | 647 | goto err; |
|---|
| 648 | + } |
|---|
| 596 | 649 | |
|---|
| 597 | 650 | return rc; |
|---|
| 598 | 651 | |
|---|
| 599 | 652 | err: |
|---|
| 600 | | - cm_id->rem_ref(cm_id); |
|---|
| 601 | | - kfree(ep); |
|---|
| 653 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 602 | 654 | return rc; |
|---|
| 603 | 655 | } |
|---|
| 604 | 656 | |
|---|
| .. | .. |
|---|
| 691 | 743 | |
|---|
| 692 | 744 | DP_DEBUG(dev, QEDR_MSG_IWARP, "Accept on qpid=%d\n", conn_param->qpn); |
|---|
| 693 | 745 | |
|---|
| 694 | | - qp = idr_find(&dev->qpidr.idr, conn_param->qpn); |
|---|
| 746 | + qp = qedr_iw_load_qp(dev, conn_param->qpn); |
|---|
| 695 | 747 | if (!qp) { |
|---|
| 696 | 748 | DP_ERR(dev, "Invalid QP number %d\n", conn_param->qpn); |
|---|
| 697 | 749 | return -EINVAL; |
|---|
| 698 | 750 | } |
|---|
| 699 | 751 | |
|---|
| 700 | 752 | ep->qp = qp; |
|---|
| 701 | | - qp->ep = ep; |
|---|
| 702 | 753 | cm_id->add_ref(cm_id); |
|---|
| 703 | 754 | ep->cm_id = cm_id; |
|---|
| 704 | 755 | |
|---|
| .. | .. |
|---|
| 710 | 761 | params.ird = conn_param->ird; |
|---|
| 711 | 762 | params.ord = conn_param->ord; |
|---|
| 712 | 763 | |
|---|
| 713 | | - ep->during_connect = 1; |
|---|
| 764 | + if (test_and_set_bit(QEDR_IWARP_CM_WAIT_FOR_CONNECT, |
|---|
| 765 | + &qp->iwarp_cm_flags)) { |
|---|
| 766 | + rc = -EINVAL; |
|---|
| 767 | + goto err; /* QP already destroyed */ |
|---|
| 768 | + } |
|---|
| 769 | + |
|---|
| 714 | 770 | rc = dev->ops->iwarp_accept(dev->rdma_ctx, ¶ms); |
|---|
| 715 | | - if (rc) |
|---|
| 771 | + if (rc) { |
|---|
| 772 | + complete(&qp->iwarp_cm_comp); |
|---|
| 716 | 773 | goto err; |
|---|
| 774 | + } |
|---|
| 717 | 775 | |
|---|
| 718 | 776 | return rc; |
|---|
| 777 | + |
|---|
| 719 | 778 | err: |
|---|
| 720 | | - ep->during_connect = 0; |
|---|
| 721 | | - cm_id->rem_ref(cm_id); |
|---|
| 779 | + kref_put(&ep->refcnt, qedr_iw_free_ep); |
|---|
| 780 | + |
|---|
| 722 | 781 | return rc; |
|---|
| 723 | 782 | } |
|---|
| 724 | 783 | |
|---|
| .. | .. |
|---|
| 741 | 800 | { |
|---|
| 742 | 801 | struct qedr_qp *qp = get_qedr_qp(ibqp); |
|---|
| 743 | 802 | |
|---|
| 744 | | - atomic_inc(&qp->refcnt); |
|---|
| 803 | + kref_get(&qp->refcnt); |
|---|
| 745 | 804 | } |
|---|
| 746 | 805 | |
|---|
| 747 | 806 | void qedr_iw_qp_rem_ref(struct ib_qp *ibqp) |
|---|
| 748 | 807 | { |
|---|
| 749 | 808 | struct qedr_qp *qp = get_qedr_qp(ibqp); |
|---|
| 750 | 809 | |
|---|
| 751 | | - if (atomic_dec_and_test(&qp->refcnt)) { |
|---|
| 752 | | - spin_lock_irq(&qp->dev->qpidr.idr_lock); |
|---|
| 753 | | - idr_remove(&qp->dev->qpidr.idr, qp->qp_id); |
|---|
| 754 | | - spin_unlock_irq(&qp->dev->qpidr.idr_lock); |
|---|
| 755 | | - kfree(qp); |
|---|
| 756 | | - } |
|---|
| 810 | + kref_put(&qp->refcnt, qedr_iw_free_qp); |
|---|
| 757 | 811 | } |
|---|
| 758 | 812 | |
|---|
| 759 | 813 | struct ib_qp *qedr_iw_get_qp(struct ib_device *ibdev, int qpn) |
|---|
| 760 | 814 | { |
|---|
| 761 | 815 | struct qedr_dev *dev = get_qedr_dev(ibdev); |
|---|
| 762 | 816 | |
|---|
| 763 | | - return idr_find(&dev->qpidr.idr, qpn); |
|---|
| 817 | + return xa_load(&dev->qps, qpn); |
|---|
| 764 | 818 | } |
|---|