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

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