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) |