diff options
| author | Mike Christie <michaelc@cs.wisc.edu> | 2008-02-29 19:25:19 -0500 |
|---|---|---|
| committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-04-07 13:15:41 -0400 |
| commit | 30bd7df8ced23eefec87a5cda96dc99b002ed9da (patch) | |
| tree | 80702b8ba0454540cc7ea19cfe49871192ab860c /drivers/scsi/scsi_error.c | |
| parent | f7441a791aeaeac2e1f1f71b485d1372016f9285 (diff) | |
[SCSI] scsi_error: add target reset handler
The problem is that serveral drivers are sending a target reset from the
device reset handler, and if we have multiple devices a target reset gets
sent for each device when only one would be sufficient. And if we do a target
reset it affects all the commands on the target so the device reset handler
code only cleaning up one devices's commands makes programming the driver a
little more difficult than it should be.
This patch adds a target reset handler, which drivers can use to send
a target reset. If successful it cleans up the commands for a devices
accessed through that starget.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/scsi_error.c')
| -rw-r--r-- | drivers/scsi/scsi_error.c | 122 |
1 files changed, 104 insertions, 18 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 045a0868fc7b..1221d2ca0c64 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
| @@ -524,6 +524,41 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd) | |||
| 524 | return rtn; | 524 | return rtn; |
| 525 | } | 525 | } |
| 526 | 526 | ||
| 527 | static void __scsi_report_device_reset(struct scsi_device *sdev, void *data) | ||
| 528 | { | ||
| 529 | sdev->was_reset = 1; | ||
| 530 | sdev->expecting_cc_ua = 1; | ||
| 531 | } | ||
| 532 | |||
| 533 | /** | ||
| 534 | * scsi_try_target_reset - Ask host to perform a target reset | ||
| 535 | * @scmd: SCSI cmd used to send a target reset | ||
| 536 | * | ||
| 537 | * Notes: | ||
| 538 | * There is no timeout for this operation. if this operation is | ||
| 539 | * unreliable for a given host, then the host itself needs to put a | ||
| 540 | * timer on it, and set the host back to a consistent state prior to | ||
| 541 | * returning. | ||
| 542 | */ | ||
| 543 | static int scsi_try_target_reset(struct scsi_cmnd *scmd) | ||
| 544 | { | ||
| 545 | unsigned long flags; | ||
| 546 | int rtn; | ||
| 547 | |||
| 548 | if (!scmd->device->host->hostt->eh_target_reset_handler) | ||
| 549 | return FAILED; | ||
| 550 | |||
| 551 | rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd); | ||
| 552 | if (rtn == SUCCESS) { | ||
| 553 | spin_lock_irqsave(scmd->device->host->host_lock, flags); | ||
| 554 | __starget_for_each_device(scsi_target(scmd->device), NULL, | ||
| 555 | __scsi_report_device_reset); | ||
| 556 | spin_unlock_irqrestore(scmd->device->host->host_lock, flags); | ||
| 557 | } | ||
| 558 | |||
| 559 | return rtn; | ||
| 560 | } | ||
| 561 | |||
| 527 | /** | 562 | /** |
| 528 | * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev | 563 | * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev |
| 529 | * @scmd: SCSI cmd used to send BDR | 564 | * @scmd: SCSI cmd used to send BDR |
| @@ -542,11 +577,8 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) | |||
| 542 | return FAILED; | 577 | return FAILED; |
| 543 | 578 | ||
| 544 | rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd); | 579 | rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd); |
| 545 | if (rtn == SUCCESS) { | 580 | if (rtn == SUCCESS) |
| 546 | scmd->device->was_reset = 1; | 581 | __scsi_report_device_reset(scmd->device, NULL); |
| 547 | scmd->device->expecting_cc_ua = 1; | ||
| 548 | } | ||
| 549 | |||
| 550 | return rtn; | 582 | return rtn; |
| 551 | } | 583 | } |
| 552 | 584 | ||
| @@ -584,8 +616,9 @@ static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd) | |||
| 584 | { | 616 | { |
| 585 | if (__scsi_try_to_abort_cmd(scmd) != SUCCESS) | 617 | if (__scsi_try_to_abort_cmd(scmd) != SUCCESS) |
| 586 | if (scsi_try_bus_device_reset(scmd) != SUCCESS) | 618 | if (scsi_try_bus_device_reset(scmd) != SUCCESS) |
| 587 | if (scsi_try_bus_reset(scmd) != SUCCESS) | 619 | if (scsi_try_target_reset(scmd) != SUCCESS) |
| 588 | scsi_try_host_reset(scmd); | 620 | if (scsi_try_bus_reset(scmd) != SUCCESS) |
| 621 | scsi_try_host_reset(scmd); | ||
| 589 | } | 622 | } |
| 590 | 623 | ||
| 591 | /** | 624 | /** |
| @@ -1060,6 +1093,56 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, | |||
| 1060 | } | 1093 | } |
| 1061 | 1094 | ||
| 1062 | /** | 1095 | /** |
| 1096 | * scsi_eh_target_reset - send target reset if needed | ||
| 1097 | * @shost: scsi host being recovered. | ||
| 1098 | * @work_q: &list_head for pending commands. | ||
| 1099 | * @done_q: &list_head for processed commands. | ||
| 1100 | * | ||
| 1101 | * Notes: | ||
| 1102 | * Try a target reset. | ||
| 1103 | */ | ||
| 1104 | static int scsi_eh_target_reset(struct Scsi_Host *shost, | ||
| 1105 | struct list_head *work_q, | ||
| 1106 | struct list_head *done_q) | ||
| 1107 | { | ||
| 1108 | struct scsi_cmnd *scmd, *tgtr_scmd, *next; | ||
| 1109 | unsigned int id; | ||
| 1110 | int rtn; | ||
| 1111 | |||
| 1112 | for (id = 0; id <= shost->max_id; id++) { | ||
| 1113 | tgtr_scmd = NULL; | ||
| 1114 | list_for_each_entry(scmd, work_q, eh_entry) { | ||
| 1115 | if (id == scmd_id(scmd)) { | ||
| 1116 | tgtr_scmd = scmd; | ||
| 1117 | break; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | if (!tgtr_scmd) | ||
| 1121 | continue; | ||
| 1122 | |||
| 1123 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset " | ||
| 1124 | "to target %d\n", | ||
| 1125 | current->comm, id)); | ||
| 1126 | rtn = scsi_try_target_reset(tgtr_scmd); | ||
| 1127 | if (rtn == SUCCESS) { | ||
| 1128 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { | ||
| 1129 | if (id == scmd_id(scmd)) | ||
| 1130 | if (!scsi_device_online(scmd->device) || | ||
| 1131 | !scsi_eh_tur(tgtr_scmd)) | ||
| 1132 | scsi_eh_finish_cmd(scmd, | ||
| 1133 | done_q); | ||
| 1134 | } | ||
| 1135 | } else | ||
| 1136 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset" | ||
| 1137 | " failed target: " | ||
| 1138 | "%d\n", | ||
| 1139 | current->comm, id)); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | return list_empty(work_q); | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | /** | ||
| 1063 | * scsi_eh_bus_reset - send a bus reset | 1146 | * scsi_eh_bus_reset - send a bus reset |
| 1064 | * @shost: &scsi host being recovered. | 1147 | * @shost: &scsi host being recovered. |
| 1065 | * @work_q: &list_head for pending commands. | 1148 | * @work_q: &list_head for pending commands. |
| @@ -1447,9 +1530,11 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost, | |||
| 1447 | { | 1530 | { |
| 1448 | if (!scsi_eh_stu(shost, work_q, done_q)) | 1531 | if (!scsi_eh_stu(shost, work_q, done_q)) |
| 1449 | if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) | 1532 | if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) |
| 1450 | if (!scsi_eh_bus_reset(shost, work_q, done_q)) | 1533 | if (!scsi_eh_target_reset(shost, work_q, done_q)) |
| 1451 | if (!scsi_eh_host_reset(work_q, done_q)) | 1534 | if (!scsi_eh_bus_reset(shost, work_q, done_q)) |
| 1452 | scsi_eh_offline_sdevs(work_q, done_q); | 1535 | if (!scsi_eh_host_reset(work_q, done_q)) |
| 1536 | scsi_eh_offline_sdevs(work_q, | ||
| 1537 | done_q); | ||
| 1453 | } | 1538 | } |
| 1454 | EXPORT_SYMBOL_GPL(scsi_eh_ready_devs); | 1539 | EXPORT_SYMBOL_GPL(scsi_eh_ready_devs); |
| 1455 | 1540 | ||
| @@ -1619,10 +1704,8 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel) | |||
| 1619 | struct scsi_device *sdev; | 1704 | struct scsi_device *sdev; |
| 1620 | 1705 | ||
| 1621 | __shost_for_each_device(sdev, shost) { | 1706 | __shost_for_each_device(sdev, shost) { |
| 1622 | if (channel == sdev_channel(sdev)) { | 1707 | if (channel == sdev_channel(sdev)) |
| 1623 | sdev->was_reset = 1; | 1708 | __scsi_report_device_reset(sdev, NULL); |
| 1624 | sdev->expecting_cc_ua = 1; | ||
| 1625 | } | ||
| 1626 | } | 1709 | } |
| 1627 | } | 1710 | } |
| 1628 | EXPORT_SYMBOL(scsi_report_bus_reset); | 1711 | EXPORT_SYMBOL(scsi_report_bus_reset); |
| @@ -1655,10 +1738,8 @@ void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target) | |||
| 1655 | 1738 | ||
| 1656 | __shost_for_each_device(sdev, shost) { | 1739 | __shost_for_each_device(sdev, shost) { |
| 1657 | if (channel == sdev_channel(sdev) && | 1740 | if (channel == sdev_channel(sdev) && |
| 1658 | target == sdev_id(sdev)) { | 1741 | target == sdev_id(sdev)) |
| 1659 | sdev->was_reset = 1; | 1742 | __scsi_report_device_reset(sdev, NULL); |
| 1660 | sdev->expecting_cc_ua = 1; | ||
| 1661 | } | ||
| 1662 | } | 1743 | } |
| 1663 | } | 1744 | } |
| 1664 | EXPORT_SYMBOL(scsi_report_device_reset); | 1745 | EXPORT_SYMBOL(scsi_report_device_reset); |
| @@ -1714,6 +1795,11 @@ scsi_reset_provider(struct scsi_device *dev, int flag) | |||
| 1714 | if (rtn == SUCCESS) | 1795 | if (rtn == SUCCESS) |
| 1715 | break; | 1796 | break; |
| 1716 | /* FALLTHROUGH */ | 1797 | /* FALLTHROUGH */ |
| 1798 | case SCSI_TRY_RESET_TARGET: | ||
| 1799 | rtn = scsi_try_target_reset(scmd); | ||
| 1800 | if (rtn == SUCCESS) | ||
| 1801 | break; | ||
| 1802 | /* FALLTHROUGH */ | ||
| 1717 | case SCSI_TRY_RESET_BUS: | 1803 | case SCSI_TRY_RESET_BUS: |
| 1718 | rtn = scsi_try_bus_reset(scmd); | 1804 | rtn = scsi_try_bus_reset(scmd); |
| 1719 | if (rtn == SUCCESS) | 1805 | if (rtn == SUCCESS) |
