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

Last change on this file since 181 was 181, checked in by David Azarewicz, 9 years ago

Debugging changes

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