diff options
-rw-r--r-- | drivers/scsi/hosts.c | 14 | ||||
-rw-r--r-- | drivers/scsi/scsi.c | 3 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 151 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 2 | ||||
-rw-r--r-- | include/scsi/scsi_cmnd.h | 1 | ||||
-rw-r--r-- | include/scsi/scsi_host.h | 10 |
6 files changed, 167 insertions, 14 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index f2c5005f312a..c3ab093dd8a7 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -169,6 +169,7 @@ void scsi_remove_host(struct Scsi_Host *shost) | |||
169 | spin_unlock_irqrestore(shost->host_lock, flags); | 169 | spin_unlock_irqrestore(shost->host_lock, flags); |
170 | 170 | ||
171 | scsi_autopm_get_host(shost); | 171 | scsi_autopm_get_host(shost); |
172 | flush_workqueue(shost->tmf_work_q); | ||
172 | scsi_forget_host(shost); | 173 | scsi_forget_host(shost); |
173 | mutex_unlock(&shost->scan_mutex); | 174 | mutex_unlock(&shost->scan_mutex); |
174 | scsi_proc_host_rm(shost); | 175 | scsi_proc_host_rm(shost); |
@@ -294,6 +295,8 @@ static void scsi_host_dev_release(struct device *dev) | |||
294 | 295 | ||
295 | scsi_proc_hostdir_rm(shost->hostt); | 296 | scsi_proc_hostdir_rm(shost->hostt); |
296 | 297 | ||
298 | if (shost->tmf_work_q) | ||
299 | destroy_workqueue(shost->tmf_work_q); | ||
297 | if (shost->ehandler) | 300 | if (shost->ehandler) |
298 | kthread_stop(shost->ehandler); | 301 | kthread_stop(shost->ehandler); |
299 | if (shost->work_q) | 302 | if (shost->work_q) |
@@ -360,7 +363,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
360 | INIT_LIST_HEAD(&shost->eh_cmd_q); | 363 | INIT_LIST_HEAD(&shost->eh_cmd_q); |
361 | INIT_LIST_HEAD(&shost->starved_list); | 364 | INIT_LIST_HEAD(&shost->starved_list); |
362 | init_waitqueue_head(&shost->host_wait); | 365 | init_waitqueue_head(&shost->host_wait); |
363 | |||
364 | mutex_init(&shost->scan_mutex); | 366 | mutex_init(&shost->scan_mutex); |
365 | 367 | ||
366 | /* | 368 | /* |
@@ -444,9 +446,19 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
444 | goto fail_kfree; | 446 | goto fail_kfree; |
445 | } | 447 | } |
446 | 448 | ||
449 | shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d", | ||
450 | WQ_UNBOUND | WQ_MEM_RECLAIM, | ||
451 | 1, shost->host_no); | ||
452 | if (!shost->tmf_work_q) { | ||
453 | printk(KERN_WARNING "scsi%d: failed to create tmf workq\n", | ||
454 | shost->host_no); | ||
455 | goto fail_kthread; | ||
456 | } | ||
447 | scsi_proc_hostdir_add(shost->hostt); | 457 | scsi_proc_hostdir_add(shost->hostt); |
448 | return shost; | 458 | return shost; |
449 | 459 | ||
460 | fail_kthread: | ||
461 | kthread_stop(shost->ehandler); | ||
450 | fail_kfree: | 462 | fail_kfree: |
451 | kfree(shost); | 463 | kfree(shost); |
452 | return NULL; | 464 | return NULL; |
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index fe0bcb18fb26..2b04a57e0f4f 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -297,6 +297,7 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) | |||
297 | 297 | ||
298 | cmd->device = dev; | 298 | cmd->device = dev; |
299 | INIT_LIST_HEAD(&cmd->list); | 299 | INIT_LIST_HEAD(&cmd->list); |
300 | INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler); | ||
300 | spin_lock_irqsave(&dev->list_lock, flags); | 301 | spin_lock_irqsave(&dev->list_lock, flags); |
301 | list_add_tail(&cmd->list, &dev->cmd_list); | 302 | list_add_tail(&cmd->list, &dev->cmd_list); |
302 | spin_unlock_irqrestore(&dev->list_lock, flags); | 303 | spin_unlock_irqrestore(&dev->list_lock, flags); |
@@ -353,6 +354,8 @@ void scsi_put_command(struct scsi_cmnd *cmd) | |||
353 | list_del_init(&cmd->list); | 354 | list_del_init(&cmd->list); |
354 | spin_unlock_irqrestore(&cmd->device->list_lock, flags); | 355 | spin_unlock_irqrestore(&cmd->device->list_lock, flags); |
355 | 356 | ||
357 | cancel_delayed_work(&cmd->abort_work); | ||
358 | |||
356 | __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev); | 359 | __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev); |
357 | } | 360 | } |
358 | EXPORT_SYMBOL(scsi_put_command); | 361 | EXPORT_SYMBOL(scsi_put_command); |
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 67c001457cb8..3dd04026d466 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -53,6 +53,8 @@ static void scsi_eh_done(struct scsi_cmnd *scmd); | |||
53 | #define HOST_RESET_SETTLE_TIME (10) | 53 | #define HOST_RESET_SETTLE_TIME (10) |
54 | 54 | ||
55 | static int scsi_eh_try_stu(struct scsi_cmnd *scmd); | 55 | static int scsi_eh_try_stu(struct scsi_cmnd *scmd); |
56 | static int scsi_try_to_abort_cmd(struct scsi_host_template *, | ||
57 | struct scsi_cmnd *); | ||
56 | 58 | ||
57 | /* called with shost->host_lock held */ | 59 | /* called with shost->host_lock held */ |
58 | void scsi_eh_wakeup(struct Scsi_Host *shost) | 60 | void scsi_eh_wakeup(struct Scsi_Host *shost) |
@@ -100,6 +102,116 @@ static int scsi_host_eh_past_deadline(struct Scsi_Host *shost) | |||
100 | } | 102 | } |
101 | 103 | ||
102 | /** | 104 | /** |
105 | * scmd_eh_abort_handler - Handle command aborts | ||
106 | * @work: command to be aborted. | ||
107 | */ | ||
108 | void | ||
109 | scmd_eh_abort_handler(struct work_struct *work) | ||
110 | { | ||
111 | struct scsi_cmnd *scmd = | ||
112 | container_of(work, struct scsi_cmnd, abort_work.work); | ||
113 | struct scsi_device *sdev = scmd->device; | ||
114 | unsigned long flags; | ||
115 | int rtn; | ||
116 | |||
117 | spin_lock_irqsave(sdev->host->host_lock, flags); | ||
118 | if (scsi_host_eh_past_deadline(sdev->host)) { | ||
119 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
120 | SCSI_LOG_ERROR_RECOVERY(3, | ||
121 | scmd_printk(KERN_INFO, scmd, | ||
122 | "scmd %p eh timeout, not aborting\n", | ||
123 | scmd)); | ||
124 | } else { | ||
125 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
126 | SCSI_LOG_ERROR_RECOVERY(3, | ||
127 | scmd_printk(KERN_INFO, scmd, | ||
128 | "aborting command %p\n", scmd)); | ||
129 | rtn = scsi_try_to_abort_cmd(sdev->host->hostt, scmd); | ||
130 | if (rtn == SUCCESS) { | ||
131 | scmd->result |= DID_TIME_OUT << 16; | ||
132 | if (!scsi_noretry_cmd(scmd) && | ||
133 | (++scmd->retries <= scmd->allowed)) { | ||
134 | SCSI_LOG_ERROR_RECOVERY(3, | ||
135 | scmd_printk(KERN_WARNING, scmd, | ||
136 | "scmd %p retry " | ||
137 | "aborted command\n", scmd)); | ||
138 | scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY); | ||
139 | } else { | ||
140 | SCSI_LOG_ERROR_RECOVERY(3, | ||
141 | scmd_printk(KERN_WARNING, scmd, | ||
142 | "scmd %p finish " | ||
143 | "aborted command\n", scmd)); | ||
144 | scsi_finish_command(scmd); | ||
145 | } | ||
146 | return; | ||
147 | } | ||
148 | SCSI_LOG_ERROR_RECOVERY(3, | ||
149 | scmd_printk(KERN_INFO, scmd, | ||
150 | "scmd %p abort failed, rtn %d\n", | ||
151 | scmd, rtn)); | ||
152 | } | ||
153 | |||
154 | if (!scsi_eh_scmd_add(scmd, 0)) { | ||
155 | SCSI_LOG_ERROR_RECOVERY(3, | ||
156 | scmd_printk(KERN_WARNING, scmd, | ||
157 | "scmd %p terminate " | ||
158 | "aborted command\n", scmd)); | ||
159 | scmd->result |= DID_TIME_OUT << 16; | ||
160 | scsi_finish_command(scmd); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * scsi_abort_command - schedule a command abort | ||
166 | * @scmd: scmd to abort. | ||
167 | * | ||
168 | * We only need to abort commands after a command timeout | ||
169 | */ | ||
170 | static int | ||
171 | scsi_abort_command(struct scsi_cmnd *scmd) | ||
172 | { | ||
173 | struct scsi_device *sdev = scmd->device; | ||
174 | struct Scsi_Host *shost = sdev->host; | ||
175 | unsigned long flags; | ||
176 | |||
177 | if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) { | ||
178 | /* | ||
179 | * Retry after abort failed, escalate to next level. | ||
180 | */ | ||
181 | SCSI_LOG_ERROR_RECOVERY(3, | ||
182 | scmd_printk(KERN_INFO, scmd, | ||
183 | "scmd %p previous abort failed\n", scmd)); | ||
184 | cancel_delayed_work(&scmd->abort_work); | ||
185 | return FAILED; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * Do not try a command abort if | ||
190 | * SCSI EH has already started. | ||
191 | */ | ||
192 | spin_lock_irqsave(shost->host_lock, flags); | ||
193 | if (scsi_host_in_recovery(shost)) { | ||
194 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
195 | SCSI_LOG_ERROR_RECOVERY(3, | ||
196 | scmd_printk(KERN_INFO, scmd, | ||
197 | "scmd %p not aborting, host in recovery\n", | ||
198 | scmd)); | ||
199 | return FAILED; | ||
200 | } | ||
201 | |||
202 | if (shost->eh_deadline && !shost->last_reset) | ||
203 | shost->last_reset = jiffies; | ||
204 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
205 | |||
206 | scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED; | ||
207 | SCSI_LOG_ERROR_RECOVERY(3, | ||
208 | scmd_printk(KERN_INFO, scmd, | ||
209 | "scmd %p abort scheduled\n", scmd)); | ||
210 | queue_delayed_work(shost->tmf_work_q, &scmd->abort_work, HZ / 100); | ||
211 | return SUCCESS; | ||
212 | } | ||
213 | |||
214 | /** | ||
103 | * scsi_eh_scmd_add - add scsi cmd to error handling. | 215 | * scsi_eh_scmd_add - add scsi cmd to error handling. |
104 | * @scmd: scmd to run eh on. | 216 | * @scmd: scmd to run eh on. |
105 | * @eh_flag: optional SCSI_EH flag. | 217 | * @eh_flag: optional SCSI_EH flag. |
@@ -125,6 +237,8 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) | |||
125 | shost->last_reset = jiffies; | 237 | shost->last_reset = jiffies; |
126 | 238 | ||
127 | ret = 1; | 239 | ret = 1; |
240 | if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) | ||
241 | eh_flag &= ~SCSI_EH_CANCEL_CMD; | ||
128 | scmd->eh_eflags |= eh_flag; | 242 | scmd->eh_eflags |= eh_flag; |
129 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); | 243 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); |
130 | shost->host_failed++; | 244 | shost->host_failed++; |
@@ -161,6 +275,10 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) | |||
161 | else if (host->hostt->eh_timed_out) | 275 | else if (host->hostt->eh_timed_out) |
162 | rtn = host->hostt->eh_timed_out(scmd); | 276 | rtn = host->hostt->eh_timed_out(scmd); |
163 | 277 | ||
278 | if (rtn == BLK_EH_NOT_HANDLED && !host->hostt->no_async_abort) | ||
279 | if (scsi_abort_command(scmd) == SUCCESS) | ||
280 | return BLK_EH_NOT_HANDLED; | ||
281 | |||
164 | scmd->result |= DID_TIME_OUT << 16; | 282 | scmd->result |= DID_TIME_OUT << 16; |
165 | 283 | ||
166 | if (unlikely(rtn == BLK_EH_NOT_HANDLED && | 284 | if (unlikely(rtn == BLK_EH_NOT_HANDLED && |
@@ -1577,7 +1695,7 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q, | |||
1577 | } | 1695 | } |
1578 | 1696 | ||
1579 | /** | 1697 | /** |
1580 | * scsi_noretry_cmd - determinte if command should be failed fast | 1698 | * scsi_noretry_cmd - determine if command should be failed fast |
1581 | * @scmd: SCSI cmd to examine. | 1699 | * @scmd: SCSI cmd to examine. |
1582 | */ | 1700 | */ |
1583 | int scsi_noretry_cmd(struct scsi_cmnd *scmd) | 1701 | int scsi_noretry_cmd(struct scsi_cmnd *scmd) |
@@ -1585,6 +1703,8 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd) | |||
1585 | switch (host_byte(scmd->result)) { | 1703 | switch (host_byte(scmd->result)) { |
1586 | case DID_OK: | 1704 | case DID_OK: |
1587 | break; | 1705 | break; |
1706 | case DID_TIME_OUT: | ||
1707 | goto check_type; | ||
1588 | case DID_BUS_BUSY: | 1708 | case DID_BUS_BUSY: |
1589 | return (scmd->request->cmd_flags & REQ_FAILFAST_TRANSPORT); | 1709 | return (scmd->request->cmd_flags & REQ_FAILFAST_TRANSPORT); |
1590 | case DID_PARITY: | 1710 | case DID_PARITY: |
@@ -1598,18 +1718,19 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd) | |||
1598 | return (scmd->request->cmd_flags & REQ_FAILFAST_DRIVER); | 1718 | return (scmd->request->cmd_flags & REQ_FAILFAST_DRIVER); |
1599 | } | 1719 | } |
1600 | 1720 | ||
1601 | switch (status_byte(scmd->result)) { | 1721 | if (status_byte(scmd->result) != CHECK_CONDITION) |
1602 | case CHECK_CONDITION: | 1722 | return 0; |
1603 | /* | ||
1604 | * assume caller has checked sense and determinted | ||
1605 | * the check condition was retryable. | ||
1606 | */ | ||
1607 | if (scmd->request->cmd_flags & REQ_FAILFAST_DEV || | ||
1608 | scmd->request->cmd_type == REQ_TYPE_BLOCK_PC) | ||
1609 | return 1; | ||
1610 | } | ||
1611 | 1723 | ||
1612 | return 0; | 1724 | check_type: |
1725 | /* | ||
1726 | * assume caller has checked sense and determined | ||
1727 | * the check condition was retryable. | ||
1728 | */ | ||
1729 | if (scmd->request->cmd_flags & REQ_FAILFAST_DEV || | ||
1730 | scmd->request->cmd_type == REQ_TYPE_BLOCK_PC) | ||
1731 | return 1; | ||
1732 | else | ||
1733 | return 0; | ||
1613 | } | 1734 | } |
1614 | 1735 | ||
1615 | /** | 1736 | /** |
@@ -1659,9 +1780,13 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) | |||
1659 | * looks good. drop through, and check the next byte. | 1780 | * looks good. drop through, and check the next byte. |
1660 | */ | 1781 | */ |
1661 | break; | 1782 | break; |
1783 | case DID_ABORT: | ||
1784 | if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) { | ||
1785 | scmd->result |= DID_TIME_OUT << 16; | ||
1786 | return SUCCESS; | ||
1787 | } | ||
1662 | case DID_NO_CONNECT: | 1788 | case DID_NO_CONNECT: |
1663 | case DID_BAD_TARGET: | 1789 | case DID_BAD_TARGET: |
1664 | case DID_ABORT: | ||
1665 | /* | 1790 | /* |
1666 | * note - this means that we just report the status back | 1791 | * note - this means that we just report the status back |
1667 | * to the top level driver, not that we actually think | 1792 | * to the top level driver, not that we actually think |
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 8f9a0cadc296..f079a598bed4 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h | |||
@@ -19,6 +19,7 @@ struct scsi_nl_hdr; | |||
19 | * Scsi Error Handler Flags | 19 | * Scsi Error Handler Flags |
20 | */ | 20 | */ |
21 | #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */ | 21 | #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */ |
22 | #define SCSI_EH_ABORT_SCHEDULED 0x0002 /* Abort has been scheduled */ | ||
22 | 23 | ||
23 | #define SCSI_SENSE_VALID(scmd) \ | 24 | #define SCSI_SENSE_VALID(scmd) \ |
24 | (((scmd)->sense_buffer[0] & 0x70) == 0x70) | 25 | (((scmd)->sense_buffer[0] & 0x70) == 0x70) |
@@ -66,6 +67,7 @@ extern int __init scsi_init_devinfo(void); | |||
66 | extern void scsi_exit_devinfo(void); | 67 | extern void scsi_exit_devinfo(void); |
67 | 68 | ||
68 | /* scsi_error.c */ | 69 | /* scsi_error.c */ |
70 | extern void scmd_eh_abort_handler(struct work_struct *work); | ||
69 | extern enum blk_eh_timer_return scsi_times_out(struct request *req); | 71 | extern enum blk_eh_timer_return scsi_times_out(struct request *req); |
70 | extern int scsi_error_handler(void *host); | 72 | extern int scsi_error_handler(void *host); |
71 | extern int scsi_decide_disposition(struct scsi_cmnd *cmd); | 73 | extern int scsi_decide_disposition(struct scsi_cmnd *cmd); |
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index de5f5d8f1f8a..91558a1f97f4 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h | |||
@@ -55,6 +55,7 @@ struct scsi_cmnd { | |||
55 | struct scsi_device *device; | 55 | struct scsi_device *device; |
56 | struct list_head list; /* scsi_cmnd participates in queue lists */ | 56 | struct list_head list; /* scsi_cmnd participates in queue lists */ |
57 | struct list_head eh_entry; /* entry for the host eh_cmd_q */ | 57 | struct list_head eh_entry; /* entry for the host eh_cmd_q */ |
58 | struct delayed_work abort_work; | ||
58 | int eh_eflags; /* Used by error handlr */ | 59 | int eh_eflags; /* Used by error handlr */ |
59 | 60 | ||
60 | /* | 61 | /* |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index fe3b58e836c8..53075e5039e6 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
@@ -479,6 +479,11 @@ struct scsi_host_template { | |||
479 | unsigned no_write_same:1; | 479 | unsigned no_write_same:1; |
480 | 480 | ||
481 | /* | 481 | /* |
482 | * True if asynchronous aborts are not supported | ||
483 | */ | ||
484 | unsigned no_async_abort:1; | ||
485 | |||
486 | /* | ||
482 | * Countdown for host blocking with no commands outstanding. | 487 | * Countdown for host blocking with no commands outstanding. |
483 | */ | 488 | */ |
484 | unsigned int max_host_blocked; | 489 | unsigned int max_host_blocked; |
@@ -690,6 +695,11 @@ struct Scsi_Host { | |||
690 | struct workqueue_struct *work_q; | 695 | struct workqueue_struct *work_q; |
691 | 696 | ||
692 | /* | 697 | /* |
698 | * Task management function work queue | ||
699 | */ | ||
700 | struct workqueue_struct *tmf_work_q; | ||
701 | |||
702 | /* | ||
693 | * Host has rejected a command because it was busy. | 703 | * Host has rejected a command because it was busy. |
694 | */ | 704 | */ |
695 | unsigned int host_blocked; | 705 | unsigned int host_blocked; |