diff options
Diffstat (limited to 'drivers/scsi/libata-eh.c')
-rw-r--r-- | drivers/scsi/libata-eh.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c new file mode 100644 index 000000000000..e73f5612aea8 --- /dev/null +++ b/drivers/scsi/libata-eh.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * libata-eh.c - libata error handling | ||
3 | * | ||
4 | * Maintained by: Jeff Garzik <jgarzik@pobox.com> | ||
5 | * Please ALWAYS copy linux-ide@vger.kernel.org | ||
6 | * on emails. | ||
7 | * | ||
8 | * Copyright 2006 Tejun Heo <htejun@gmail.com> | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License as | ||
13 | * published by the Free Software Foundation; either version 2, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; see the file COPYING. If not, write to | ||
23 | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
24 | * USA. | ||
25 | * | ||
26 | * | ||
27 | * libata documentation is available via 'make {ps|pdf}docs', | ||
28 | * as Documentation/DocBook/libata.* | ||
29 | * | ||
30 | * Hardware documentation available from http://www.t13.org/ and | ||
31 | * http://www.sata-io.org/ | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | #include <linux/config.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <scsi/scsi.h> | ||
38 | #include <scsi/scsi_host.h> | ||
39 | #include <scsi/scsi_eh.h> | ||
40 | #include <scsi/scsi_device.h> | ||
41 | #include <scsi/scsi_cmnd.h> | ||
42 | |||
43 | #include <linux/libata.h> | ||
44 | |||
45 | #include "libata.h" | ||
46 | |||
47 | /** | ||
48 | * ata_scsi_timed_out - SCSI layer time out callback | ||
49 | * @cmd: timed out SCSI command | ||
50 | * | ||
51 | * Handles SCSI layer timeout. We race with normal completion of | ||
52 | * the qc for @cmd. If the qc is already gone, we lose and let | ||
53 | * the scsi command finish (EH_HANDLED). Otherwise, the qc has | ||
54 | * timed out and EH should be invoked. Prevent ata_qc_complete() | ||
55 | * from finishing it by setting EH_SCHEDULED and return | ||
56 | * EH_NOT_HANDLED. | ||
57 | * | ||
58 | * LOCKING: | ||
59 | * Called from timer context | ||
60 | * | ||
61 | * RETURNS: | ||
62 | * EH_HANDLED or EH_NOT_HANDLED | ||
63 | */ | ||
64 | enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) | ||
65 | { | ||
66 | struct Scsi_Host *host = cmd->device->host; | ||
67 | struct ata_port *ap = (struct ata_port *) &host->hostdata[0]; | ||
68 | unsigned long flags; | ||
69 | struct ata_queued_cmd *qc; | ||
70 | enum scsi_eh_timer_return ret = EH_HANDLED; | ||
71 | |||
72 | DPRINTK("ENTER\n"); | ||
73 | |||
74 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
75 | qc = ata_qc_from_tag(ap, ap->active_tag); | ||
76 | if (qc) { | ||
77 | WARN_ON(qc->scsicmd != cmd); | ||
78 | qc->flags |= ATA_QCFLAG_EH_SCHEDULED; | ||
79 | qc->err_mask |= AC_ERR_TIMEOUT; | ||
80 | ret = EH_NOT_HANDLED; | ||
81 | } | ||
82 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
83 | |||
84 | DPRINTK("EXIT, ret=%d\n", ret); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * ata_scsi_error - SCSI layer error handler callback | ||
90 | * @host: SCSI host on which error occurred | ||
91 | * | ||
92 | * Handles SCSI-layer-thrown error events. | ||
93 | * | ||
94 | * LOCKING: | ||
95 | * Inherited from SCSI layer (none, can sleep) | ||
96 | * | ||
97 | * RETURNS: | ||
98 | * Zero. | ||
99 | */ | ||
100 | int ata_scsi_error(struct Scsi_Host *host) | ||
101 | { | ||
102 | struct ata_port *ap = (struct ata_port *)&host->hostdata[0]; | ||
103 | |||
104 | DPRINTK("ENTER\n"); | ||
105 | |||
106 | /* synchronize with IRQ handler and port task */ | ||
107 | spin_unlock_wait(&ap->host_set->lock); | ||
108 | ata_port_flush_task(ap); | ||
109 | |||
110 | WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL); | ||
111 | |||
112 | ap->ops->eng_timeout(ap); | ||
113 | |||
114 | WARN_ON(host->host_failed || !list_empty(&host->eh_cmd_q)); | ||
115 | |||
116 | scsi_eh_flush_done_q(&ap->eh_done_q); | ||
117 | |||
118 | DPRINTK("EXIT\n"); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * ata_qc_timeout - Handle timeout of queued command | ||
124 | * @qc: Command that timed out | ||
125 | * | ||
126 | * Some part of the kernel (currently, only the SCSI layer) | ||
127 | * has noticed that the active command on port @ap has not | ||
128 | * completed after a specified length of time. Handle this | ||
129 | * condition by disabling DMA (if necessary) and completing | ||
130 | * transactions, with error if necessary. | ||
131 | * | ||
132 | * This also handles the case of the "lost interrupt", where | ||
133 | * for some reason (possibly hardware bug, possibly driver bug) | ||
134 | * an interrupt was not delivered to the driver, even though the | ||
135 | * transaction completed successfully. | ||
136 | * | ||
137 | * LOCKING: | ||
138 | * Inherited from SCSI layer (none, can sleep) | ||
139 | */ | ||
140 | static void ata_qc_timeout(struct ata_queued_cmd *qc) | ||
141 | { | ||
142 | struct ata_port *ap = qc->ap; | ||
143 | struct ata_host_set *host_set = ap->host_set; | ||
144 | u8 host_stat = 0, drv_stat; | ||
145 | unsigned long flags; | ||
146 | |||
147 | DPRINTK("ENTER\n"); | ||
148 | |||
149 | ap->hsm_task_state = HSM_ST_IDLE; | ||
150 | |||
151 | spin_lock_irqsave(&host_set->lock, flags); | ||
152 | |||
153 | switch (qc->tf.protocol) { | ||
154 | |||
155 | case ATA_PROT_DMA: | ||
156 | case ATA_PROT_ATAPI_DMA: | ||
157 | host_stat = ap->ops->bmdma_status(ap); | ||
158 | |||
159 | /* before we do anything else, clear DMA-Start bit */ | ||
160 | ap->ops->bmdma_stop(qc); | ||
161 | |||
162 | /* fall through */ | ||
163 | |||
164 | default: | ||
165 | ata_altstatus(ap); | ||
166 | drv_stat = ata_chk_status(ap); | ||
167 | |||
168 | /* ack bmdma irq events */ | ||
169 | ap->ops->irq_clear(ap); | ||
170 | |||
171 | printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n", | ||
172 | ap->id, qc->tf.command, drv_stat, host_stat); | ||
173 | |||
174 | /* complete taskfile transaction */ | ||
175 | qc->err_mask |= ac_err_mask(drv_stat); | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | spin_unlock_irqrestore(&host_set->lock, flags); | ||
180 | |||
181 | ata_eh_qc_complete(qc); | ||
182 | |||
183 | DPRINTK("EXIT\n"); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * ata_eng_timeout - Handle timeout of queued command | ||
188 | * @ap: Port on which timed-out command is active | ||
189 | * | ||
190 | * Some part of the kernel (currently, only the SCSI layer) | ||
191 | * has noticed that the active command on port @ap has not | ||
192 | * completed after a specified length of time. Handle this | ||
193 | * condition by disabling DMA (if necessary) and completing | ||
194 | * transactions, with error if necessary. | ||
195 | * | ||
196 | * This also handles the case of the "lost interrupt", where | ||
197 | * for some reason (possibly hardware bug, possibly driver bug) | ||
198 | * an interrupt was not delivered to the driver, even though the | ||
199 | * transaction completed successfully. | ||
200 | * | ||
201 | * LOCKING: | ||
202 | * Inherited from SCSI layer (none, can sleep) | ||
203 | */ | ||
204 | void ata_eng_timeout(struct ata_port *ap) | ||
205 | { | ||
206 | DPRINTK("ENTER\n"); | ||
207 | |||
208 | ata_qc_timeout(ata_qc_from_tag(ap, ap->active_tag)); | ||
209 | |||
210 | DPRINTK("EXIT\n"); | ||
211 | } | ||
212 | |||
213 | static void ata_eh_scsidone(struct scsi_cmnd *scmd) | ||
214 | { | ||
215 | /* nada */ | ||
216 | } | ||
217 | |||
218 | static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) | ||
219 | { | ||
220 | struct ata_port *ap = qc->ap; | ||
221 | struct scsi_cmnd *scmd = qc->scsicmd; | ||
222 | unsigned long flags; | ||
223 | |||
224 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
225 | qc->scsidone = ata_eh_scsidone; | ||
226 | __ata_qc_complete(qc); | ||
227 | WARN_ON(ata_tag_valid(qc->tag)); | ||
228 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
229 | |||
230 | scsi_eh_finish_cmd(scmd, &ap->eh_done_q); | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * ata_eh_qc_complete - Complete an active ATA command from EH | ||
235 | * @qc: Command to complete | ||
236 | * | ||
237 | * Indicate to the mid and upper layers that an ATA command has | ||
238 | * completed. To be used from EH. | ||
239 | */ | ||
240 | void ata_eh_qc_complete(struct ata_queued_cmd *qc) | ||
241 | { | ||
242 | struct scsi_cmnd *scmd = qc->scsicmd; | ||
243 | scmd->retries = scmd->allowed; | ||
244 | __ata_eh_qc_complete(qc); | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH | ||
249 | * @qc: Command to retry | ||
250 | * | ||
251 | * Indicate to the mid and upper layers that an ATA command | ||
252 | * should be retried. To be used from EH. | ||
253 | * | ||
254 | * SCSI midlayer limits the number of retries to scmd->allowed. | ||
255 | * scmd->retries is decremented for commands which get retried | ||
256 | * due to unrelated failures (qc->err_mask is zero). | ||
257 | */ | ||
258 | void ata_eh_qc_retry(struct ata_queued_cmd *qc) | ||
259 | { | ||
260 | struct scsi_cmnd *scmd = qc->scsicmd; | ||
261 | if (!qc->err_mask && scmd->retries) | ||
262 | scmd->retries--; | ||
263 | __ata_eh_qc_complete(qc); | ||
264 | } | ||