aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Smart <jsmart2021@gmail.com>2018-02-06 09:48:29 -0500
committerSagi Grimberg <sagi@grimberg.me>2018-02-11 03:45:34 -0500
commit3efd6e8ebe19f0774c82de582849539b60cc4d97 (patch)
tree7b920c5ea26231bc3d1d00858cadee1e5674d253
parent8cb6af7b3a6d47f95ecb461a3f8d39cf6a64e4ae (diff)
nvme_fc: correct abort race condition on resets
During reset handling, there is live io completing while the reset is taking place. The reset path attempts to abort all outstanding io, counting the number of ios that were reset. It then waits for those ios to be reclaimed from the lldd before continuing. The transport's logic on io state and flag setting was poor, allowing ios to complete simultaneous to the abort request. The completed ios were counted, but as the completion had already occurred, the completion never reduced the count. As the count never zeros, the reset/delete never completes. Tighten it up by unconditionally changing the op state to completed when the io done handler is called. The reset/abort path now changes the op state to aborted, but the abort only continues if the op state was live priviously. If complete, the abort is backed out. Thus proper counting of io aborts and their completions is working again. Also removed the TERMIO state on the op as it's redundant with the op's aborted state. Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: James Smart <james.smart@broadcom.com> Signed-off-by: Sagi Grimberg <sagi@grimberg.me>
-rw-r--r--drivers/nvme/host/fc.c98
1 files changed, 26 insertions, 72 deletions
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index e2df22d56b2a..4673882ce152 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -1512,13 +1512,19 @@ nvme_fc_exit_request(struct blk_mq_tag_set *set, struct request *rq,
1512static int 1512static int
1513__nvme_fc_abort_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_fcp_op *op) 1513__nvme_fc_abort_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_fcp_op *op)
1514{ 1514{
1515 int state; 1515 unsigned long flags;
1516 int opstate;
1517
1518 spin_lock_irqsave(&ctrl->lock, flags);
1519 opstate = atomic_xchg(&op->state, FCPOP_STATE_ABORTED);
1520 if (opstate != FCPOP_STATE_ACTIVE)
1521 atomic_set(&op->state, opstate);
1522 else if (ctrl->flags & FCCTRL_TERMIO)
1523 ctrl->iocnt++;
1524 spin_unlock_irqrestore(&ctrl->lock, flags);
1516 1525
1517 state = atomic_xchg(&op->state, FCPOP_STATE_ABORTED); 1526 if (opstate != FCPOP_STATE_ACTIVE)
1518 if (state != FCPOP_STATE_ACTIVE) {
1519 atomic_set(&op->state, state);
1520 return -ECANCELED; 1527 return -ECANCELED;
1521 }
1522 1528
1523 ctrl->lport->ops->fcp_abort(&ctrl->lport->localport, 1529 ctrl->lport->ops->fcp_abort(&ctrl->lport->localport,
1524 &ctrl->rport->remoteport, 1530 &ctrl->rport->remoteport,
@@ -1532,52 +1538,23 @@ static void
1532nvme_fc_abort_aen_ops(struct nvme_fc_ctrl *ctrl) 1538nvme_fc_abort_aen_ops(struct nvme_fc_ctrl *ctrl)
1533{ 1539{
1534 struct nvme_fc_fcp_op *aen_op = ctrl->aen_ops; 1540 struct nvme_fc_fcp_op *aen_op = ctrl->aen_ops;
1535 unsigned long flags; 1541 int i;
1536 int i, ret;
1537
1538 for (i = 0; i < NVME_NR_AEN_COMMANDS; i++, aen_op++) {
1539 if (atomic_read(&aen_op->state) != FCPOP_STATE_ACTIVE)
1540 continue;
1541
1542 spin_lock_irqsave(&ctrl->lock, flags);
1543 if (ctrl->flags & FCCTRL_TERMIO) {
1544 ctrl->iocnt++;
1545 aen_op->flags |= FCOP_FLAGS_TERMIO;
1546 }
1547 spin_unlock_irqrestore(&ctrl->lock, flags);
1548
1549 ret = __nvme_fc_abort_op(ctrl, aen_op);
1550 if (ret) {
1551 /*
1552 * if __nvme_fc_abort_op failed the io wasn't
1553 * active. Thus this call path is running in
1554 * parallel to the io complete. Treat as non-error.
1555 */
1556 1542
1557 /* back out the flags/counters */ 1543 for (i = 0; i < NVME_NR_AEN_COMMANDS; i++, aen_op++)
1558 spin_lock_irqsave(&ctrl->lock, flags); 1544 __nvme_fc_abort_op(ctrl, aen_op);
1559 if (ctrl->flags & FCCTRL_TERMIO)
1560 ctrl->iocnt--;
1561 aen_op->flags &= ~FCOP_FLAGS_TERMIO;
1562 spin_unlock_irqrestore(&ctrl->lock, flags);
1563 return;
1564 }
1565 }
1566} 1545}
1567 1546
1568static inline int 1547static inline int
1569__nvme_fc_fcpop_chk_teardowns(struct nvme_fc_ctrl *ctrl, 1548__nvme_fc_fcpop_chk_teardowns(struct nvme_fc_ctrl *ctrl,
1570 struct nvme_fc_fcp_op *op) 1549 struct nvme_fc_fcp_op *op, int opstate)
1571{ 1550{
1572 unsigned long flags; 1551 unsigned long flags;
1573 bool complete_rq = false; 1552 bool complete_rq = false;
1574 1553
1575 spin_lock_irqsave(&ctrl->lock, flags); 1554 spin_lock_irqsave(&ctrl->lock, flags);
1576 if (unlikely(op->flags & FCOP_FLAGS_TERMIO)) { 1555 if (opstate == FCPOP_STATE_ABORTED && ctrl->flags & FCCTRL_TERMIO) {
1577 if (ctrl->flags & FCCTRL_TERMIO) { 1556 if (!--ctrl->iocnt)
1578 if (!--ctrl->iocnt) 1557 wake_up(&ctrl->ioabort_wait);
1579 wake_up(&ctrl->ioabort_wait);
1580 }
1581 } 1558 }
1582 if (op->flags & FCOP_FLAGS_RELEASED) 1559 if (op->flags & FCOP_FLAGS_RELEASED)
1583 complete_rq = true; 1560 complete_rq = true;
@@ -1601,6 +1578,7 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
1601 __le16 status = cpu_to_le16(NVME_SC_SUCCESS << 1); 1578 __le16 status = cpu_to_le16(NVME_SC_SUCCESS << 1);
1602 union nvme_result result; 1579 union nvme_result result;
1603 bool terminate_assoc = true; 1580 bool terminate_assoc = true;
1581 int opstate;
1604 1582
1605 /* 1583 /*
1606 * WARNING: 1584 * WARNING:
@@ -1639,11 +1617,12 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
1639 * association to be terminated. 1617 * association to be terminated.
1640 */ 1618 */
1641 1619
1620 opstate = atomic_xchg(&op->state, FCPOP_STATE_COMPLETE);
1621
1642 fc_dma_sync_single_for_cpu(ctrl->lport->dev, op->fcp_req.rspdma, 1622 fc_dma_sync_single_for_cpu(ctrl->lport->dev, op->fcp_req.rspdma,
1643 sizeof(op->rsp_iu), DMA_FROM_DEVICE); 1623 sizeof(op->rsp_iu), DMA_FROM_DEVICE);
1644 1624
1645 if (atomic_read(&op->state) == FCPOP_STATE_ABORTED || 1625 if (opstate == FCPOP_STATE_ABORTED)
1646 op->flags & FCOP_FLAGS_TERMIO)
1647 status = cpu_to_le16(NVME_SC_ABORT_REQ << 1); 1626 status = cpu_to_le16(NVME_SC_ABORT_REQ << 1);
1648 else if (freq->status) 1627 else if (freq->status)
1649 status = cpu_to_le16(NVME_SC_INTERNAL << 1); 1628 status = cpu_to_le16(NVME_SC_INTERNAL << 1);
@@ -1708,7 +1687,7 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
1708done: 1687done:
1709 if (op->flags & FCOP_FLAGS_AEN) { 1688 if (op->flags & FCOP_FLAGS_AEN) {
1710 nvme_complete_async_event(&queue->ctrl->ctrl, status, &result); 1689 nvme_complete_async_event(&queue->ctrl->ctrl, status, &result);
1711 __nvme_fc_fcpop_chk_teardowns(ctrl, op); 1690 __nvme_fc_fcpop_chk_teardowns(ctrl, op, opstate);
1712 atomic_set(&op->state, FCPOP_STATE_IDLE); 1691 atomic_set(&op->state, FCPOP_STATE_IDLE);
1713 op->flags = FCOP_FLAGS_AEN; /* clear other flags */ 1692 op->flags = FCOP_FLAGS_AEN; /* clear other flags */
1714 nvme_fc_ctrl_put(ctrl); 1693 nvme_fc_ctrl_put(ctrl);
@@ -1725,7 +1704,7 @@ done:
1725 ctrl->ctrl.state == NVME_CTRL_CONNECTING)) 1704 ctrl->ctrl.state == NVME_CTRL_CONNECTING))
1726 status |= cpu_to_le16(NVME_SC_DNR << 1); 1705 status |= cpu_to_le16(NVME_SC_DNR << 1);
1727 1706
1728 if (__nvme_fc_fcpop_chk_teardowns(ctrl, op)) 1707 if (__nvme_fc_fcpop_chk_teardowns(ctrl, op, opstate))
1729 __nvme_fc_final_op_cleanup(rq); 1708 __nvme_fc_final_op_cleanup(rq);
1730 else 1709 else
1731 nvme_end_request(rq, status, result); 1710 nvme_end_request(rq, status, result);
@@ -2421,8 +2400,7 @@ __nvme_fc_final_op_cleanup(struct request *rq)
2421 struct nvme_fc_ctrl *ctrl = op->ctrl; 2400 struct nvme_fc_ctrl *ctrl = op->ctrl;
2422 2401
2423 atomic_set(&op->state, FCPOP_STATE_IDLE); 2402 atomic_set(&op->state, FCPOP_STATE_IDLE);
2424 op->flags &= ~(FCOP_FLAGS_TERMIO | FCOP_FLAGS_RELEASED | 2403 op->flags &= ~(FCOP_FLAGS_RELEASED | FCOP_FLAGS_COMPLETE);
2425 FCOP_FLAGS_COMPLETE);
2426 2404
2427 nvme_fc_unmap_data(ctrl, rq, op); 2405 nvme_fc_unmap_data(ctrl, rq, op);
2428 nvme_complete_rq(rq); 2406 nvme_complete_rq(rq);
@@ -2476,35 +2454,11 @@ nvme_fc_terminate_exchange(struct request *req, void *data, bool reserved)
2476 struct nvme_ctrl *nctrl = data; 2454 struct nvme_ctrl *nctrl = data;
2477 struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); 2455 struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
2478 struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req); 2456 struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req);
2479 unsigned long flags;
2480 int status;
2481 2457
2482 if (!blk_mq_request_started(req)) 2458 if (!blk_mq_request_started(req))
2483 return; 2459 return;
2484 2460
2485 spin_lock_irqsave(&ctrl->lock, flags); 2461 __nvme_fc_abort_op(ctrl, op);
2486 if (ctrl->flags & FCCTRL_TERMIO) {
2487 ctrl->iocnt++;
2488 op->flags |= FCOP_FLAGS_TERMIO;
2489 }
2490 spin_unlock_irqrestore(&ctrl->lock, flags);
2491
2492 status = __nvme_fc_abort_op(ctrl, op);
2493 if (status) {
2494 /*
2495 * if __nvme_fc_abort_op failed the io wasn't
2496 * active. Thus this call path is running in
2497 * parallel to the io complete. Treat as non-error.
2498 */
2499
2500 /* back out the flags/counters */
2501 spin_lock_irqsave(&ctrl->lock, flags);
2502 if (ctrl->flags & FCCTRL_TERMIO)
2503 ctrl->iocnt--;
2504 op->flags &= ~FCOP_FLAGS_TERMIO;
2505 spin_unlock_irqrestore(&ctrl->lock, flags);
2506 return;
2507 }
2508} 2462}
2509 2463
2510 2464