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

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

added feature byte to ATAPI commands; fixed request sense CDB

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