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 |
