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

Last change on this file since 73 was 69, checked in by markus, 15 years ago

fixed ATAPI packet commands; added cache flush in shutdown handler; allow write cache for NCQ commands

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)\n",
146 ai->ports[p].ncq_cmds, ai->ports[p].reg_cmds);
147 }
148
149 if (!need_reset) {
150 if ((readl(port_mmio + PORT_TFDATA) & 0x88) != 0) {
151 /* device is not in an idle state */
152 need_reset = 1;
153 }
154 }
155
156 /* restart/reset port */
157 ai->busy = 1;
158 spin_unlock(drv_lock);
159 if (need_reset) {
160 ahci_reset_port(ai, p, 1);
161 } else {
162 ahci_stop_port(ai, p);
163 ahci_start_port(ai, p, 1);
164 }
165 spin_lock(drv_lock);
166 ai->busy = 0;
167
168 /* reset internal port status */
169 ai->ports[p].ncq_cmds = 0;
170 ai->ports[p].reg_cmds = 0;
171 ai->ports[p].cmd_slot = 0;
172
173 if (problem_iorb != NULL) {
174 /* get details about the error that caused this IORB to fail */
175 if (need_reset) {
176 /* no way to retrieve error details after a reset */
177 iorb_seterr(problem_iorb, IOERR_DEVICE_NONSPECIFIC);
178 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
179 iorb_queue_add(&done_queue, problem_iorb);
180
181 } else {
182 /* get sense information */
183 ADD_WORKSPACE _far *aws = add_workspace(problem_iorb);
184 int d = iorb_unit_device(problem_iorb);
185 int (*req_sense)(IORBH _far *, int) =
186 (ai->ports[p].devs[d].atapi) ? atapi_req_sense : ata_req_sense;
187
188 aws->processing = 1;
189 aws->queued_hw = 1;
190
191 if (req_sense(problem_iorb, 0) == 0) {
192 /* execute request sense on slot #0 before anything else comes along */
193 ADD_StartTimerMS(&aws->timer, 5000, (PFN) timeout_callback, iorb, 0);
194 aws->cmd_slot = 0;
195 ai->ports[p].reg_cmds = 1;
196 writel(port_mmio + PORT_CMD_ISSUE, 1);
197 readl(port_mmio); /* flush */
198
199 } else {
200 /* IORB is expected to contain the error code; just move to done queue */
201 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
202 iorb_queue_add(&done_queue, problem_iorb);
203 }
204 }
205 }
206 }
207 }
208 }
209
210 spin_unlock(drv_lock);
211
212 /* call notification routine on all IORBs which have completed */
213 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
214 next = iorb->pNxtIORB;
215 if (iorb->RequestControl & IORB_ASYNC_POST) {
216 iorb->NotifyAddress(iorb);
217 }
218 }
219
220 /* restart engine to resume IORB processing */
221 spin_lock(drv_lock);
222 trigger_engine();
223 spin_unlock(drv_lock);
224
225 dprintf("restart_ctxhook() completed\n");
226
227 /* Check whether we have to rearm ourselves because some adapters were busy
228 * when we wanted to restart ports on them.
229 */
230 if (rearm_ctx_hook) {
231 msleep(250);
232 DevHelp_ArmCtxHook(0, restart_ctxhook_h);
233 }
234}
235
236/******************************************************************************
237 * Reset and abort context hook. This function runs at task time and takes
238 * care of port resets and their side effects. Input to this function are:
239 *
240 * ports_to_reset[] - array of port bitmaps, each bit indicating which port
241 * should be reset unconditionally. This is primarily
242 * used by the error interrupt handler.
243 *
244 * abort_queue - queue with IORBs to be arborted (timed-out, ...) If
245 * any of these commands have reached the hardware, the
246 * corresponding port is reset to interrupt command
247 * execution. This is primarily used for timeout
248 * handling and when IORBs are requested to be aborted.
249 *
250 * After resetting the requested ports, all remaining active IORBs on those
251 * ports have to be retried or aborted. Whether a retry is attempted depends
252 * on the kind of IORB -- those which are idempotent are retried, all others
253 * are aborted. This is different from the port restart hook because the
254 * restart hook can assume it is called with the port in error state, thus
255 * the controller will have stopped executing commands. The reset handler can
256 * be called at any time and we can't tell what's going on in the controller.
257 *
258 * The IORBs in the global abort_queue are expected to have their error code
259 * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
260 * the upstream code might reuse the IORBs before we're done with them.
261 */
262void reset_ctxhook(ULONG parm)
263{
264 IORB_QUEUE done_queue;
265 AD_INFO *ai;
266 IORBH _far *iorb;
267 IORBH _far *next = NULL;
268 int rearm_ctx_hook = 0;
269 int a;
270 int p;
271
272 dprintf("reset_ctxhook() started\n");
273 memset(&done_queue, 0x00, sizeof(done_queue));
274
275 spin_lock(drv_lock);
276
277 /* add ports of active IORBs from the abort queue to ports_to_reset[] */
278 for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
279 next = iorb->pNxtIORB;
280 a = iorb_unit_adapter(iorb);
281 p = iorb_unit_port(iorb);
282 ai = ad_infos + a;
283
284 if (ai->busy) {
285 /* this adapter is busy; leave it alone for now */
286 rearm_ctx_hook = 1;
287 continue;
288 }
289
290 /* move IORB to the local 'done' queue */
291 iorb_queue_del(&abort_queue, iorb);
292 iorb_queue_add(&done_queue, iorb);
293
294 /* reset port if the IORB has already been queued to hardware */
295 if (add_workspace(iorb)->queued_hw) {
296 /* prepare port reset */
297 ports_to_reset[a] |= (1UL << p);
298 }
299 }
300
301 /* reset all ports in 'ports_to_reset[]' */
302 for (a = 0; a < ad_info_cnt; a++) {
303 ai = ad_infos + a;
304
305 if (ai->busy) {
306 /* this adapter is busy; leave it alone for now */
307 rearm_ctx_hook = 1;
308 continue;
309 }
310
311 for (p = 0; p <= ai->port_max; p++) {
312 if (ports_to_reset[a] & (1UL << p)) {
313 ports_to_reset[a] &= ~(1UL << p);
314
315 /* Reset this port. Since this is a rather slow operation, we'll
316 * release the spinlock while doing so. The adapter is marked as
317 * 'busy' to prevent similar routines (e.g. an ahci port scan) from
318 * interfering.
319 */
320 ai->busy = 1;
321 spin_unlock(drv_lock);
322 ahci_reset_port(ai, p, 1);
323 spin_lock(drv_lock);
324 ai->busy = 0;
325
326 /* reset port status */
327 ai->ports[p].ncq_cmds = 0;
328 ai->ports[p].reg_cmds = 0;
329 ai->ports[p].cmd_slot = 0;
330
331 /* retry or abort all remaining active commands on this port */
332 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
333 next = iorb->pNxtIORB;
334 if (add_workspace(iorb)->queued_hw) {
335 /* this IORB had already been queued to HW when we reset the port */
336 if (add_workspace(iorb)->idempotent) {
337 /* We can retry this IORB; just reset its status and it will be
338 * picked up by subsequent trigger_engine() calls.
339 */
340 iorb_requeue(iorb);
341
342 } else {
343 /* we cannot retry this IORB; consider it aborted */
344 iorb->ErrorCode = IOERR_CMD_ABORTED;
345 iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
346 iorb_queue_add(&done_queue, iorb);
347 }
348 }
349 }
350 }
351 }
352 }
353
354 spin_unlock(drv_lock);
355
356 /* call notification routine on all aborted IORBs */
357 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
358 next = iorb->pNxtIORB;
359 iorb->Status = IORB_DONE | IORB_ERROR;
360 aws_free(add_workspace(iorb));
361 if (iorb->RequestControl & IORB_ASYNC_POST) {
362 iorb->NotifyAddress(iorb);
363 }
364 }
365
366 /* restart engine to resume IORB processing */
367 spin_lock(drv_lock);
368 trigger_engine();
369 spin_unlock(drv_lock);
370
371 dprintf("reset_ctxhook() completed\n");
372
373 /* Check whether we have to rearm ourselves because some adapters were busy
374 * when we wanted to reset ports on them.
375 */
376 if (rearm_ctx_hook) {
377 msleep(250);
378 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
379 }
380}
381
382/******************************************************************************
383 * IORB Engine context hook. This hook is executed if trigger_engine() came
384 * to the conclusion that some of the IORBs keep bouncing, most likely due to
385 * some condition on the adapter such as being busy. It could also be a very
386 * busy system. Either way, this requires some task-time help.
387 */
388void engine_ctxhook(ULONG parm)
389{
390 int iorbs_sent;
391 int i;
392
393 dprintf("engine_ctxhook() started\n");
394
395 spin_lock(drv_lock);
396 for (i = 0; i < 10; i++) {
397 if ((iorbs_sent = trigger_engine_1()) == 0) {
398 break;
399 }
400 }
401 spin_unlock(drv_lock);
402
403 dprintf("engine_ctxhook() completed\n");
404
405 if (iorbs_sent != 0) {
406 /* need to rearm ourselves for another run */
407 msleep(250);
408 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
409 }
410}
Note: See TracBrowser for help on using the repository browser.