RDMA/cxgb4: Couple of abort fixes

- fix a race where the driver could end up sending a close_con_req
  after an abort_rpl.  In c4iw_ep_disconnect(), send abort or close
  request with the ep mutex held.

- fix a hang where driver fails to wake up when a connection is reset
  during a normal close.  Wake up any waiters in the interrupt path,
  and correctly cleanup after rdma_fini() failures.

Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
This commit is contained in:
Steve Wise 2011-06-14 20:59:27 +00:00 committed by Roland Dreier
parent 301c2c3f03
commit 8da7e7a552
2 changed files with 38 additions and 13 deletions

View File

@ -1463,9 +1463,9 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_qp_attributes attrs; struct c4iw_qp_attributes attrs;
int disconnect = 1; int disconnect = 1;
int release = 0; int release = 0;
int abort = 0;
struct tid_info *t = dev->rdev.lldi.tids; struct tid_info *t = dev->rdev.lldi.tids;
unsigned int tid = GET_TID(hdr); unsigned int tid = GET_TID(hdr);
int ret;
ep = lookup_tid(t, tid); ep = lookup_tid(t, tid);
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
@ -1501,10 +1501,12 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
start_ep_timer(ep); start_ep_timer(ep);
__state_set(&ep->com, CLOSING); __state_set(&ep->com, CLOSING);
attrs.next_state = C4IW_QP_STATE_CLOSING; attrs.next_state = C4IW_QP_STATE_CLOSING;
abort = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, ret = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
if (ret != -ECONNRESET) {
peer_close_upcall(ep); peer_close_upcall(ep);
disconnect = 1; disconnect = 1;
}
break; break;
case ABORTING: case ABORTING:
disconnect = 0; disconnect = 0;
@ -2109,15 +2111,16 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
break; break;
} }
mutex_unlock(&ep->com.mutex);
if (close) { if (close) {
if (abrupt) if (abrupt) {
ret = abort_connection(ep, NULL, gfp); close_complete_upcall(ep);
else ret = send_abort(ep, NULL, gfp);
} else
ret = send_halfclose(ep, gfp); ret = send_halfclose(ep, gfp);
if (ret) if (ret)
fatal = 1; fatal = 1;
} }
mutex_unlock(&ep->com.mutex);
if (fatal) if (fatal)
release_ep_resources(ep); release_ep_resources(ep);
return ret; return ret;
@ -2301,6 +2304,31 @@ static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
return 0; return 0;
} }
static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct cpl_abort_req_rss *req = cplhdr(skb);
struct c4iw_ep *ep;
struct tid_info *t = dev->rdev.lldi.tids;
unsigned int tid = GET_TID(req);
ep = lookup_tid(t, tid);
if (is_neg_adv_abort(req->status)) {
PDBG("%s neg_adv_abort ep %p tid %u\n", __func__, ep,
ep->hwtid);
kfree_skb(skb);
return 0;
}
PDBG("%s ep %p tid %u state %u\n", __func__, ep, ep->hwtid,
ep->com.state);
/*
* Wake up any threads in rdma_init() or rdma_fini().
*/
c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
sched(dev, skb);
return 0;
}
/* /*
* Most upcalls from the T4 Core go to sched() to * Most upcalls from the T4 Core go to sched() to
* schedule the processing on a work queue. * schedule the processing on a work queue.
@ -2317,7 +2345,7 @@ c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS] = {
[CPL_PASS_ESTABLISH] = sched, [CPL_PASS_ESTABLISH] = sched,
[CPL_PEER_CLOSE] = sched, [CPL_PEER_CLOSE] = sched,
[CPL_CLOSE_CON_RPL] = sched, [CPL_CLOSE_CON_RPL] = sched,
[CPL_ABORT_REQ_RSS] = sched, [CPL_ABORT_REQ_RSS] = peer_abort_intr,
[CPL_RDMA_TERMINATE] = sched, [CPL_RDMA_TERMINATE] = sched,
[CPL_FW4_ACK] = sched, [CPL_FW4_ACK] = sched,
[CPL_SET_TCB_RPL] = set_tcb_rpl, [CPL_SET_TCB_RPL] = set_tcb_rpl,

View File

@ -1207,11 +1207,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
c4iw_get_ep(&qhp->ep->com); c4iw_get_ep(&qhp->ep->com);
} }
ret = rdma_fini(rhp, qhp, ep); ret = rdma_fini(rhp, qhp, ep);
if (ret) { if (ret)
if (internal)
c4iw_get_ep(&qhp->ep->com);
goto err; goto err;
}
break; break;
case C4IW_QP_STATE_TERMINATE: case C4IW_QP_STATE_TERMINATE:
set_state(qhp, C4IW_QP_STATE_TERMINATE); set_state(qhp, C4IW_QP_STATE_TERMINATE);