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

Last change on this file since 87 was 87, checked in by markus, 14 years ago

changed copyright headers according to contract; removed evaluation message

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