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

Last change on this file since 77 was 77, checked in by chris, 14 years ago
  • Further fixes to automatic ATAPI sense handling, now supporting sense buffers larger than 64 bytes if requested by initiator (cdrecord wanted 96 bytes)
  • Separate, and internally handled, spinlock for libc malloc/free calls to reduce chances of memory corruption if somebody forgets to get the driver-level spinlock before calling malloc/free. There was no real problem with that, just some awkward code fragments which look much better now.
  • Link power management implemented
  • More generic support for adapter/port options so all of them can now have a global, adapter or port scope
  • Generic support for inverting driver options (i.e. turn them off with '!')
  • Thorough PCI scan is now the default; the reason it wasn't so far was a delay in Virtualbox but that was never a problem on real hardware
  • SCSI emulation for ATAPI devices; this can be enabled on global, adapter or port scope
File size: 14.9 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 that
58 * all other commands have not yet started executing. Thus, we can requeue
59 * all of them, replacing the failing command with a "request sense"
60 * command to get error details.
61 *
62 * - Multiple non-NCQ commands are executed on different devices behind a
63 * port multiplier which supports FIS-based command switching. This is
64 * more difficult to recover from but currently not an issue because we
65 * don't yet support FIS-based command switching (the FIS receive areas
66 * would become too large for the current data model).
67 *
68 * - One or more NCQ commands were active at the time of the error, with or
69 * without FIS-based command switching. We would have to interrogate the
70 * corresponding devices to find out which command has failed but if this
71 * is combined with FIS-based command switching, even the AHCI spec
72 * recommends to reset the port. This leads to a much simpler approach:
73 * requeue all NCQ commands (they are idempotent per definition, otherwise
74 * they couldn't be reordered by the device) with the 'no_ncq' flag set
75 * in the IORB and reset the port. Then those comands will be executed as
76 * regular commands. The error, if it reoccurrs, can be then handled by
77 * one of the above cases.
78 *
79 * The upstream code will guarantee that we will never have a mix of NCQ and
80 * non-NCQ commands active at the same time in order to reduce complexity
81 * in the interrupt and error handlers.
82 */
83void restart_ctxhook(ULONG parm)
84{
85 IORB_QUEUE done_queue;
86 AD_INFO *ai;
87 IORBH _far *problem_iorb;
88 IORBH _far *iorb;
89 IORBH _far *next = NULL;
90 u8 _far *port_mmio;
91 int rearm_ctx_hook = 0;
92 int need_reset;
93 int ccs;
94 int a;
95 int p;
96
97 dprintf("restart_ctxhook() started\n");
98 memset(&done_queue, 0x00, sizeof(done_queue));
99
100 spin_lock(drv_lock);
101
102 for (a = 0; a < ad_info_cnt; a++) {
103 ai = ad_infos + a;
104
105 if (ai->busy) {
106 /* this adapter is busy; leave it alone for now */
107 rearm_ctx_hook = 1;
108 continue;
109 }
110
111 for (p = 0; p <= ai->port_max; p++) {
112 if (ports_to_restart[a] & (1UL << p)) {
113 ports_to_restart[a] &= ~(1UL << p);
114
115 /* restart this port */
116 port_mmio = port_base(ai, p);
117 problem_iorb = NULL;
118 need_reset = 0;
119
120 dprintf("port %d, TF_DATA: 0x%lx\n", p, readl(port_mmio + PORT_TFDATA));
121
122 /* get "current command slot"; only valid if there are no NCQ cmds */
123 ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x0f);
124
125 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
126 ADD_WORKSPACE _far *aws = add_workspace(iorb);
127 next = iorb->pNxtIORB;
128
129 if (aws->queued_hw) {
130 if (ai->ports[p].ncq_cmds & (1UL << aws->cmd_slot)) {
131 /* NCQ command; force non-NCQ mode and trigger port reset */
132 ai->ports[p].ncq_cmds &= ~(1UL << aws->cmd_slot);
133 aws->no_ncq = 1;
134 need_reset = 1;
135 } else {
136 /* regular command; clear cmd bit and identify problem IORB */
137 ai->ports[p].reg_cmds &= ~(1UL << aws->cmd_slot);
138 if (aws->cmd_slot == ccs) {
139 /* this is the non-NCQ comand that failed */
140 problem_iorb = iorb;
141 }
142 }
143 /* we can requeue all IORBs unconditionally (see function comment) */
144 iorb_requeue(iorb);
145 }
146 }
147
148 /* sanity check: issued command bitmaps should be 0 now */
149 if (ai->ports[p].ncq_cmds != 0 || ai->ports[p].reg_cmds != 0) {
150 dprintf("warning: commands issued not 0 (%08lx/%08lx); resetting...\n",
151 ai->ports[p].ncq_cmds, ai->ports[p].reg_cmds);
152 need_reset = 1;
153 }
154
155 if (!need_reset) {
156 if ((readl(port_mmio + PORT_TFDATA) & 0x88) != 0) {
157 /* device is not in an idle state */
158 need_reset = 1;
159 }
160 }
161
162 /* restart/reset port */
163 ai->busy = 1;
164 spin_unlock(drv_lock);
165 if (need_reset) {
166 ahci_reset_port(ai, p, 1);
167 } else {
168 ahci_stop_port(ai, p);
169 ahci_start_port(ai, p, 1);
170 }
171 spin_lock(drv_lock);
172 ai->busy = 0;
173
174 /* reset internal port status */
175 ai->ports[p].ncq_cmds = 0;
176 ai->ports[p].reg_cmds = 0;
177 ai->ports[p].cmd_slot = 0;
178
179 if (problem_iorb != NULL) {
180 /* get details about the error that caused this IORB to fail */
181 if (need_reset) {
182 /* no way to retrieve error details after a reset */
183 iorb_seterr(problem_iorb, IOERR_DEVICE_NONSPECIFIC);
184 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
185 iorb_queue_add(&done_queue, problem_iorb);
186
187 } else {
188 /* get sense information */
189 ADD_WORKSPACE _far *aws = add_workspace(problem_iorb);
190 int d = iorb_unit_device(problem_iorb);
191 int (*req_sense)(IORBH _far *, int) = (ai->ports[p].devs[d].atapi) ?
192 atapi_req_sense : ata_req_sense;
193
194 aws->processing = 1;
195 aws->queued_hw = 1;
196
197 if (req_sense(problem_iorb, 0) == 0) {
198 /* execute request sense on slot #0 before anything else comes along */
199 ADD_StartTimerMS(&aws->timer, 5000, (PFN) timeout_callback, iorb, 0);
200 aws->cmd_slot = 0;
201 ai->ports[p].reg_cmds = 1;
202 writel(port_mmio + PORT_CMD_ISSUE, 1);
203 readl(port_mmio); /* flush */
204
205 } else {
206 /* IORB is expected to contain the error code; just move to done queue */
207 iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
208 iorb_queue_add(&done_queue, problem_iorb);
209 }
210 }
211 }
212 }
213 }
214 }
215
216 spin_unlock(drv_lock);
217
218 /* call notification routine on all IORBs which have completed */
219 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
220 next = iorb->pNxtIORB;
221
222 spin_lock(drv_lock);
223 aws_free(add_workspace(iorb));
224 spin_unlock(drv_lock);
225
226 iorb_complete(iorb);
227 }
228
229 /* restart engine to resume IORB processing */
230 spin_lock(drv_lock);
231 trigger_engine();
232 spin_unlock(drv_lock);
233
234 dprintf("restart_ctxhook() completed\n");
235
236 /* Check whether we have to rearm ourselves because some adapters were busy
237 * when we wanted to restart ports on them.
238 */
239 if (rearm_ctx_hook) {
240 msleep(250);
241 DevHelp_ArmCtxHook(0, restart_ctxhook_h);
242 }
243}
244
245/******************************************************************************
246 * Reset and abort context hook. This function runs at task time and takes
247 * care of port resets and their side effects. Input to this function are:
248 *
249 * ports_to_reset[] - array of port bitmaps, each bit indicating which port
250 * should be reset unconditionally. This is primarily
251 * used by the error interrupt handler.
252 *
253 * abort_queue - queue with IORBs to be arborted (timed-out, ...) If
254 * any of these commands have reached the hardware, the
255 * corresponding port is reset to interrupt command
256 * execution. This is primarily used for timeout
257 * handling and when IORBs are requested to be aborted.
258 *
259 * After resetting the requested ports, all remaining active IORBs on those
260 * ports have to be retried or aborted. Whether a retry is attempted depends
261 * on the kind of IORB -- those which are idempotent are retried, all others
262 * are aborted. This is different from the port restart hook because the
263 * restart hook can assume it is called with the port in error state, thus
264 * the controller will have stopped executing commands. The reset handler can
265 * be called at any time and we can't tell what's going on in the controller.
266 *
267 * The IORBs in the global abort_queue are expected to have their error code
268 * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
269 * the upstream code might reuse the IORBs before we're done with them.
270 */
271void reset_ctxhook(ULONG parm)
272{
273 IORB_QUEUE done_queue;
274 AD_INFO *ai;
275 IORBH _far *iorb;
276 IORBH _far *next = NULL;
277 int rearm_ctx_hook = 0;
278 int a;
279 int p;
280
281 dprintf("reset_ctxhook() started\n");
282 memset(&done_queue, 0x00, sizeof(done_queue));
283
284 spin_lock(drv_lock);
285
286 /* add ports of active IORBs from the abort queue to ports_to_reset[] */
287 for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
288 next = iorb->pNxtIORB;
289 a = iorb_unit_adapter(iorb);
290 p = iorb_unit_port(iorb);
291 ai = ad_infos + a;
292
293 if (ai->busy) {
294 /* this adapter is busy; leave it alone for now */
295 rearm_ctx_hook = 1;
296 continue;
297 }
298
299 /* move IORB to the local 'done' queue */
300 iorb_queue_del(&abort_queue, iorb);
301 iorb_queue_add(&done_queue, iorb);
302
303 /* reset port if the IORB has already been queued to hardware */
304 if (add_workspace(iorb)->queued_hw) {
305 /* prepare port reset */
306 ports_to_reset[a] |= (1UL << p);
307 }
308 }
309
310 /* reset all ports in 'ports_to_reset[]' */
311 for (a = 0; a < ad_info_cnt; a++) {
312 ai = ad_infos + a;
313
314 if (ai->busy) {
315 /* this adapter is busy; leave it alone for now */
316 rearm_ctx_hook = 1;
317 continue;
318 }
319
320 for (p = 0; p <= ai->port_max; p++) {
321 if (ports_to_reset[a] & (1UL << p)) {
322 ports_to_reset[a] &= ~(1UL << p);
323
324 /* Reset this port. Since this is a rather slow operation, we'll
325 * release the spinlock while doing so. The adapter is marked as
326 * 'busy' to prevent similar routines (e.g. an ahci port scan) from
327 * interfering.
328 */
329 ai->busy = 1;
330 spin_unlock(drv_lock);
331 ahci_reset_port(ai, p, 1);
332 spin_lock(drv_lock);
333 ai->busy = 0;
334
335 /* reset port status */
336 ai->ports[p].ncq_cmds = 0;
337 ai->ports[p].reg_cmds = 0;
338 ai->ports[p].cmd_slot = 0;
339
340 /* retry or abort all remaining active commands on this port */
341 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
342 next = iorb->pNxtIORB;
343 if (add_workspace(iorb)->queued_hw) {
344 /* this IORB had already been queued to HW when we reset the port */
345 if (add_workspace(iorb)->idempotent) {
346 /* We can retry this IORB; just reset its status and it will be
347 * picked up by subsequent trigger_engine() calls.
348 */
349 iorb_requeue(iorb);
350
351 } else {
352 /* we cannot retry this IORB; consider it aborted */
353 iorb->ErrorCode = IOERR_CMD_ABORTED;
354 iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
355 iorb_queue_add(&done_queue, iorb);
356 }
357 }
358 }
359 }
360 }
361 }
362
363 spin_unlock(drv_lock);
364
365 /* complete all aborted IORBs */
366 for (iorb = done_queue.root; iorb != NULL; iorb = next) {
367 next = iorb->pNxtIORB;
368
369 spin_lock(drv_lock);
370 aws_free(add_workspace(iorb));
371 spin_unlock(drv_lock);
372
373 iorb->Status |= IORB_ERROR;
374 iorb_complete(iorb);
375 }
376
377 /* restart engine to resume IORB processing */
378 spin_lock(drv_lock);
379 trigger_engine();
380 spin_unlock(drv_lock);
381
382 dprintf("reset_ctxhook() completed\n");
383
384 /* Check whether we have to rearm ourselves because some adapters were busy
385 * when we wanted to reset ports on them.
386 */
387 if (rearm_ctx_hook) {
388 msleep(250);
389 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
390 }
391}
392
393/******************************************************************************
394 * IORB Engine context hook. This hook is executed if trigger_engine() came
395 * to the conclusion that some of the IORBs keep bouncing, most likely due to
396 * some condition on the adapter such as being busy. It could also be a very
397 * busy system. Either way, this requires some task-time help.
398 */
399void engine_ctxhook(ULONG parm)
400{
401 int iorbs_sent;
402 int i;
403
404 dprintf("engine_ctxhook() started\n");
405
406 spin_lock(drv_lock);
407 for (i = 0; i < 10; i++) {
408 if ((iorbs_sent = trigger_engine_1()) == 0) {
409 break;
410 }
411 }
412 spin_unlock(drv_lock);
413
414 dprintf("engine_ctxhook() completed\n");
415
416 if (iorbs_sent != 0) {
417 /* need to rearm ourselves for another run */
418 msleep(250);
419 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
420 }
421}
422
Note: See TracBrowser for help on using the repository browser.