aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2008-02-29 19:25:19 -0500
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-04-07 13:15:41 -0400
commit30bd7df8ced23eefec87a5cda96dc99b002ed9da (patch)
tree80702b8ba0454540cc7ea19cfe49871192ab860c
parentf7441a791aeaeac2e1f1f71b485d1372016f9285 (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>
-rw-r--r--drivers/scsi/scsi_error.c122
-rw-r--r--include/scsi/scsi_eh.h1
-rw-r--r--include/scsi/scsi_host.h1
3 files changed, 106 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
527static 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 */
543static 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 */
1104static 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}
1454EXPORT_SYMBOL_GPL(scsi_eh_ready_devs); 1539EXPORT_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}
1628EXPORT_SYMBOL(scsi_report_bus_reset); 1711EXPORT_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}
1664EXPORT_SYMBOL(scsi_report_device_reset); 1745EXPORT_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)
diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h
index 25071d5d9bf8..37a7614f62f4 100644
--- a/include/scsi/scsi_eh.h
+++ b/include/scsi/scsi_eh.h
@@ -64,6 +64,7 @@ extern int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
64#define SCSI_TRY_RESET_DEVICE 1 64#define SCSI_TRY_RESET_DEVICE 1
65#define SCSI_TRY_RESET_BUS 2 65#define SCSI_TRY_RESET_BUS 2
66#define SCSI_TRY_RESET_HOST 3 66#define SCSI_TRY_RESET_HOST 3
67#define SCSI_TRY_RESET_TARGET 4
67 68
68extern int scsi_reset_provider(struct scsi_device *, int); 69extern int scsi_reset_provider(struct scsi_device *, int);
69 70
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 530ff4c553f8..49132862bfaa 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -172,6 +172,7 @@ struct scsi_host_template {
172 */ 172 */
173 int (* eh_abort_handler)(struct scsi_cmnd *); 173 int (* eh_abort_handler)(struct scsi_cmnd *);
174 int (* eh_device_reset_handler)(struct scsi_cmnd *); 174 int (* eh_device_reset_handler)(struct scsi_cmnd *);
175 int (* eh_target_reset_handler)(struct scsi_cmnd *);
175 int (* eh_bus_reset_handler)(struct scsi_cmnd *); 176 int (* eh_bus_reset_handler)(struct scsi_cmnd *);
176 int (* eh_host_reset_handler)(struct scsi_cmnd *); 177 int (* eh_host_reset_handler)(struct scsi_cmnd *);
177 178