diff options
Diffstat (limited to 'drivers/scsi/isci/task.c')
-rw-r--r-- | drivers/scsi/isci/task.c | 259 |
1 files changed, 258 insertions, 1 deletions
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 69f17b98e4fa..709c08171743 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/completion.h> | 56 | #include <linux/completion.h> |
57 | #include <linux/irqflags.h> | 57 | #include <linux/irqflags.h> |
58 | #include "sas.h" | 58 | #include "sas.h" |
59 | #include <scsi/libsas.h> | ||
59 | #include "remote_device.h" | 60 | #include "remote_device.h" |
60 | #include "remote_node_context.h" | 61 | #include "remote_node_context.h" |
61 | #include "isci.h" | 62 | #include "isci.h" |
@@ -1397,11 +1398,250 @@ isci_task_request_complete(struct isci_host *ihost, | |||
1397 | complete(tmf_complete); | 1398 | complete(tmf_complete); |
1398 | } | 1399 | } |
1399 | 1400 | ||
1401 | static void isci_smp_task_timedout(unsigned long _task) | ||
1402 | { | ||
1403 | struct sas_task *task = (void *) _task; | ||
1404 | unsigned long flags; | ||
1405 | |||
1406 | spin_lock_irqsave(&task->task_state_lock, flags); | ||
1407 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) | ||
1408 | task->task_state_flags |= SAS_TASK_STATE_ABORTED; | ||
1409 | spin_unlock_irqrestore(&task->task_state_lock, flags); | ||
1410 | |||
1411 | complete(&task->completion); | ||
1412 | } | ||
1413 | |||
1414 | static void isci_smp_task_done(struct sas_task *task) | ||
1415 | { | ||
1416 | if (!del_timer(&task->timer)) | ||
1417 | return; | ||
1418 | complete(&task->completion); | ||
1419 | } | ||
1420 | |||
1421 | static struct sas_task *isci_alloc_task(void) | ||
1422 | { | ||
1423 | struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL); | ||
1424 | |||
1425 | if (task) { | ||
1426 | INIT_LIST_HEAD(&task->list); | ||
1427 | spin_lock_init(&task->task_state_lock); | ||
1428 | task->task_state_flags = SAS_TASK_STATE_PENDING; | ||
1429 | init_timer(&task->timer); | ||
1430 | init_completion(&task->completion); | ||
1431 | } | ||
1432 | |||
1433 | return task; | ||
1434 | } | ||
1435 | |||
1436 | static void isci_free_task(struct isci_host *ihost, struct sas_task *task) | ||
1437 | { | ||
1438 | if (task) { | ||
1439 | BUG_ON(!list_empty(&task->list)); | ||
1440 | kfree(task); | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1444 | static int isci_smp_execute_task(struct isci_host *ihost, | ||
1445 | struct domain_device *dev, void *req, | ||
1446 | int req_size, void *resp, int resp_size) | ||
1447 | { | ||
1448 | int res, retry; | ||
1449 | struct sas_task *task = NULL; | ||
1450 | |||
1451 | for (retry = 0; retry < 3; retry++) { | ||
1452 | task = isci_alloc_task(); | ||
1453 | if (!task) | ||
1454 | return -ENOMEM; | ||
1455 | |||
1456 | task->dev = dev; | ||
1457 | task->task_proto = dev->tproto; | ||
1458 | sg_init_one(&task->smp_task.smp_req, req, req_size); | ||
1459 | sg_init_one(&task->smp_task.smp_resp, resp, resp_size); | ||
1460 | |||
1461 | task->task_done = isci_smp_task_done; | ||
1462 | |||
1463 | task->timer.data = (unsigned long) task; | ||
1464 | task->timer.function = isci_smp_task_timedout; | ||
1465 | task->timer.expires = jiffies + 10*HZ; | ||
1466 | add_timer(&task->timer); | ||
1467 | |||
1468 | res = isci_task_execute_task(task, 1, GFP_KERNEL); | ||
1469 | |||
1470 | if (res) { | ||
1471 | del_timer(&task->timer); | ||
1472 | dev_err(&ihost->pdev->dev, | ||
1473 | "%s: executing SMP task failed:%d\n", | ||
1474 | __func__, res); | ||
1475 | goto ex_err; | ||
1476 | } | ||
1477 | |||
1478 | wait_for_completion(&task->completion); | ||
1479 | res = -ECOMM; | ||
1480 | if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { | ||
1481 | dev_err(&ihost->pdev->dev, | ||
1482 | "%s: smp task timed out or aborted\n", | ||
1483 | __func__); | ||
1484 | isci_task_abort_task(task); | ||
1485 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { | ||
1486 | dev_err(&ihost->pdev->dev, | ||
1487 | "%s: SMP task aborted and not done\n", | ||
1488 | __func__); | ||
1489 | goto ex_err; | ||
1490 | } | ||
1491 | } | ||
1492 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1493 | task->task_status.stat == SAM_STAT_GOOD) { | ||
1494 | res = 0; | ||
1495 | break; | ||
1496 | } | ||
1497 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1498 | task->task_status.stat == SAS_DATA_UNDERRUN) { | ||
1499 | /* no error, but return the number of bytes of | ||
1500 | * underrun */ | ||
1501 | res = task->task_status.residual; | ||
1502 | break; | ||
1503 | } | ||
1504 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1505 | task->task_status.stat == SAS_DATA_OVERRUN) { | ||
1506 | res = -EMSGSIZE; | ||
1507 | break; | ||
1508 | } else { | ||
1509 | dev_err(&ihost->pdev->dev, | ||
1510 | "%s: task to dev %016llx response: 0x%x " | ||
1511 | "status 0x%x\n", __func__, | ||
1512 | SAS_ADDR(dev->sas_addr), | ||
1513 | task->task_status.resp, | ||
1514 | task->task_status.stat); | ||
1515 | isci_free_task(ihost, task); | ||
1516 | task = NULL; | ||
1517 | } | ||
1518 | } | ||
1519 | ex_err: | ||
1520 | BUG_ON(retry == 3 && task != NULL); | ||
1521 | isci_free_task(ihost, task); | ||
1522 | return res; | ||
1523 | } | ||
1524 | |||
1525 | #define DISCOVER_REQ_SIZE 16 | ||
1526 | #define DISCOVER_RESP_SIZE 56 | ||
1527 | |||
1528 | int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost, | ||
1529 | struct domain_device *dev, | ||
1530 | int phy_id, int *adt) | ||
1531 | { | ||
1532 | struct smp_resp *disc_resp; | ||
1533 | u8 *disc_req; | ||
1534 | int res; | ||
1535 | |||
1536 | disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL); | ||
1537 | if (!disc_resp) | ||
1538 | return -ENOMEM; | ||
1539 | |||
1540 | disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL); | ||
1541 | if (disc_req) { | ||
1542 | disc_req[0] = SMP_REQUEST; | ||
1543 | disc_req[1] = SMP_DISCOVER; | ||
1544 | disc_req[9] = phy_id; | ||
1545 | } else { | ||
1546 | kfree(disc_resp); | ||
1547 | return -ENOMEM; | ||
1548 | } | ||
1549 | res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE, | ||
1550 | disc_resp, DISCOVER_RESP_SIZE); | ||
1551 | if (!res) { | ||
1552 | if (disc_resp->result != SMP_RESP_FUNC_ACC) | ||
1553 | res = disc_resp->result; | ||
1554 | else | ||
1555 | *adt = disc_resp->disc.attached_dev_type; | ||
1556 | } | ||
1557 | kfree(disc_req); | ||
1558 | kfree(disc_resp); | ||
1559 | |||
1560 | return res; | ||
1561 | } | ||
1562 | |||
1563 | static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num) | ||
1564 | { | ||
1565 | struct domain_device *dev = idev->domain_dev; | ||
1566 | struct isci_port *iport = idev->isci_port; | ||
1567 | struct isci_host *ihost = iport->isci_host; | ||
1568 | int res, iteration = 0, attached_device_type; | ||
1569 | #define STP_WAIT_MSECS 25000 | ||
1570 | unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS); | ||
1571 | unsigned long deadline = jiffies + tmo; | ||
1572 | enum { | ||
1573 | SMP_PHYWAIT_PHYDOWN, | ||
1574 | SMP_PHYWAIT_PHYUP, | ||
1575 | SMP_PHYWAIT_DONE | ||
1576 | } phy_state = SMP_PHYWAIT_PHYDOWN; | ||
1577 | |||
1578 | /* While there is time, wait for the phy to go away and come back */ | ||
1579 | while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) { | ||
1580 | int event = atomic_read(&iport->event); | ||
1581 | |||
1582 | ++iteration; | ||
1583 | |||
1584 | tmo = wait_event_timeout(ihost->eventq, | ||
1585 | event != atomic_read(&iport->event) || | ||
1586 | !test_bit(IPORT_BCN_BLOCKED, &iport->flags), | ||
1587 | tmo); | ||
1588 | /* link down, stop polling */ | ||
1589 | if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags)) | ||
1590 | break; | ||
1591 | |||
1592 | dev_dbg(&ihost->pdev->dev, | ||
1593 | "%s: iport %p, iteration %d," | ||
1594 | " phase %d: time_remaining %lu, bcns = %d\n", | ||
1595 | __func__, iport, iteration, phy_state, | ||
1596 | tmo, test_bit(IPORT_BCN_PENDING, &iport->flags)); | ||
1597 | |||
1598 | res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num, | ||
1599 | &attached_device_type); | ||
1600 | tmo = deadline - jiffies; | ||
1601 | |||
1602 | if (res) { | ||
1603 | dev_warn(&ihost->pdev->dev, | ||
1604 | "%s: iteration %d, phase %d:" | ||
1605 | " SMP error=%d, time_remaining=%lu\n", | ||
1606 | __func__, iteration, phy_state, res, tmo); | ||
1607 | break; | ||
1608 | } | ||
1609 | dev_dbg(&ihost->pdev->dev, | ||
1610 | "%s: iport %p, iteration %d," | ||
1611 | " phase %d: time_remaining %lu, bcns = %d, " | ||
1612 | "attdevtype = %x\n", | ||
1613 | __func__, iport, iteration, phy_state, | ||
1614 | tmo, test_bit(IPORT_BCN_PENDING, &iport->flags), | ||
1615 | attached_device_type); | ||
1616 | |||
1617 | switch (phy_state) { | ||
1618 | case SMP_PHYWAIT_PHYDOWN: | ||
1619 | /* Has the device gone away? */ | ||
1620 | if (!attached_device_type) | ||
1621 | phy_state = SMP_PHYWAIT_PHYUP; | ||
1622 | |||
1623 | break; | ||
1624 | |||
1625 | case SMP_PHYWAIT_PHYUP: | ||
1626 | /* Has the device come back? */ | ||
1627 | if (attached_device_type) | ||
1628 | phy_state = SMP_PHYWAIT_DONE; | ||
1629 | break; | ||
1630 | |||
1631 | case SMP_PHYWAIT_DONE: | ||
1632 | break; | ||
1633 | } | ||
1634 | |||
1635 | } | ||
1636 | dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__); | ||
1637 | } | ||
1638 | |||
1400 | static int isci_reset_device(struct domain_device *dev, int hard_reset) | 1639 | static int isci_reset_device(struct domain_device *dev, int hard_reset) |
1401 | { | 1640 | { |
1402 | struct isci_remote_device *idev = dev->lldd_dev; | 1641 | struct isci_remote_device *idev = dev->lldd_dev; |
1403 | struct sas_phy *phy = sas_find_local_phy(dev); | 1642 | struct sas_phy *phy = sas_find_local_phy(dev); |
1404 | struct isci_host *ihost = dev_to_ihost(dev); | 1643 | struct isci_host *ihost = dev_to_ihost(dev); |
1644 | struct isci_port *iport = idev->isci_port; | ||
1405 | enum sci_status status; | 1645 | enum sci_status status; |
1406 | unsigned long flags; | 1646 | unsigned long flags; |
1407 | int rc; | 1647 | int rc; |
@@ -1432,6 +1672,10 @@ static int isci_reset_device(struct domain_device *dev, int hard_reset) | |||
1432 | /* Make sure all pending requests are able to be fully terminated. */ | 1672 | /* Make sure all pending requests are able to be fully terminated. */ |
1433 | isci_device_clear_reset_pending(ihost, idev); | 1673 | isci_device_clear_reset_pending(ihost, idev); |
1434 | 1674 | ||
1675 | /* If this is a device on an expander, disable BCN processing. */ | ||
1676 | if (!scsi_is_sas_phy_local(phy)) | ||
1677 | set_bit(IPORT_BCN_BLOCKED, &iport->flags); | ||
1678 | |||
1435 | rc = sas_phy_reset(phy, hard_reset); | 1679 | rc = sas_phy_reset(phy, hard_reset); |
1436 | 1680 | ||
1437 | /* Terminate in-progress I/O now. */ | 1681 | /* Terminate in-progress I/O now. */ |
@@ -1442,7 +1686,20 @@ static int isci_reset_device(struct domain_device *dev, int hard_reset) | |||
1442 | status = scic_remote_device_reset_complete(&idev->sci); | 1686 | status = scic_remote_device_reset_complete(&idev->sci); |
1443 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 1687 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
1444 | 1688 | ||
1445 | msleep(2000); /* just like mvsas */ | 1689 | /* If this is a device on an expander, bring the phy back up. */ |
1690 | if (!scsi_is_sas_phy_local(phy)) { | ||
1691 | /* A phy reset will cause the device to go away then reappear. | ||
1692 | * Since libsas will take action on incoming BCNs (eg. remove | ||
1693 | * a device going through an SMP phy-control driven reset), | ||
1694 | * we need to wait until the phy comes back up before letting | ||
1695 | * discovery proceed in libsas. | ||
1696 | */ | ||
1697 | isci_wait_for_smp_phy_reset(idev, phy->number); | ||
1698 | |||
1699 | spin_lock_irqsave(&ihost->scic_lock, flags); | ||
1700 | isci_port_bcn_enable(ihost, idev->isci_port); | ||
1701 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | ||
1702 | } | ||
1446 | 1703 | ||
1447 | if (status != SCI_SUCCESS) { | 1704 | if (status != SCI_SUCCESS) { |
1448 | dev_warn(&ihost->pdev->dev, | 1705 | dev_warn(&ihost->pdev->dev, |