RDMA/cxgb3: Don't free endpoints early
- Keep ref on connection request endpoints until either accepted or rejected so it doesn't get freed early. - Endpoint flags now need to be set via atomic bitops because they can be set on both the iw_cxgb3 workqueue thread and user disconnect threads. - Don't move out of CLOSING too early due to multiple calls to iwch_ep_disconnect. Signed-off-by: Steve Wise <swise@opengridcomputing.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
fa0d4c11c4
commit
6e47fe4350
@ -286,7 +286,7 @@ void __free_ep(struct kref *kref)
|
|||||||
ep = container_of(container_of(kref, struct iwch_ep_common, kref),
|
ep = container_of(container_of(kref, struct iwch_ep_common, kref),
|
||||||
struct iwch_ep, com);
|
struct iwch_ep, com);
|
||||||
PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]);
|
PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]);
|
||||||
if (ep->com.flags & RELEASE_RESOURCES) {
|
if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) {
|
||||||
cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid);
|
cxgb3_remove_tid(ep->com.tdev, (void *)ep, ep->hwtid);
|
||||||
dst_release(ep->dst);
|
dst_release(ep->dst);
|
||||||
l2t_release(L2DATA(ep->com.tdev), ep->l2t);
|
l2t_release(L2DATA(ep->com.tdev), ep->l2t);
|
||||||
@ -297,7 +297,7 @@ void __free_ep(struct kref *kref)
|
|||||||
static void release_ep_resources(struct iwch_ep *ep)
|
static void release_ep_resources(struct iwch_ep *ep)
|
||||||
{
|
{
|
||||||
PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid);
|
PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid);
|
||||||
ep->com.flags |= RELEASE_RESOURCES;
|
set_bit(RELEASE_RESOURCES, &ep->com.flags);
|
||||||
put_ep(&ep->com);
|
put_ep(&ep->com);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,10 +786,12 @@ static void connect_request_upcall(struct iwch_ep *ep)
|
|||||||
event.private_data_len = ep->plen;
|
event.private_data_len = ep->plen;
|
||||||
event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
|
event.private_data = ep->mpa_pkt + sizeof(struct mpa_message);
|
||||||
event.provider_data = ep;
|
event.provider_data = ep;
|
||||||
if (state_read(&ep->parent_ep->com) != DEAD)
|
if (state_read(&ep->parent_ep->com) != DEAD) {
|
||||||
|
get_ep(&ep->com);
|
||||||
ep->parent_ep->com.cm_id->event_handler(
|
ep->parent_ep->com.cm_id->event_handler(
|
||||||
ep->parent_ep->com.cm_id,
|
ep->parent_ep->com.cm_id,
|
||||||
&event);
|
&event);
|
||||||
|
}
|
||||||
put_ep(&ep->parent_ep->com);
|
put_ep(&ep->parent_ep->com);
|
||||||
ep->parent_ep = NULL;
|
ep->parent_ep = NULL;
|
||||||
}
|
}
|
||||||
@ -1156,8 +1158,7 @@ static int abort_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||||||
* We get 2 abort replies from the HW. The first one must
|
* We get 2 abort replies from the HW. The first one must
|
||||||
* be ignored except for scribbling that we need one more.
|
* be ignored except for scribbling that we need one more.
|
||||||
*/
|
*/
|
||||||
if (!(ep->com.flags & ABORT_REQ_IN_PROGRESS)) {
|
if (!test_and_set_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags)) {
|
||||||
ep->com.flags |= ABORT_REQ_IN_PROGRESS;
|
|
||||||
return CPL_RET_BUF_DONE;
|
return CPL_RET_BUF_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,7 +1481,6 @@ static int peer_close(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||||||
* rejects the CR.
|
* rejects the CR.
|
||||||
*/
|
*/
|
||||||
__state_set(&ep->com, CLOSING);
|
__state_set(&ep->com, CLOSING);
|
||||||
get_ep(&ep->com);
|
|
||||||
break;
|
break;
|
||||||
case MPA_REP_SENT:
|
case MPA_REP_SENT:
|
||||||
__state_set(&ep->com, CLOSING);
|
__state_set(&ep->com, CLOSING);
|
||||||
@ -1561,8 +1561,7 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||||||
* We get 2 peer aborts from the HW. The first one must
|
* We get 2 peer aborts from the HW. The first one must
|
||||||
* be ignored except for scribbling that we need one more.
|
* be ignored except for scribbling that we need one more.
|
||||||
*/
|
*/
|
||||||
if (!(ep->com.flags & PEER_ABORT_IN_PROGRESS)) {
|
if (!test_and_set_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags)) {
|
||||||
ep->com.flags |= PEER_ABORT_IN_PROGRESS;
|
|
||||||
return CPL_RET_BUF_DONE;
|
return CPL_RET_BUF_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1591,7 +1590,6 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||||||
* the reference on it until the ULP accepts or
|
* the reference on it until the ULP accepts or
|
||||||
* rejects the CR.
|
* rejects the CR.
|
||||||
*/
|
*/
|
||||||
get_ep(&ep->com);
|
|
||||||
break;
|
break;
|
||||||
case MORIBUND:
|
case MORIBUND:
|
||||||
case CLOSING:
|
case CLOSING:
|
||||||
@ -1797,6 +1795,7 @@ int iwch_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
|
|||||||
err = send_mpa_reject(ep, pdata, pdata_len);
|
err = send_mpa_reject(ep, pdata, pdata_len);
|
||||||
err = iwch_ep_disconnect(ep, 0, GFP_KERNEL);
|
err = iwch_ep_disconnect(ep, 0, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
put_ep(&ep->com);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1810,8 +1809,10 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
struct iwch_qp *qp = get_qhp(h, conn_param->qpn);
|
struct iwch_qp *qp = get_qhp(h, conn_param->qpn);
|
||||||
|
|
||||||
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
|
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
|
||||||
if (state_read(&ep->com) == DEAD)
|
if (state_read(&ep->com) == DEAD) {
|
||||||
return -ECONNRESET;
|
err = -ECONNRESET;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD);
|
BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD);
|
||||||
BUG_ON(!qp);
|
BUG_ON(!qp);
|
||||||
@ -1819,7 +1820,8 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
if ((conn_param->ord > qp->rhp->attr.max_rdma_read_qp_depth) ||
|
if ((conn_param->ord > qp->rhp->attr.max_rdma_read_qp_depth) ||
|
||||||
(conn_param->ird > qp->rhp->attr.max_rdma_reads_per_qp)) {
|
(conn_param->ird > qp->rhp->attr.max_rdma_reads_per_qp)) {
|
||||||
abort_connection(ep, NULL, GFP_KERNEL);
|
abort_connection(ep, NULL, GFP_KERNEL);
|
||||||
return -EINVAL;
|
err = -EINVAL;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
cm_id->add_ref(cm_id);
|
cm_id->add_ref(cm_id);
|
||||||
@ -1836,8 +1838,6 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
|
|
||||||
PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord);
|
PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord);
|
||||||
|
|
||||||
get_ep(&ep->com);
|
|
||||||
|
|
||||||
/* bind QP to EP and move to RTS */
|
/* bind QP to EP and move to RTS */
|
||||||
attrs.mpa_attr = ep->mpa_attr;
|
attrs.mpa_attr = ep->mpa_attr;
|
||||||
attrs.max_ird = ep->ird;
|
attrs.max_ird = ep->ird;
|
||||||
@ -1855,30 +1855,31 @@ int iwch_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
err = iwch_modify_qp(ep->com.qp->rhp,
|
err = iwch_modify_qp(ep->com.qp->rhp,
|
||||||
ep->com.qp, mask, &attrs, 1);
|
ep->com.qp, mask, &attrs, 1);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err1;
|
||||||
|
|
||||||
/* if needed, wait for wr_ack */
|
/* if needed, wait for wr_ack */
|
||||||
if (iwch_rqes_posted(qp)) {
|
if (iwch_rqes_posted(qp)) {
|
||||||
wait_event(ep->com.waitq, ep->com.rpl_done);
|
wait_event(ep->com.waitq, ep->com.rpl_done);
|
||||||
err = ep->com.rpl_err;
|
err = ep->com.rpl_err;
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = send_mpa_reply(ep, conn_param->private_data,
|
err = send_mpa_reply(ep, conn_param->private_data,
|
||||||
conn_param->private_data_len);
|
conn_param->private_data_len);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err1;
|
||||||
|
|
||||||
|
|
||||||
state_set(&ep->com, FPDU_MODE);
|
state_set(&ep->com, FPDU_MODE);
|
||||||
established_upcall(ep);
|
established_upcall(ep);
|
||||||
put_ep(&ep->com);
|
put_ep(&ep->com);
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err1:
|
||||||
ep->com.cm_id = NULL;
|
ep->com.cm_id = NULL;
|
||||||
ep->com.qp = NULL;
|
ep->com.qp = NULL;
|
||||||
cm_id->rem_ref(cm_id);
|
cm_id->rem_ref(cm_id);
|
||||||
|
err:
|
||||||
put_ep(&ep->com);
|
put_ep(&ep->com);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -2097,14 +2098,17 @@ int iwch_ep_disconnect(struct iwch_ep *ep, int abrupt, gfp_t gfp)
|
|||||||
ep->com.state = CLOSING;
|
ep->com.state = CLOSING;
|
||||||
start_ep_timer(ep);
|
start_ep_timer(ep);
|
||||||
}
|
}
|
||||||
|
set_bit(CLOSE_SENT, &ep->com.flags);
|
||||||
break;
|
break;
|
||||||
case CLOSING:
|
case CLOSING:
|
||||||
close = 1;
|
if (!test_and_set_bit(CLOSE_SENT, &ep->com.flags)) {
|
||||||
if (abrupt) {
|
close = 1;
|
||||||
stop_ep_timer(ep);
|
if (abrupt) {
|
||||||
ep->com.state = ABORTING;
|
stop_ep_timer(ep);
|
||||||
} else
|
ep->com.state = ABORTING;
|
||||||
ep->com.state = MORIBUND;
|
} else
|
||||||
|
ep->com.state = MORIBUND;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MORIBUND:
|
case MORIBUND:
|
||||||
case ABORTING:
|
case ABORTING:
|
||||||
|
@ -145,9 +145,10 @@ enum iwch_ep_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum iwch_ep_flags {
|
enum iwch_ep_flags {
|
||||||
PEER_ABORT_IN_PROGRESS = (1 << 0),
|
PEER_ABORT_IN_PROGRESS = 0,
|
||||||
ABORT_REQ_IN_PROGRESS = (1 << 1),
|
ABORT_REQ_IN_PROGRESS = 1,
|
||||||
RELEASE_RESOURCES = (1 << 2),
|
RELEASE_RESOURCES = 2,
|
||||||
|
CLOSE_SENT = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct iwch_ep_common {
|
struct iwch_ep_common {
|
||||||
@ -162,7 +163,7 @@ struct iwch_ep_common {
|
|||||||
wait_queue_head_t waitq;
|
wait_queue_head_t waitq;
|
||||||
int rpl_done;
|
int rpl_done;
|
||||||
int rpl_err;
|
int rpl_err;
|
||||||
u32 flags;
|
unsigned long flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct iwch_listen_ep {
|
struct iwch_listen_ep {
|
||||||
|
Loading…
Reference in New Issue
Block a user