aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/hw
diff options
context:
space:
mode:
authorSteve Wise <swise@opengridcomputing.com>2014-04-09 10:38:26 -0400
committerRoland Dreier <roland@purestorage.com>2014-04-11 14:36:08 -0400
commitb4e2901c52cc79f287e2b25804e029880e5e4b07 (patch)
tree51920dd15a71c6aabcd896da6265fa8ddedc78a3 /drivers/infiniband/hw
parentdef4771f4bf428d39c7fe6006a9e1a20ee380d1e (diff)
RDMA/cxgb4: SQ flush fix
There is a race when moving a QP from RTS->CLOSING where a SQ work request could be posted after the FW receives the RDMA_RI/FINI WR. The SQ work request will never get processed, and should be completed with FLUSHED status. Function c4iw_flush_sq(), however was dropping the oldest SQ work request when in CLOSING or IDLE states, instead of completing the pending work request. If that oldest pending work request was actually complete and has a CQE in the CQ, then when that CQE is proceessed in poll_cq, we'll BUG_ON() due to the inconsistent SQ/CQ state. This is a very small timing hole and has only been hit once so far. The fix is two-fold: 1) c4iw_flush_sq() MUST always flush all non-completed WRs with FLUSHED status regardless of the QP state. 2) In c4iw_modify_rc_qp(), always set the "in error" bit on the queue before moving the state out of RTS. This ensures that the state transition will not happen while another thread is in post_rc_send(), because set_state() and post_rc_send() both aquire the qp spinlock. Also, once we transition the state out of RTS, subsequent calls to post_rc_send() will fail because the "in error" bit is set. I don't think this fully closes the race where the FW can get a FINI followed a SQ work request being posted (because they are posted to differente EQs), but the #1 fix will handle the issue by flushing the SQ work request. Signed-off-by: Steve Wise <swise@opengridcomputing.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband/hw')
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c22
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c6
2 files changed, 11 insertions, 17 deletions
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index ce468e542428..e17b155b3758 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -235,27 +235,21 @@ int c4iw_flush_sq(struct c4iw_qp *qhp)
235 struct t4_cq *cq = &chp->cq; 235 struct t4_cq *cq = &chp->cq;
236 int idx; 236 int idx;
237 struct t4_swsqe *swsqe; 237 struct t4_swsqe *swsqe;
238 int error = (qhp->attr.state != C4IW_QP_STATE_CLOSING &&
239 qhp->attr.state != C4IW_QP_STATE_IDLE);
240 238
241 if (wq->sq.flush_cidx == -1) 239 if (wq->sq.flush_cidx == -1)
242 wq->sq.flush_cidx = wq->sq.cidx; 240 wq->sq.flush_cidx = wq->sq.cidx;
243 idx = wq->sq.flush_cidx; 241 idx = wq->sq.flush_cidx;
244 BUG_ON(idx >= wq->sq.size); 242 BUG_ON(idx >= wq->sq.size);
245 while (idx != wq->sq.pidx) { 243 while (idx != wq->sq.pidx) {
246 if (error) { 244 swsqe = &wq->sq.sw_sq[idx];
247 swsqe = &wq->sq.sw_sq[idx]; 245 BUG_ON(swsqe->flushed);
248 BUG_ON(swsqe->flushed); 246 swsqe->flushed = 1;
249 swsqe->flushed = 1; 247 insert_sq_cqe(wq, cq, swsqe);
250 insert_sq_cqe(wq, cq, swsqe); 248 if (wq->sq.oldest_read == swsqe) {
251 if (wq->sq.oldest_read == swsqe) { 249 BUG_ON(swsqe->opcode != FW_RI_READ_REQ);
252 BUG_ON(swsqe->opcode != FW_RI_READ_REQ); 250 advance_oldest_read(wq);
253 advance_oldest_read(wq);
254 }
255 flushed++;
256 } else {
257 t4_sq_consume(wq);
258 } 251 }
252 flushed++;
259 if (++idx == wq->sq.size) 253 if (++idx == wq->sq.size)
260 idx = 0; 254 idx = 0;
261 } 255 }
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index e2fcbf4814f2..9b4a8b88908e 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -1367,6 +1367,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
1367 switch (attrs->next_state) { 1367 switch (attrs->next_state) {
1368 case C4IW_QP_STATE_CLOSING: 1368 case C4IW_QP_STATE_CLOSING:
1369 BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2); 1369 BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2);
1370 t4_set_wq_in_error(&qhp->wq);
1370 set_state(qhp, C4IW_QP_STATE_CLOSING); 1371 set_state(qhp, C4IW_QP_STATE_CLOSING);
1371 ep = qhp->ep; 1372 ep = qhp->ep;
1372 if (!internal) { 1373 if (!internal) {
@@ -1374,16 +1375,15 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
1374 disconnect = 1; 1375 disconnect = 1;
1375 c4iw_get_ep(&qhp->ep->com); 1376 c4iw_get_ep(&qhp->ep->com);
1376 } 1377 }
1377 t4_set_wq_in_error(&qhp->wq);
1378 ret = rdma_fini(rhp, qhp, ep); 1378 ret = rdma_fini(rhp, qhp, ep);
1379 if (ret) 1379 if (ret)
1380 goto err; 1380 goto err;
1381 break; 1381 break;
1382 case C4IW_QP_STATE_TERMINATE: 1382 case C4IW_QP_STATE_TERMINATE:
1383 t4_set_wq_in_error(&qhp->wq);
1383 set_state(qhp, C4IW_QP_STATE_TERMINATE); 1384 set_state(qhp, C4IW_QP_STATE_TERMINATE);
1384 qhp->attr.layer_etype = attrs->layer_etype; 1385 qhp->attr.layer_etype = attrs->layer_etype;
1385 qhp->attr.ecode = attrs->ecode; 1386 qhp->attr.ecode = attrs->ecode;
1386 t4_set_wq_in_error(&qhp->wq);
1387 ep = qhp->ep; 1387 ep = qhp->ep;
1388 disconnect = 1; 1388 disconnect = 1;
1389 if (!internal) 1389 if (!internal)
@@ -1396,8 +1396,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
1396 c4iw_get_ep(&qhp->ep->com); 1396 c4iw_get_ep(&qhp->ep->com);
1397 break; 1397 break;
1398 case C4IW_QP_STATE_ERROR: 1398 case C4IW_QP_STATE_ERROR:
1399 set_state(qhp, C4IW_QP_STATE_ERROR);
1400 t4_set_wq_in_error(&qhp->wq); 1399 t4_set_wq_in_error(&qhp->wq);
1400 set_state(qhp, C4IW_QP_STATE_ERROR);
1401 if (!internal) { 1401 if (!internal) {
1402 abort = 1; 1402 abort = 1;
1403 disconnect = 1; 1403 disconnect = 1;