diff options
-rw-r--r-- | drivers/scsi/scsi.c | 6 | ||||
-rw-r--r-- | drivers/scsi/scsi_debug.c | 4 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 12 | ||||
-rw-r--r-- | drivers/scsi/sd.c | 82 | ||||
-rw-r--r-- | drivers/scsi/sd.h | 35 | ||||
-rw-r--r-- | include/scsi/scsi_cmnd.h | 6 | ||||
-rw-r--r-- | include/scsi/scsi_driver.h | 1 |
7 files changed, 137 insertions, 9 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2aeb2e9c4d3b..07322ecff90d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -782,12 +782,6 @@ static void scsi_done(struct scsi_cmnd *cmd) | |||
782 | blk_complete_request(cmd->request); | 782 | blk_complete_request(cmd->request); |
783 | } | 783 | } |
784 | 784 | ||
785 | /* Move this to a header if it becomes more generally useful */ | ||
786 | static struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) | ||
787 | { | ||
788 | return *(struct scsi_driver **)cmd->request->rq_disk->private_data; | ||
789 | } | ||
790 | |||
791 | /** | 785 | /** |
792 | * scsi_finish_command - cleanup and pass command back to upper layer | 786 | * scsi_finish_command - cleanup and pass command back to upper layer |
793 | * @cmd: the command | 787 | * @cmd: the command |
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index d2fd0efca565..8917154d96c7 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c | |||
@@ -126,6 +126,7 @@ static const char * scsi_debug_version_date = "20100324"; | |||
126 | #define SCSI_DEBUG_OPT_TRANSPORT_ERR 16 | 126 | #define SCSI_DEBUG_OPT_TRANSPORT_ERR 16 |
127 | #define SCSI_DEBUG_OPT_DIF_ERR 32 | 127 | #define SCSI_DEBUG_OPT_DIF_ERR 32 |
128 | #define SCSI_DEBUG_OPT_DIX_ERR 64 | 128 | #define SCSI_DEBUG_OPT_DIX_ERR 64 |
129 | #define SCSI_DEBUG_OPT_MAC_TIMEOUT 128 | ||
129 | /* When "every_nth" > 0 then modulo "every_nth" commands: | 130 | /* When "every_nth" > 0 then modulo "every_nth" commands: |
130 | * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set | 131 | * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set |
131 | * - a RECOVERED_ERROR is simulated on successful read and write | 132 | * - a RECOVERED_ERROR is simulated on successful read and write |
@@ -3615,6 +3616,9 @@ int scsi_debug_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done) | |||
3615 | scsi_debug_every_nth = -1; | 3616 | scsi_debug_every_nth = -1; |
3616 | if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) | 3617 | if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) |
3617 | return 0; /* ignore command causing timeout */ | 3618 | return 0; /* ignore command causing timeout */ |
3619 | else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts && | ||
3620 | scsi_medium_access_command(SCpnt)) | ||
3621 | return 0; /* time out reads and writes */ | ||
3618 | else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) | 3622 | else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) |
3619 | inj_recovered = 1; /* to reads and writes below */ | 3623 | inj_recovered = 1; /* to reads and writes below */ |
3620 | else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts) | 3624 | else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts) |
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index f66e90db3bee..2cfcbffa41fd 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <scsi/scsi_cmnd.h> | 30 | #include <scsi/scsi_cmnd.h> |
31 | #include <scsi/scsi_dbg.h> | 31 | #include <scsi/scsi_dbg.h> |
32 | #include <scsi/scsi_device.h> | 32 | #include <scsi/scsi_device.h> |
33 | #include <scsi/scsi_driver.h> | ||
33 | #include <scsi/scsi_eh.h> | 34 | #include <scsi/scsi_eh.h> |
34 | #include <scsi/scsi_transport.h> | 35 | #include <scsi/scsi_transport.h> |
35 | #include <scsi/scsi_host.h> | 36 | #include <scsi/scsi_host.h> |
@@ -141,11 +142,11 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) | |||
141 | else if (host->hostt->eh_timed_out) | 142 | else if (host->hostt->eh_timed_out) |
142 | rtn = host->hostt->eh_timed_out(scmd); | 143 | rtn = host->hostt->eh_timed_out(scmd); |
143 | 144 | ||
145 | scmd->result |= DID_TIME_OUT << 16; | ||
146 | |||
144 | if (unlikely(rtn == BLK_EH_NOT_HANDLED && | 147 | if (unlikely(rtn == BLK_EH_NOT_HANDLED && |
145 | !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { | 148 | !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) |
146 | scmd->result |= DID_TIME_OUT << 16; | ||
147 | rtn = BLK_EH_HANDLED; | 149 | rtn = BLK_EH_HANDLED; |
148 | } | ||
149 | 150 | ||
150 | return rtn; | 151 | return rtn; |
151 | } | 152 | } |
@@ -778,6 +779,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
778 | int cmnd_size, int timeout, unsigned sense_bytes) | 779 | int cmnd_size, int timeout, unsigned sense_bytes) |
779 | { | 780 | { |
780 | struct scsi_device *sdev = scmd->device; | 781 | struct scsi_device *sdev = scmd->device; |
782 | struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd); | ||
781 | struct Scsi_Host *shost = sdev->host; | 783 | struct Scsi_Host *shost = sdev->host; |
782 | DECLARE_COMPLETION_ONSTACK(done); | 784 | DECLARE_COMPLETION_ONSTACK(done); |
783 | unsigned long timeleft; | 785 | unsigned long timeleft; |
@@ -832,6 +834,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
832 | } | 834 | } |
833 | 835 | ||
834 | scsi_eh_restore_cmnd(scmd, &ses); | 836 | scsi_eh_restore_cmnd(scmd, &ses); |
837 | |||
838 | if (sdrv->eh_action) | ||
839 | rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn); | ||
840 | |||
835 | return rtn; | 841 | return rtn; |
836 | } | 842 | } |
837 | 843 | ||
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8c525aa1b858..bd17cf8af013 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c | |||
@@ -107,6 +107,7 @@ static int sd_suspend(struct device *, pm_message_t state); | |||
107 | static int sd_resume(struct device *); | 107 | static int sd_resume(struct device *); |
108 | static void sd_rescan(struct device *); | 108 | static void sd_rescan(struct device *); |
109 | static int sd_done(struct scsi_cmnd *); | 109 | static int sd_done(struct scsi_cmnd *); |
110 | static int sd_eh_action(struct scsi_cmnd *, unsigned char *, int, int); | ||
110 | static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); | 111 | static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); |
111 | static void scsi_disk_release(struct device *cdev); | 112 | static void scsi_disk_release(struct device *cdev); |
112 | static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *); | 113 | static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *); |
@@ -346,6 +347,31 @@ sd_store_provisioning_mode(struct device *dev, struct device_attribute *attr, | |||
346 | return count; | 347 | return count; |
347 | } | 348 | } |
348 | 349 | ||
350 | static ssize_t | ||
351 | sd_show_max_medium_access_timeouts(struct device *dev, | ||
352 | struct device_attribute *attr, char *buf) | ||
353 | { | ||
354 | struct scsi_disk *sdkp = to_scsi_disk(dev); | ||
355 | |||
356 | return snprintf(buf, 20, "%u\n", sdkp->max_medium_access_timeouts); | ||
357 | } | ||
358 | |||
359 | static ssize_t | ||
360 | sd_store_max_medium_access_timeouts(struct device *dev, | ||
361 | struct device_attribute *attr, | ||
362 | const char *buf, size_t count) | ||
363 | { | ||
364 | struct scsi_disk *sdkp = to_scsi_disk(dev); | ||
365 | int err; | ||
366 | |||
367 | if (!capable(CAP_SYS_ADMIN)) | ||
368 | return -EACCES; | ||
369 | |||
370 | err = kstrtouint(buf, 10, &sdkp->max_medium_access_timeouts); | ||
371 | |||
372 | return err ? err : count; | ||
373 | } | ||
374 | |||
349 | static struct device_attribute sd_disk_attrs[] = { | 375 | static struct device_attribute sd_disk_attrs[] = { |
350 | __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, | 376 | __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, |
351 | sd_store_cache_type), | 377 | sd_store_cache_type), |
@@ -360,6 +386,9 @@ static struct device_attribute sd_disk_attrs[] = { | |||
360 | __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), | 386 | __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), |
361 | __ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode, | 387 | __ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode, |
362 | sd_store_provisioning_mode), | 388 | sd_store_provisioning_mode), |
389 | __ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR, | ||
390 | sd_show_max_medium_access_timeouts, | ||
391 | sd_store_max_medium_access_timeouts), | ||
363 | __ATTR_NULL, | 392 | __ATTR_NULL, |
364 | }; | 393 | }; |
365 | 394 | ||
@@ -382,6 +411,7 @@ static struct scsi_driver sd_template = { | |||
382 | }, | 411 | }, |
383 | .rescan = sd_rescan, | 412 | .rescan = sd_rescan, |
384 | .done = sd_done, | 413 | .done = sd_done, |
414 | .eh_action = sd_eh_action, | ||
385 | }; | 415 | }; |
386 | 416 | ||
387 | /* | 417 | /* |
@@ -1313,6 +1343,55 @@ static const struct block_device_operations sd_fops = { | |||
1313 | .unlock_native_capacity = sd_unlock_native_capacity, | 1343 | .unlock_native_capacity = sd_unlock_native_capacity, |
1314 | }; | 1344 | }; |
1315 | 1345 | ||
1346 | /** | ||
1347 | * sd_eh_action - error handling callback | ||
1348 | * @scmd: sd-issued command that has failed | ||
1349 | * @eh_cmnd: The command that was sent during error handling | ||
1350 | * @eh_cmnd_len: Length of eh_cmnd in bytes | ||
1351 | * @eh_disp: The recovery disposition suggested by the midlayer | ||
1352 | * | ||
1353 | * This function is called by the SCSI midlayer upon completion of | ||
1354 | * an error handling command (TEST UNIT READY, START STOP UNIT, | ||
1355 | * etc.) The command sent to the device by the error handler is | ||
1356 | * stored in eh_cmnd. The result of sending the eh command is | ||
1357 | * passed in eh_disp. | ||
1358 | **/ | ||
1359 | static int sd_eh_action(struct scsi_cmnd *scmd, unsigned char *eh_cmnd, | ||
1360 | int eh_cmnd_len, int eh_disp) | ||
1361 | { | ||
1362 | struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk); | ||
1363 | |||
1364 | if (!scsi_device_online(scmd->device) || | ||
1365 | !scsi_medium_access_command(scmd)) | ||
1366 | return eh_disp; | ||
1367 | |||
1368 | /* | ||
1369 | * The device has timed out executing a medium access command. | ||
1370 | * However, the TEST UNIT READY command sent during error | ||
1371 | * handling completed successfully. Either the device is in the | ||
1372 | * process of recovering or has it suffered an internal failure | ||
1373 | * that prevents access to the storage medium. | ||
1374 | */ | ||
1375 | if (host_byte(scmd->result) == DID_TIME_OUT && eh_disp == SUCCESS && | ||
1376 | eh_cmnd_len && eh_cmnd[0] == TEST_UNIT_READY) | ||
1377 | sdkp->medium_access_timed_out++; | ||
1378 | |||
1379 | /* | ||
1380 | * If the device keeps failing read/write commands but TEST UNIT | ||
1381 | * READY always completes successfully we assume that medium | ||
1382 | * access is no longer possible and take the device offline. | ||
1383 | */ | ||
1384 | if (sdkp->medium_access_timed_out >= sdkp->max_medium_access_timeouts) { | ||
1385 | scmd_printk(KERN_ERR, scmd, | ||
1386 | "Medium access timeout failure. Offlining disk!\n"); | ||
1387 | scsi_device_set_state(scmd->device, SDEV_OFFLINE); | ||
1388 | |||
1389 | return FAILED; | ||
1390 | } | ||
1391 | |||
1392 | return eh_disp; | ||
1393 | } | ||
1394 | |||
1316 | static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) | 1395 | static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) |
1317 | { | 1396 | { |
1318 | u64 start_lba = blk_rq_pos(scmd->request); | 1397 | u64 start_lba = blk_rq_pos(scmd->request); |
@@ -1402,6 +1481,8 @@ static int sd_done(struct scsi_cmnd *SCpnt) | |||
1402 | (!sense_valid || sense_deferred)) | 1481 | (!sense_valid || sense_deferred)) |
1403 | goto out; | 1482 | goto out; |
1404 | 1483 | ||
1484 | sdkp->medium_access_timed_out = 0; | ||
1485 | |||
1405 | switch (sshdr.sense_key) { | 1486 | switch (sshdr.sense_key) { |
1406 | case HARDWARE_ERROR: | 1487 | case HARDWARE_ERROR: |
1407 | case MEDIUM_ERROR: | 1488 | case MEDIUM_ERROR: |
@@ -2523,6 +2604,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie) | |||
2523 | sdkp->RCD = 0; | 2604 | sdkp->RCD = 0; |
2524 | sdkp->ATO = 0; | 2605 | sdkp->ATO = 0; |
2525 | sdkp->first_scan = 1; | 2606 | sdkp->first_scan = 1; |
2607 | sdkp->max_medium_access_timeouts = SD_MAX_MEDIUM_TIMEOUTS; | ||
2526 | 2608 | ||
2527 | sd_revalidate_disk(gd); | 2609 | sd_revalidate_disk(gd); |
2528 | 2610 | ||
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 4163f2910e3d..f703f4827b6f 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #define SD_MAX_RETRIES 5 | 21 | #define SD_MAX_RETRIES 5 |
22 | #define SD_PASSTHROUGH_RETRIES 1 | 22 | #define SD_PASSTHROUGH_RETRIES 1 |
23 | #define SD_MAX_MEDIUM_TIMEOUTS 2 | ||
23 | 24 | ||
24 | /* | 25 | /* |
25 | * Size of the initial data buffer for mode and read capacity data | 26 | * Size of the initial data buffer for mode and read capacity data |
@@ -59,6 +60,8 @@ struct scsi_disk { | |||
59 | u32 unmap_alignment; | 60 | u32 unmap_alignment; |
60 | u32 index; | 61 | u32 index; |
61 | unsigned int physical_block_size; | 62 | unsigned int physical_block_size; |
63 | unsigned int max_medium_access_timeouts; | ||
64 | unsigned int medium_access_timed_out; | ||
62 | u8 media_present; | 65 | u8 media_present; |
63 | u8 write_prot; | 66 | u8 write_prot; |
64 | u8 protection_type;/* Data Integrity Field */ | 67 | u8 protection_type;/* Data Integrity Field */ |
@@ -88,6 +91,38 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) | |||
88 | (sdsk)->disk->disk_name, ##a) : \ | 91 | (sdsk)->disk->disk_name, ##a) : \ |
89 | sdev_printk(prefix, (sdsk)->device, fmt, ##a) | 92 | sdev_printk(prefix, (sdsk)->device, fmt, ##a) |
90 | 93 | ||
94 | static inline int scsi_medium_access_command(struct scsi_cmnd *scmd) | ||
95 | { | ||
96 | switch (scmd->cmnd[0]) { | ||
97 | case READ_6: | ||
98 | case READ_10: | ||
99 | case READ_12: | ||
100 | case READ_16: | ||
101 | case SYNCHRONIZE_CACHE: | ||
102 | case VERIFY: | ||
103 | case VERIFY_12: | ||
104 | case VERIFY_16: | ||
105 | case WRITE_6: | ||
106 | case WRITE_10: | ||
107 | case WRITE_12: | ||
108 | case WRITE_16: | ||
109 | case WRITE_SAME: | ||
110 | case WRITE_SAME_16: | ||
111 | case UNMAP: | ||
112 | return 1; | ||
113 | case VARIABLE_LENGTH_CMD: | ||
114 | switch (scmd->cmnd[9]) { | ||
115 | case READ_32: | ||
116 | case VERIFY_32: | ||
117 | case WRITE_32: | ||
118 | case WRITE_SAME_32: | ||
119 | return 1; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
91 | /* | 126 | /* |
92 | * A DIF-capable target device can be formatted with different | 127 | * A DIF-capable target device can be formatted with different |
93 | * protection schemes. Currently 0 through 3 are defined: | 128 | * protection schemes. Currently 0 through 3 are defined: |
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 9be0128bf303..377df4a28512 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | struct Scsi_Host; | 11 | struct Scsi_Host; |
12 | struct scsi_device; | 12 | struct scsi_device; |
13 | struct scsi_driver; | ||
13 | 14 | ||
14 | /* | 15 | /* |
15 | * MAX_COMMAND_SIZE is: | 16 | * MAX_COMMAND_SIZE is: |
@@ -131,6 +132,11 @@ struct scsi_cmnd { | |||
131 | unsigned char tag; /* SCSI-II queued command tag */ | 132 | unsigned char tag; /* SCSI-II queued command tag */ |
132 | }; | 133 | }; |
133 | 134 | ||
135 | static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) | ||
136 | { | ||
137 | return *(struct scsi_driver **)cmd->request->rq_disk->private_data; | ||
138 | } | ||
139 | |||
134 | extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); | 140 | extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); |
135 | extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); | 141 | extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); |
136 | extern void scsi_put_command(struct scsi_cmnd *); | 142 | extern void scsi_put_command(struct scsi_cmnd *); |
diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 9fd6702f02e2..d443aa06a722 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h | |||
@@ -16,6 +16,7 @@ struct scsi_driver { | |||
16 | 16 | ||
17 | void (*rescan)(struct device *); | 17 | void (*rescan)(struct device *); |
18 | int (*done)(struct scsi_cmnd *); | 18 | int (*done)(struct scsi_cmnd *); |
19 | int (*eh_action)(struct scsi_cmnd *, unsigned char *, int, int); | ||
19 | }; | 20 | }; |
20 | #define to_scsi_driver(drv) \ | 21 | #define to_scsi_driver(drv) \ |
21 | container_of((drv), struct scsi_driver, gendrv) | 22 | container_of((drv), struct scsi_driver, gendrv) |