source: trunk/src/ctxhook.c@ 4

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

initial checkin of CM's code

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