diff options
Diffstat (limited to 'drivers/scsi/scsi_error.c')
-rw-r--r-- | drivers/scsi/scsi_error.c | 146 |
1 files changed, 133 insertions, 13 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 83e591b60193..e8bee9f0ad0f 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -87,6 +87,18 @@ void scsi_schedule_eh(struct Scsi_Host *shost) | |||
87 | } | 87 | } |
88 | EXPORT_SYMBOL_GPL(scsi_schedule_eh); | 88 | EXPORT_SYMBOL_GPL(scsi_schedule_eh); |
89 | 89 | ||
90 | static int scsi_host_eh_past_deadline(struct Scsi_Host *shost) | ||
91 | { | ||
92 | if (!shost->last_reset || !shost->eh_deadline) | ||
93 | return 0; | ||
94 | |||
95 | if (time_before(jiffies, | ||
96 | shost->last_reset + shost->eh_deadline)) | ||
97 | return 0; | ||
98 | |||
99 | return 1; | ||
100 | } | ||
101 | |||
90 | /** | 102 | /** |
91 | * scsi_eh_scmd_add - add scsi cmd to error handling. | 103 | * scsi_eh_scmd_add - add scsi cmd to error handling. |
92 | * @scmd: scmd to run eh on. | 104 | * @scmd: scmd to run eh on. |
@@ -109,6 +121,9 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) | |||
109 | if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) | 121 | if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) |
110 | goto out_unlock; | 122 | goto out_unlock; |
111 | 123 | ||
124 | if (shost->eh_deadline && !shost->last_reset) | ||
125 | shost->last_reset = jiffies; | ||
126 | |||
112 | ret = 1; | 127 | ret = 1; |
113 | scmd->eh_eflags |= eh_flag; | 128 | scmd->eh_eflags |= eh_flag; |
114 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); | 129 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); |
@@ -138,6 +153,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) | |||
138 | trace_scsi_dispatch_cmd_timeout(scmd); | 153 | trace_scsi_dispatch_cmd_timeout(scmd); |
139 | scsi_log_completion(scmd, TIMEOUT_ERROR); | 154 | scsi_log_completion(scmd, TIMEOUT_ERROR); |
140 | 155 | ||
156 | if (host->eh_deadline && !host->last_reset) | ||
157 | host->last_reset = jiffies; | ||
158 | |||
141 | if (host->transportt->eh_timed_out) | 159 | if (host->transportt->eh_timed_out) |
142 | rtn = host->transportt->eh_timed_out(scmd); | 160 | rtn = host->transportt->eh_timed_out(scmd); |
143 | else if (host->hostt->eh_timed_out) | 161 | else if (host->hostt->eh_timed_out) |
@@ -990,13 +1008,26 @@ int scsi_eh_get_sense(struct list_head *work_q, | |||
990 | struct list_head *done_q) | 1008 | struct list_head *done_q) |
991 | { | 1009 | { |
992 | struct scsi_cmnd *scmd, *next; | 1010 | struct scsi_cmnd *scmd, *next; |
1011 | struct Scsi_Host *shost; | ||
993 | int rtn; | 1012 | int rtn; |
1013 | unsigned long flags; | ||
994 | 1014 | ||
995 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { | 1015 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { |
996 | if ((scmd->eh_eflags & SCSI_EH_CANCEL_CMD) || | 1016 | if ((scmd->eh_eflags & SCSI_EH_CANCEL_CMD) || |
997 | SCSI_SENSE_VALID(scmd)) | 1017 | SCSI_SENSE_VALID(scmd)) |
998 | continue; | 1018 | continue; |
999 | 1019 | ||
1020 | shost = scmd->device->host; | ||
1021 | spin_lock_irqsave(shost->host_lock, flags); | ||
1022 | if (scsi_host_eh_past_deadline(shost)) { | ||
1023 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1024 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1025 | shost_printk(KERN_INFO, shost, | ||
1026 | "skip %s, past eh deadline\n", | ||
1027 | __func__)); | ||
1028 | break; | ||
1029 | } | ||
1030 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1000 | SCSI_LOG_ERROR_RECOVERY(2, scmd_printk(KERN_INFO, scmd, | 1031 | SCSI_LOG_ERROR_RECOVERY(2, scmd_printk(KERN_INFO, scmd, |
1001 | "%s: requesting sense\n", | 1032 | "%s: requesting sense\n", |
1002 | current->comm)); | 1033 | current->comm)); |
@@ -1082,11 +1113,28 @@ static int scsi_eh_test_devices(struct list_head *cmd_list, | |||
1082 | struct scsi_cmnd *scmd, *next; | 1113 | struct scsi_cmnd *scmd, *next; |
1083 | struct scsi_device *sdev; | 1114 | struct scsi_device *sdev; |
1084 | int finish_cmds; | 1115 | int finish_cmds; |
1116 | unsigned long flags; | ||
1085 | 1117 | ||
1086 | while (!list_empty(cmd_list)) { | 1118 | while (!list_empty(cmd_list)) { |
1087 | scmd = list_entry(cmd_list->next, struct scsi_cmnd, eh_entry); | 1119 | scmd = list_entry(cmd_list->next, struct scsi_cmnd, eh_entry); |
1088 | sdev = scmd->device; | 1120 | sdev = scmd->device; |
1089 | 1121 | ||
1122 | if (!try_stu) { | ||
1123 | spin_lock_irqsave(sdev->host->host_lock, flags); | ||
1124 | if (scsi_host_eh_past_deadline(sdev->host)) { | ||
1125 | /* Push items back onto work_q */ | ||
1126 | list_splice_init(cmd_list, work_q); | ||
1127 | spin_unlock_irqrestore(sdev->host->host_lock, | ||
1128 | flags); | ||
1129 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1130 | shost_printk(KERN_INFO, sdev->host, | ||
1131 | "skip %s, past eh deadline", | ||
1132 | __func__)); | ||
1133 | break; | ||
1134 | } | ||
1135 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
1136 | } | ||
1137 | |||
1090 | finish_cmds = !scsi_device_online(scmd->device) || | 1138 | finish_cmds = !scsi_device_online(scmd->device) || |
1091 | (try_stu && !scsi_eh_try_stu(scmd) && | 1139 | (try_stu && !scsi_eh_try_stu(scmd) && |
1092 | !scsi_eh_tur(scmd)) || | 1140 | !scsi_eh_tur(scmd)) || |
@@ -1122,26 +1170,42 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, | |||
1122 | struct scsi_cmnd *scmd, *next; | 1170 | struct scsi_cmnd *scmd, *next; |
1123 | LIST_HEAD(check_list); | 1171 | LIST_HEAD(check_list); |
1124 | int rtn; | 1172 | int rtn; |
1173 | struct Scsi_Host *shost; | ||
1174 | unsigned long flags; | ||
1125 | 1175 | ||
1126 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { | 1176 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { |
1127 | if (!(scmd->eh_eflags & SCSI_EH_CANCEL_CMD)) | 1177 | if (!(scmd->eh_eflags & SCSI_EH_CANCEL_CMD)) |
1128 | continue; | 1178 | continue; |
1179 | shost = scmd->device->host; | ||
1180 | spin_lock_irqsave(shost->host_lock, flags); | ||
1181 | if (scsi_host_eh_past_deadline(shost)) { | ||
1182 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1183 | list_splice_init(&check_list, work_q); | ||
1184 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1185 | shost_printk(KERN_INFO, shost, | ||
1186 | "skip %s, past eh deadline\n", | ||
1187 | __func__)); | ||
1188 | return list_empty(work_q); | ||
1189 | } | ||
1190 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1129 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:" | 1191 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:" |
1130 | "0x%p\n", current->comm, | 1192 | "0x%p\n", current->comm, |
1131 | scmd)); | 1193 | scmd)); |
1132 | rtn = scsi_try_to_abort_cmd(scmd->device->host->hostt, scmd); | 1194 | rtn = scsi_try_to_abort_cmd(shost->hostt, scmd); |
1133 | if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { | 1195 | if (rtn == FAILED) { |
1134 | scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD; | ||
1135 | if (rtn == FAST_IO_FAIL) | ||
1136 | scsi_eh_finish_cmd(scmd, done_q); | ||
1137 | else | ||
1138 | list_move_tail(&scmd->eh_entry, &check_list); | ||
1139 | } else | ||
1140 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting" | 1196 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting" |
1141 | " cmd failed:" | 1197 | " cmd failed:" |
1142 | "0x%p\n", | 1198 | "0x%p\n", |
1143 | current->comm, | 1199 | current->comm, |
1144 | scmd)); | 1200 | scmd)); |
1201 | list_splice_init(&check_list, work_q); | ||
1202 | return list_empty(work_q); | ||
1203 | } | ||
1204 | scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD; | ||
1205 | if (rtn == FAST_IO_FAIL) | ||
1206 | scsi_eh_finish_cmd(scmd, done_q); | ||
1207 | else | ||
1208 | list_move_tail(&scmd->eh_entry, &check_list); | ||
1145 | } | 1209 | } |
1146 | 1210 | ||
1147 | return scsi_eh_test_devices(&check_list, work_q, done_q, 0); | 1211 | return scsi_eh_test_devices(&check_list, work_q, done_q, 0); |
@@ -1187,8 +1251,19 @@ static int scsi_eh_stu(struct Scsi_Host *shost, | |||
1187 | { | 1251 | { |
1188 | struct scsi_cmnd *scmd, *stu_scmd, *next; | 1252 | struct scsi_cmnd *scmd, *stu_scmd, *next; |
1189 | struct scsi_device *sdev; | 1253 | struct scsi_device *sdev; |
1254 | unsigned long flags; | ||
1190 | 1255 | ||
1191 | shost_for_each_device(sdev, shost) { | 1256 | shost_for_each_device(sdev, shost) { |
1257 | spin_lock_irqsave(shost->host_lock, flags); | ||
1258 | if (scsi_host_eh_past_deadline(shost)) { | ||
1259 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1260 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1261 | shost_printk(KERN_INFO, shost, | ||
1262 | "skip %s, past eh deadline\n", | ||
1263 | __func__)); | ||
1264 | break; | ||
1265 | } | ||
1266 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1192 | stu_scmd = NULL; | 1267 | stu_scmd = NULL; |
1193 | list_for_each_entry(scmd, work_q, eh_entry) | 1268 | list_for_each_entry(scmd, work_q, eh_entry) |
1194 | if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) && | 1269 | if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) && |
@@ -1241,9 +1316,20 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, | |||
1241 | { | 1316 | { |
1242 | struct scsi_cmnd *scmd, *bdr_scmd, *next; | 1317 | struct scsi_cmnd *scmd, *bdr_scmd, *next; |
1243 | struct scsi_device *sdev; | 1318 | struct scsi_device *sdev; |
1319 | unsigned long flags; | ||
1244 | int rtn; | 1320 | int rtn; |
1245 | 1321 | ||
1246 | shost_for_each_device(sdev, shost) { | 1322 | shost_for_each_device(sdev, shost) { |
1323 | spin_lock_irqsave(shost->host_lock, flags); | ||
1324 | if (scsi_host_eh_past_deadline(shost)) { | ||
1325 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1326 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1327 | shost_printk(KERN_INFO, shost, | ||
1328 | "skip %s, past eh deadline\n", | ||
1329 | __func__)); | ||
1330 | break; | ||
1331 | } | ||
1332 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1247 | bdr_scmd = NULL; | 1333 | bdr_scmd = NULL; |
1248 | list_for_each_entry(scmd, work_q, eh_entry) | 1334 | list_for_each_entry(scmd, work_q, eh_entry) |
1249 | if (scmd->device == sdev) { | 1335 | if (scmd->device == sdev) { |
@@ -1303,6 +1389,21 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, | |||
1303 | struct scsi_cmnd *next, *scmd; | 1389 | struct scsi_cmnd *next, *scmd; |
1304 | int rtn; | 1390 | int rtn; |
1305 | unsigned int id; | 1391 | unsigned int id; |
1392 | unsigned long flags; | ||
1393 | |||
1394 | spin_lock_irqsave(shost->host_lock, flags); | ||
1395 | if (scsi_host_eh_past_deadline(shost)) { | ||
1396 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1397 | /* push back on work queue for further processing */ | ||
1398 | list_splice_init(&check_list, work_q); | ||
1399 | list_splice_init(&tmp_list, work_q); | ||
1400 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1401 | shost_printk(KERN_INFO, shost, | ||
1402 | "skip %s, past eh deadline\n", | ||
1403 | __func__)); | ||
1404 | return list_empty(work_q); | ||
1405 | } | ||
1406 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1306 | 1407 | ||
1307 | scmd = list_entry(tmp_list.next, struct scsi_cmnd, eh_entry); | 1408 | scmd = list_entry(tmp_list.next, struct scsi_cmnd, eh_entry); |
1308 | id = scmd_id(scmd); | 1409 | id = scmd_id(scmd); |
@@ -1347,6 +1448,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, | |||
1347 | LIST_HEAD(check_list); | 1448 | LIST_HEAD(check_list); |
1348 | unsigned int channel; | 1449 | unsigned int channel; |
1349 | int rtn; | 1450 | int rtn; |
1451 | unsigned long flags; | ||
1350 | 1452 | ||
1351 | /* | 1453 | /* |
1352 | * we really want to loop over the various channels, and do this on | 1454 | * we really want to loop over the various channels, and do this on |
@@ -1356,6 +1458,18 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, | |||
1356 | */ | 1458 | */ |
1357 | 1459 | ||
1358 | for (channel = 0; channel <= shost->max_channel; channel++) { | 1460 | for (channel = 0; channel <= shost->max_channel; channel++) { |
1461 | spin_lock_irqsave(shost->host_lock, flags); | ||
1462 | if (scsi_host_eh_past_deadline(shost)) { | ||
1463 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1464 | list_splice_init(&check_list, work_q); | ||
1465 | SCSI_LOG_ERROR_RECOVERY(3, | ||
1466 | shost_printk(KERN_INFO, shost, | ||
1467 | "skip %s, past eh deadline\n", | ||
1468 | __func__)); | ||
1469 | return list_empty(work_q); | ||
1470 | } | ||
1471 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1472 | |||
1359 | chan_scmd = NULL; | 1473 | chan_scmd = NULL; |
1360 | list_for_each_entry(scmd, work_q, eh_entry) { | 1474 | list_for_each_entry(scmd, work_q, eh_entry) { |
1361 | if (channel == scmd_channel(scmd)) { | 1475 | if (channel == scmd_channel(scmd)) { |
@@ -1755,8 +1869,9 @@ static void scsi_restart_operations(struct Scsi_Host *shost) | |||
1755 | * will be requests for character device operations, and also for | 1869 | * will be requests for character device operations, and also for |
1756 | * ioctls to queued block devices. | 1870 | * ioctls to queued block devices. |
1757 | */ | 1871 | */ |
1758 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", | 1872 | SCSI_LOG_ERROR_RECOVERY(3, |
1759 | __func__)); | 1873 | printk("scsi_eh_%d waking up host to restart\n", |
1874 | shost->host_no)); | ||
1760 | 1875 | ||
1761 | spin_lock_irqsave(shost->host_lock, flags); | 1876 | spin_lock_irqsave(shost->host_lock, flags); |
1762 | if (scsi_host_set_state(shost, SHOST_RUNNING)) | 1877 | if (scsi_host_set_state(shost, SHOST_RUNNING)) |
@@ -1883,6 +1998,10 @@ static void scsi_unjam_host(struct Scsi_Host *shost) | |||
1883 | if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q)) | 1998 | if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q)) |
1884 | scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q); | 1999 | scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q); |
1885 | 2000 | ||
2001 | spin_lock_irqsave(shost->host_lock, flags); | ||
2002 | if (shost->eh_deadline) | ||
2003 | shost->last_reset = 0; | ||
2004 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1886 | scsi_eh_flush_done_q(&eh_done_q); | 2005 | scsi_eh_flush_done_q(&eh_done_q); |
1887 | } | 2006 | } |
1888 | 2007 | ||
@@ -1909,7 +2028,7 @@ int scsi_error_handler(void *data) | |||
1909 | if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || | 2028 | if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || |
1910 | shost->host_failed != shost->host_busy) { | 2029 | shost->host_failed != shost->host_busy) { |
1911 | SCSI_LOG_ERROR_RECOVERY(1, | 2030 | SCSI_LOG_ERROR_RECOVERY(1, |
1912 | printk("Error handler scsi_eh_%d sleeping\n", | 2031 | printk("scsi_eh_%d: sleeping\n", |
1913 | shost->host_no)); | 2032 | shost->host_no)); |
1914 | schedule(); | 2033 | schedule(); |
1915 | continue; | 2034 | continue; |
@@ -1917,8 +2036,9 @@ int scsi_error_handler(void *data) | |||
1917 | 2036 | ||
1918 | __set_current_state(TASK_RUNNING); | 2037 | __set_current_state(TASK_RUNNING); |
1919 | SCSI_LOG_ERROR_RECOVERY(1, | 2038 | SCSI_LOG_ERROR_RECOVERY(1, |
1920 | printk("Error handler scsi_eh_%d waking up\n", | 2039 | printk("scsi_eh_%d: waking up %d/%d/%d\n", |
1921 | shost->host_no)); | 2040 | shost->host_no, shost->host_eh_scheduled, |
2041 | shost->host_failed, shost->host_busy)); | ||
1922 | 2042 | ||
1923 | /* | 2043 | /* |
1924 | * We have a host that is failing for some reason. Figure out | 2044 | * We have a host that is failing for some reason. Figure out |