source: trunk/src/os2ahci/ctxhook.c@ 138

Last change on this file since 138 was 138, checked in by Markus Thielen, 13 years ago

fix for #14

File size: 15.7 KB
Line 
1/******************************************************************************
2 * ctxhook.c - context hooks (kernel thread functions) for os2ahci
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28#include "ata.h"
29#include "atapi.h"
30
31/* -------------------------- macros and constants ------------------------- */
32
33/* ------------------------ typedefs and structures ------------------------ */
34
35/* -------------------------- function prototypes -------------------------- */
36
37/* ------------------------ global/static variables ------------------------ */
38
39/* port restart context hook and input data */
40ULONG restart_ctxhook_h;
41volatile u32 ports_to_restart[MAX_AD];
42
43/* port reset context hook and input data */
44ULONG reset_ctxhook_h;
45ULONG th_reset_watchdog;
46volatile u32 ports_to_reset[MAX_AD];
47IORB_QUEUE abort_queue;
48
49/* trigger engine context hook and input data */
50ULONG engine_ctxhook_h;
51
52/* ----------------------------- start of code ----------------------------- */
53
54/******************************************************************************
55 * Port restart context hook. This context hook is executed at task time and
56 * will handle ports which are stopped due to a device error condition.
57 *
58 * The following conditions may exist:
59 *
60 * - Only a single non-NCQ command is executed by the AHCI adapter at any
61 * given time (even if more are outstanding). This is the case for single
62 * devices or port multipliers without FIS-based command switching. Error
63 * recovery is simple because we know which command has failed and that
64 * all other commands have not yet started executing. Thus, we can requeue
65 * all of them, replacing the failing command with a "request sense"
66 * command to get error details.
67 *
68 * - Multiple non-NCQ commands are executed on different devices behind a
69 * port multiplier which supports FIS-based command switching. This is
70 * more difficult to recover from but currently not an issue because we
71 * don't yet support FIS-based command switching (the FIS receive areas
72 * would become too large for the current data model).
73 *
74 * - One or more NCQ commands were active at the time of the error, with or
75 * without FIS-based command switching. We would have to interrogate the
76 * corresponding devices to find out which command has failed but if this
77 * is combined with FIS-based command switching, even the AHCI spec
78 * recommends to reset the port. This leads to a much simpler approach:
79 * requeue all NCQ commands (they are idempotent per definition, otherwise
80 * they couldn't be reordered by the device) with the 'no_ncq' flag set
81 * in the IORB and reset the port. Then those comands will be executed as
82 * regular commands. The error, if it reoccurs, can then be handled by
83 * one of the above cases.
84 *
85 * The upstream code will guarantee that we will never have a mix of NCQ and
86 * non-NCQ commands active at the same time in order to reduce complexity
87 * in the interrupt and error handlers.
88 */
89void restart_ctxhook(ULONG parm)
90{
91 IORB_QUEUE done_queue;
92 AD_INFO *ai;
93 IORBH _far *problem_iorb;
94 IORBH _far *iorb;
95 IORBH _far *next = NULL;
96 u8 _far *port_mmio;
97 int rearm_ctx_hook = 0;
98 int need_reset;
99 int ccs;
100 int a;
101 int p;
102
103 dprintf("restart_ctxhook() started\n");
104 memset(&done_queue, 0x00, sizeof(done_queue));
105
106 spin_lock(drv_lock);
107
108 for (a = 0; a < ad_info_cnt; a++) {
109 ai = ad_infos + a;
110
111 if (ai->busy) {
112 /* this adapter is busy; leave it alone for now */
113 rearm_ctx_hook = 1;
114 continue;
115 }
116
117 for (p = 0; p <= ai->port_max; p++) {
118 if (ports_to_restart[a] & (1UL << p)) {
119 ports_to_restart[a] &= ~(1UL << p);
120
121 /* restart this port */
122 port_mmio = port_base(ai, p);
123 problem_iorb = NULL;
124 need_reset = 0;
125
126 dprintf("port %d, TF_DATA: 0x%lx\n", p, readl(port_mmio + PORT_TFDATA));
127
128 /* get "current command slot"; only valid if there are no NCQ cmds */
129 ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x1f);
130 ddprintf(" PORT_CMD = 0x%x\n", ccs);
131
132 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
133 ADD_WORKSPACE _far *aws = add_workspace(iorb);
134 next = iorb->pNxtIORB;
135
136 if (aws->queued_hw) {
137 if (ai->ports[p].ncq_cmds & (1UL << aws->cmd_slot)) {
138 /* NCQ command; force non-NCQ mode and trigger port reset */
139 ai->ports[p].ncq_cmds &= ~(1UL << aws->cmd_slot);
140 aws->no_ncq = 1;
141 need_reset = 1;
142 } else {
143 /* regular command; clear cmd bit and identify problem IORB */
144 ai->ports[p].reg_cmds &= ~(1UL << aws->cmd_slot);
145 if (aws->cmd_slot == ccs) {
146 /* this is the non-NCQ command that failed */
147 ddprintf("failing IORB: %Fp\n", iorb);
148 problem_iorb = iorb;
149 }
150 }
151 /* we can requeue all IORBs unconditionally (see function comment) */
152 if (aws->retries++ < MAX_RETRIES) {
153 iorb_requeue(iorb);
154
155 } else {
156 /* retry count exceeded; consider IORB aborted */
157 iorb_seterr(iorb, IOERR_CMD_ABORTED);
158 iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
159 iorb_queue_add(&done_queue, iorb);
160 if (iorb == problem_iorb) {
161 /* no further analysis -- we're done with this one */
162 problem_iorb = NULL;
163 }
164 }
165 }
166 }
167
168 /* sanity check: issued command bitmaps should be 0 now */
169 if (ai->ports[p].ncq_cmds != 0 || ai->ports[p].reg_cmds != 0) {
170 dprintf("warning: commands issued not 0 (%08lx/%08lx); resetting...\n",
171 ai->ports[p].ncq_cmds, ai->ports[p].reg_cmds);
172 need_reset = 1;
173 }
174
175 if (!need_reset) {
176 if ((readl(port_mmio + PORT_TFDATA) & 0x88) != 0) {
177 /* device is not in an idle state */
178 need_reset = 1;
179 }
180 }
181
182 /* restart/reset port */
183 ai->busy = 1;
184 spin_unlock(drv_lock);
185 if (need_reset) {
186 ahci_reset_port(ai, p, 1);
187 } else {
188 ahci_stop_port(ai, p);
189 ahci_start_port(ai, p, 1);
190 }
191 spin_lock(drv_lock);
192 ai->busy = 0;
193
194 /* reset internal port status */
195 ai->ports[p].ncq_cmds = 0;
196 ai->ports[p].reg_cmds = 0;
197 ai->ports[p].cmd_slot = 0;
198
199 if (problem_iorb != NULL) {
200 /* get details about the error that caused this IORB to fail */
201 if (need_reset) {
202 /* no way to retrieve error details after a reset */
203 iorb_seterr(problem_iorb, IOERR_DEVICE_NONSPECIFIC);
204 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
205 iorb_queue_add(&done_queue, problem_iorb);
206
207 } else {
208 /* get sense information */
209 ADD_WORKSPACE _far *aws = add_workspace(problem_iorb);
210 int d = iorb_unit_device(problem_iorb);
211 int (*req_sense)(IORBH _far *, int) = (ai->ports[p].devs[d].atapi) ?
212 atapi_req_sense : ata_req_sense;
213
214 aws->processing = 1;
215 aws->queued_hw = 1;
216
217 if (req_sense(problem_iorb, 0) == 0) {
218 /* execute request sense on slot #0 before anything else comes along */
219 ADD_StartTimerMS(&aws->timer, 5000, (PFN) timeout_callback,
220 problem_iorb, 0);
221 aws->cmd_slot = 0;
222 ai->ports[p].reg_cmds = 1;
223 writel(port_mmio + PORT_CMD_ISSUE, 1);
224 readl(port_mmio); /* flush */
225
226 } else {
227 /* IORB is expected to contain the error code; just move to done queue */
228 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
229 iorb_queue_add(&done_queue, problem_iorb);
230 }
231 }
232 }
233 }
234 }
235 }
236
237 spin_unlock(drv_lock);
238
239 /* call notification routine on all IORBs which have completed */
240 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
241 next = iorb->pNxtIORB;
242
243 spin_lock(drv_lock);
244 aws_free(add_workspace(iorb));
245 spin_unlock(drv_lock);
246
247 iorb_complete(iorb);
248 }
249
250 /* restart engine to resume IORB processing */
251 spin_lock(drv_lock);
252 trigger_engine();
253 spin_unlock(drv_lock);
254
255 dprintf("restart_ctxhook() completed\n");
256
257 /* Check whether we have to rearm ourselves because some adapters were busy
258 * when we wanted to restart ports on them.
259 */
260 if (rearm_ctx_hook) {
261 msleep(250);
262 DevHelp_ArmCtxHook(0, restart_ctxhook_h);
263 }
264}
265
266/******************************************************************************
267 * Reset and abort context hook. This function runs at task time and takes
268 * care of port resets and their side effects. Input to this function are:
269 *
270 * ports_to_reset[] - array of port bitmaps, each bit indicating which port
271 * should be reset unconditionally. This is primarily
272 * used by the error interrupt handler.
273 *
274 * abort_queue - queue with IORBs to be arborted (timed-out, ...) If
275 * any of these commands have reached the hardware, the
276 * corresponding port is reset to interrupt command
277 * execution. This is primarily used for timeout
278 * handling and when IORBs are requested to be aborted.
279 *
280 * After resetting the requested ports, all remaining active IORBs on those
281 * ports have to be retried or aborted. Whether a retry is attempted depends
282 * on the kind of IORB -- those which are idempotent are retried, all others
283 * are aborted. This is different from the port restart hook because the
284 * restart hook can assume it is called with the port in error state, thus
285 * the controller will have stopped executing commands. The reset handler can
286 * be called at any time and we can't tell what's going on in the controller.
287 *
288 * The IORBs in the global abort_queue are expected to have their error code
289 * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
290 * the upstream code might reuse the IORBs before we're done with them.
291 */
292void reset_ctxhook(ULONG parm)
293{
294 IORB_QUEUE done_queue;
295 AD_INFO *ai;
296 IORBH _far *iorb;
297 IORBH _far *next = NULL;
298 int rearm_ctx_hook = 0;
299 int a;
300 int p;
301
302 dprintf("reset_ctxhook() started\n");
303 memset(&done_queue, 0x00, sizeof(done_queue));
304
305 spin_lock(drv_lock);
306
307 if (th_reset_watchdog != 0) {
308 /* watchdog timer still active -- just reset it */
309 ADD_CancelTimer(th_reset_watchdog);
310 th_reset_watchdog = 0;
311 }
312
313 /* add ports of active IORBs from the abort queue to ports_to_reset[] */
314 for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
315 next = iorb->pNxtIORB;
316 a = iorb_unit_adapter(iorb);
317 p = iorb_unit_port(iorb);
318 ai = ad_infos + a;
319
320 if (ai->busy) {
321 /* this adapter is busy; leave it alone for now */
322 rearm_ctx_hook = 1;
323 continue;
324 }
325
326 /* move IORB to the local 'done' queue */
327 iorb_queue_del(&abort_queue, iorb);
328 iorb_queue_add(&done_queue, iorb);
329
330 /* reset port if the IORB has already been queued to hardware */
331 if (add_workspace(iorb)->queued_hw) {
332 /* prepare port reset */
333 ports_to_reset[a] |= (1UL << p);
334 }
335 }
336
337 /* reset all ports in 'ports_to_reset[]' */
338 for (a = 0; a < ad_info_cnt; a++) {
339 ai = ad_infos + a;
340
341 if (ai->busy) {
342 /* this adapter is busy; leave it alone for now */
343 rearm_ctx_hook = 1;
344 continue;
345 }
346
347 for (p = 0; p <= ai->port_max; p++) {
348 if (ports_to_reset[a] & (1UL << p)) {
349 ports_to_reset[a] &= ~(1UL << p);
350
351 /* Reset this port. Since this is a rather slow operation, we'll
352 * release the spinlock while doing so. The adapter is marked as
353 * 'busy' to prevent similar routines (e.g. an ahci port scan) from
354 * interfering.
355 */
356 ai->busy = 1;
357 spin_unlock(drv_lock);
358 ahci_reset_port(ai, p, 1);
359 spin_lock(drv_lock);
360 ai->busy = 0;
361
362 /* reset port status */
363 ai->ports[p].ncq_cmds = 0;
364 ai->ports[p].reg_cmds = 0;
365 ai->ports[p].cmd_slot = 0;
366
367 /* retry or abort all remaining active commands on this port */
368 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
369 ADD_WORKSPACE _far *aws = add_workspace(iorb);
370 next = iorb->pNxtIORB;
371
372 if (aws->queued_hw) {
373 /* this IORB had already been queued to HW when we reset the port */
374 if (aws->idempotent && aws->retries++ < MAX_RETRIES) {
375 /* we can retry this IORB */
376 iorb_requeue(iorb);
377
378 } else {
379 /* we cannot retry this IORB; consider it aborted */
380 iorb->ErrorCode = IOERR_CMD_ABORTED;
381 iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
382 iorb_queue_add(&done_queue, iorb);
383 }
384 }
385 }
386 }
387 }
388 }
389
390 spin_unlock(drv_lock);
391
392 /* complete all aborted IORBs */
393 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
394 next = iorb->pNxtIORB;
395
396 spin_lock(drv_lock);
397 aws_free(add_workspace(iorb));
398 spin_unlock(drv_lock);
399
400 iorb->Status |= IORB_ERROR;
401 iorb_complete(iorb);
402 }
403
404 /* restart engine to resume IORB processing */
405 spin_lock(drv_lock);
406 trigger_engine();
407 spin_unlock(drv_lock);
408
409 dprintf("reset_ctxhook() completed\n");
410
411 /* Check whether we have to rearm ourselves because some adapters were busy
412 * when we wanted to reset ports on them.
413 */
414 if (rearm_ctx_hook) {
415 msleep(250);
416 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
417 }
418}
419
420/******************************************************************************
421 * IORB Engine context hook. This hook is executed if trigger_engine() came
422 * to the conclusion that some of the IORBs keep bouncing, most likely due to
423 * some condition on the adapter such as being busy. It could also be a very
424 * busy system. Either way, this requires some task-time help.
425 */
426void engine_ctxhook(ULONG parm)
427{
428 int iorbs_sent;
429 int i;
430
431 dprintf("engine_ctxhook() started\n");
432
433 spin_lock(drv_lock);
434 for (i = 0; i < 10; i++) {
435 if ((iorbs_sent = trigger_engine_1()) == 0) {
436 break;
437 }
438 }
439 spin_unlock(drv_lock);
440
441 dprintf("engine_ctxhook() completed\n");
442
443 if (iorbs_sent != 0) {
444 /* need to rearm ourselves for another run */
445 msleep(250);
446 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
447 }
448}
449
Note: See TracBrowser for help on using the repository browser.