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

Last change on this file since 23 was 13, checked in by root, 15 years ago

latest NCQ changes from Christian

File size: 14.1 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 != 0) {
128 /* NCQ commands active; force non-NCQ mode and trigger port reset */
129 aws->no_ncq = 1;
130 need_reset = 1;
131 } else if (aws->cmd_slot == ccs) {
132 /* this is the non-NCQ comand that failed */
133 problem_iorb = iorb;
134 }
135 iorb_requeue(iorb);
136
137 /* remove requeued command from the issued command bitmaps */
138 ai->ports[p].ncq_cmds &= ~(1UL << p);
139 ai->ports[p].reg_cmds &= ~(1UL << p);
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 ai->ports[p].reg_cmds = 1;
194 writel(port_mmio + PORT_CMD_ISSUE, 1);
195 readl(port_mmio); /* flush */
196
197 } else {
198 /* IORB is expected to contain the error code; just move to done queue */
199 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
200 iorb_queue_add(&done_queue, problem_iorb);
201 }
202 }
203 }
204 }
205 }
206 }
207
208 spin_unlock(drv_lock);
209
210 /* call notification routine on all IORBs which have completed */
211 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
212 next = iorb->pNxtIORB;
213 if (iorb->RequestControl & IORB_ASYNC_POST) {
214 iorb->NotifyAddress(iorb);
215 }
216 }
217
218 /* restart engine to resume IORB processing */
219 spin_lock(drv_lock);
220 trigger_engine();
221 spin_unlock(drv_lock);
222
223 dprintf("restart_ctxhook() completed\n");
224
225 /* Check whether we have to rearm ourselves because some adapters were busy
226 * when we wanted to restart ports on them.
227 */
228 if (rearm_ctx_hook) {
229 msleep(250);
230 DevHelp_ArmCtxHook(0, restart_ctxhook_h);
231 }
232}
233
234/******************************************************************************
235 * Reset and abort context hook. This function runs at task time and takes
236 * care of port resets and their side effects. Input to this function are:
237 *
238 * ports_to_reset[] - array of port bitmaps, each bit indicating which port
239 * should be reset unconditionally. This is primarily
240 * used by the error interrupt handler.
241 *
242 * abort_queue - queue with IORBs to be arborted (timed-out, ...) If
243 * any of these commands have reached the hardware, the
244 * corresponding port is reset to interrupt command
245 * execution. This is primarily used for timeout
246 * handling and when IORBs are requested to be aborted.
247 *
248 * After resetting the requested ports, all remaining active IORBs on those
249 * ports have to be retried or aborted. Whether a retry is attempted depends
250 * on the kind of IORB -- those which are idempotent are retried, all others
251 * are aborted. This is different from the port restart hook because the
252 * restart hook can assume it is called with the port in error state, thus
253 * the controller will have stopped executing commands. The reset handler can
254 * be called at any time and we can't tell what's going on in the controller.
255 *
256 * The IORBs in the global abort_queue are expected to have their error code
257 * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
258 * the upstream code might reuse the IORBs before we're done with them.
259 */
260void reset_ctxhook(ULONG parm)
261{
262 IORB_QUEUE done_queue;
263 AD_INFO *ai;
264 IORBH _far *iorb;
265 IORBH _far *next = NULL;
266 int rearm_ctx_hook = 0;
267 int a;
268 int p;
269
270 dprintf("reset_ctxhook() started\n");
271 memset(&done_queue, 0x00, sizeof(done_queue));
272
273 spin_lock(drv_lock);
274
275 /* add ports of active IORBs from the abort queue to ports_to_reset[] */
276 for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
277 next = iorb->pNxtIORB;
278 a = iorb_unit_adapter(iorb);
279 p = iorb_unit_port(iorb);
280 ai = ad_infos + a;
281
282 if (ai->busy) {
283 /* this adapter is busy; leave it alone for now */
284 rearm_ctx_hook = 1;
285 continue;
286 }
287
288 /* move IORB to the local 'done' queue */
289 aws_free(add_workspace(iorb));
290 iorb_queue_del(&abort_queue, iorb);
291 iorb_queue_add(&done_queue, iorb);
292
293 /* reset port if the IORB has already been queued to hardware */
294 if (add_workspace(iorb)->queued_hw) {
295 /* prepare port reset */
296 ports_to_reset[a] |= (1UL << p);
297 }
298 }
299
300 /* reset all ports in 'ports_to_reset[]' */
301 for (a = 0; a < ad_info_cnt; a++) {
302 ai = ad_infos + a;
303
304 if (ai->busy) {
305 /* this adapter is busy; leave it alone for now */
306 rearm_ctx_hook = 1;
307 continue;
308 }
309
310 for (p = 0; p <= ai->port_max; p++) {
311 if (ports_to_reset[a] & (1UL << p)) {
312 ports_to_reset[a] &= ~(1UL << p);
313
314 /* Reset this port. Since this is a rather slow operation, we'll
315 * release the spinlock while doing so. The adapter is marked as
316 * 'busy' to prevent similar routines (e.g. an ahci port scan) from
317 * interfering.
318 */
319 ai->busy = 1;
320 spin_unlock(drv_lock);
321 ahci_reset_port(ai, p, 1);
322 spin_lock(drv_lock);
323 ai->busy = 0;
324
325 /* reset port status */
326 ai->ports[p].ncq_cmds = 0;
327 ai->ports[p].reg_cmds = 0;
328 ai->ports[p].cmd_slot = 0;
329
330 /* retry or abort all remaining active commands on this port */
331 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
332 next = iorb->pNxtIORB;
333 if (add_workspace(iorb)->queued_hw) {
334 /* this IORB had already been queued to HW when we reset the port */
335 aws_free(add_workspace(iorb));
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 memset(&iorb->ADDWorkSpace, sizeof(iorb->ADDWorkSpace), 0x00);
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 if (iorb->RequestControl & IORB_ASYNC_POST) {
361 iorb->NotifyAddress(iorb);
362 }
363 }
364
365 /* restart engine to resume IORB processing */
366 spin_lock(drv_lock);
367 trigger_engine();
368 spin_unlock(drv_lock);
369
370 dprintf("reset_ctxhook() completed\n");
371
372 /* Check whether we have to rearm ourselves because some adapters were busy
373 * when we wanted to reset ports on them.
374 */
375 if (rearm_ctx_hook) {
376 msleep(250);
377 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
378 }
379}
380
381/******************************************************************************
382 * IORB Engine context hook. This hook is executed if trigger_engine() came
383 * to the conclusion that some of the IORBs keep bouncing, most likely due to
384 * some condition on the adapter such as being busy. It could also be a very
385 * busy system. Either way, this requires some task-time help.
386 */
387void engine_ctxhook(ULONG parm)
388{
389 int iorbs_sent;
390 int i;
391
392 dprintf("engine_ctxhook() started\n");
393
394 spin_lock(drv_lock);
395 for (i = 0; i < 10; i++) {
396 if ((iorbs_sent = trigger_engine_1()) == 0) {
397 break;
398 }
399 }
400 spin_unlock(drv_lock);
401
402 dprintf("engine_ctxhook() completed\n");
403
404 if (iorbs_sent != 0) {
405 /* need to rearm ourselves for another run */
406 msleep(250);
407 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
408 }
409}
Note: See TracBrowser for help on using the repository browser.