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

Last change on this file since 209 was 209, checked in by David Azarewicz, 4 years ago

Debugging support changes.

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