FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/mlx.c
1 /* $NetBSD: mlx.c,v 1.32.2.1 2005/08/04 18:25:16 tron Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*-
40 * Copyright (c) 1999 Michael Smith
41 * All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * from FreeBSD: mlx.c,v 1.14.2.3 2000/08/04 06:52:50 msmith Exp
65 */
66
67 /*
68 * Driver for the Mylex DAC960 family of RAID controllers.
69 *
70 * TODO:
71 *
72 * o Test and enable channel pause.
73 * o SCSI pass-through.
74 */
75
76 #include <sys/cdefs.h>
77 __KERNEL_RCSID(0, "$NetBSD: mlx.c,v 1.32.2.1 2005/08/04 18:25:16 tron Exp $");
78
79 #include "ld.h"
80
81 #include <sys/param.h>
82 #include <sys/systm.h>
83 #include <sys/kernel.h>
84 #include <sys/device.h>
85 #include <sys/queue.h>
86 #include <sys/proc.h>
87 #include <sys/buf.h>
88 #include <sys/bufq.h>
89 #include <sys/endian.h>
90 #include <sys/malloc.h>
91 #include <sys/conf.h>
92 #include <sys/kthread.h>
93 #include <sys/disk.h>
94
95 #include <machine/vmparam.h>
96 #include <machine/bus.h>
97
98 #include <uvm/uvm_extern.h>
99
100 #include <dev/ldvar.h>
101
102 #include <dev/ic/mlxreg.h>
103 #include <dev/ic/mlxio.h>
104 #include <dev/ic/mlxvar.h>
105
106 #include "locators.h"
107
108 #define MLX_TIMEOUT 60
109
110 #ifdef DIAGNOSTIC
111 #define DPRINTF(x) printf x
112 #else
113 #define DPRINTF(x)
114 #endif
115
116 static void mlx_adjqparam(struct mlx_softc *, int, int);
117 static int mlx_ccb_submit(struct mlx_softc *, struct mlx_ccb *);
118 static int mlx_check(struct mlx_softc *, int);
119 static void mlx_configure(struct mlx_softc *, int);
120 static void mlx_describe(struct mlx_softc *);
121 static void *mlx_enquire(struct mlx_softc *, int, size_t,
122 void (*)(struct mlx_ccb *), int);
123 static int mlx_fw_message(struct mlx_softc *, int, int, int);
124 static void mlx_pause_action(struct mlx_softc *);
125 static void mlx_pause_done(struct mlx_ccb *);
126 static void mlx_periodic(struct mlx_softc *);
127 static void mlx_periodic_create(void *);
128 static void mlx_periodic_enquiry(struct mlx_ccb *);
129 static void mlx_periodic_eventlog_poll(struct mlx_softc *);
130 static void mlx_periodic_eventlog_respond(struct mlx_ccb *);
131 static void mlx_periodic_rebuild(struct mlx_ccb *);
132 static void mlx_periodic_thread(void *);
133 static int mlx_print(void *, const char *);
134 static int mlx_rebuild(struct mlx_softc *, int, int);
135 static void mlx_shutdown(void *);
136 static int mlx_submatch(struct device *, struct cfdata *,
137 const locdesc_t *, void *);
138 static int mlx_user_command(struct mlx_softc *, struct mlx_usercommand *);
139
140 static __inline__ time_t mlx_curtime(void);
141
142 dev_type_open(mlxopen);
143 dev_type_close(mlxclose);
144 dev_type_ioctl(mlxioctl);
145
146 const struct cdevsw mlx_cdevsw = {
147 mlxopen, mlxclose, noread, nowrite, mlxioctl,
148 nostop, notty, nopoll, nommap, nokqfilter,
149 };
150
151 extern struct cfdriver mlx_cd;
152 static struct proc *mlx_periodic_proc;
153 static void *mlx_sdh;
154
155 struct {
156 int hwid;
157 const char *name;
158 } static const mlx_cname[] = {
159 { 0x00, "960E/960M" },
160 { 0x01, "960P/PD" },
161 { 0x02, "960PL" },
162 { 0x10, "960PG" },
163 { 0x11, "960PJ" },
164 { 0x12, "960PR" },
165 { 0x13, "960PT" },
166 { 0x14, "960PTL0" },
167 { 0x15, "960PRL" },
168 { 0x16, "960PTL1" },
169 { 0x20, "1164PVX" },
170 };
171
172 static const char * const mlx_sense_msgs[] = {
173 "because write recovery failed",
174 "because of SCSI bus reset failure",
175 "because of double check condition",
176 "because it was removed",
177 "because of gross error on SCSI chip",
178 "because of bad tag returned from drive",
179 "because of timeout on SCSI command",
180 "because of reset SCSI command issued from system",
181 "because busy or parity error count exceeded limit",
182 "because of 'kill drive' command from system",
183 "because of selection timeout",
184 "due to SCSI phase sequence error",
185 "due to unknown status"
186 };
187
188 static const char * const mlx_status_msgs[] = {
189 "normal completion", /* 0 */
190 "irrecoverable data error", /* 1 */
191 "drive does not exist, or is offline", /* 2 */
192 "attempt to write beyond end of drive", /* 3 */
193 "bad data encountered", /* 4 */
194 "invalid log entry request", /* 5 */
195 "attempt to rebuild online drive", /* 6 */
196 "new disk failed during rebuild", /* 7 */
197 "invalid channel/target", /* 8 */
198 "rebuild/check already in progress", /* 9 */
199 "one or more disks are dead", /* 10 */
200 "invalid or non-redundant drive", /* 11 */
201 "channel is busy", /* 12 */
202 "channel is not stopped", /* 13 */
203 "rebuild successfully terminated", /* 14 */
204 "unsupported command", /* 15 */
205 "check condition received", /* 16 */
206 "device is busy", /* 17 */
207 "selection or command timeout", /* 18 */
208 "command terminated abnormally", /* 19 */
209 "controller wedged", /* 20 */
210 "software timeout", /* 21 */
211 "command busy (?)", /* 22 */
212 };
213
214 struct {
215 u_char command;
216 u_char msg; /* Index into mlx_status_msgs[]. */
217 u_short status;
218 } static const mlx_msgs[] = {
219 { MLX_CMD_READSG, 1, 0x0001 },
220 { MLX_CMD_READSG, 1, 0x0002 },
221 { MLX_CMD_READSG, 3, 0x0105 },
222 { MLX_CMD_READSG, 4, 0x010c },
223 { MLX_CMD_WRITESG, 1, 0x0001 },
224 { MLX_CMD_WRITESG, 1, 0x0002 },
225 { MLX_CMD_WRITESG, 3, 0x0105 },
226 { MLX_CMD_READSG_OLD, 1, 0x0001 },
227 { MLX_CMD_READSG_OLD, 1, 0x0002 },
228 { MLX_CMD_READSG_OLD, 3, 0x0105 },
229 { MLX_CMD_WRITESG_OLD, 1, 0x0001 },
230 { MLX_CMD_WRITESG_OLD, 1, 0x0002 },
231 { MLX_CMD_WRITESG_OLD, 3, 0x0105 },
232 { MLX_CMD_LOGOP, 5, 0x0105 },
233 { MLX_CMD_REBUILDASYNC, 6, 0x0002 },
234 { MLX_CMD_REBUILDASYNC, 7, 0x0004 },
235 { MLX_CMD_REBUILDASYNC, 8, 0x0105 },
236 { MLX_CMD_REBUILDASYNC, 9, 0x0106 },
237 { MLX_CMD_REBUILDASYNC, 14, 0x0107 },
238 { MLX_CMD_CHECKASYNC, 10, 0x0002 },
239 { MLX_CMD_CHECKASYNC, 11, 0x0105 },
240 { MLX_CMD_CHECKASYNC, 9, 0x0106 },
241 { MLX_CMD_STOPCHANNEL, 12, 0x0106 },
242 { MLX_CMD_STOPCHANNEL, 8, 0x0105 },
243 { MLX_CMD_STARTCHANNEL, 13, 0x0005 },
244 { MLX_CMD_STARTCHANNEL, 8, 0x0105 },
245 { MLX_CMD_DIRECT_CDB, 16, 0x0002 },
246 { MLX_CMD_DIRECT_CDB, 17, 0x0008 },
247 { MLX_CMD_DIRECT_CDB, 18, 0x000e },
248 { MLX_CMD_DIRECT_CDB, 19, 0x000f },
249 { MLX_CMD_DIRECT_CDB, 8, 0x0105 },
250
251 { 0, 20, MLX_STATUS_WEDGED },
252 { 0, 21, MLX_STATUS_LOST },
253 { 0, 22, MLX_STATUS_BUSY },
254
255 { 0, 14, 0x0104 },
256 };
257
258 /*
259 * Return the current time in seconds - we're not particularly interested in
260 * precision here.
261 */
262 static __inline__ time_t
263 mlx_curtime(void)
264 {
265 time_t rt;
266 int s;
267
268 s = splclock();
269 rt = mono_time.tv_sec;
270 splx(s);
271
272 return (rt);
273 }
274
275 /*
276 * Initialise the controller and our interface.
277 */
278 void
279 mlx_init(struct mlx_softc *mlx, const char *intrstr)
280 {
281 struct mlx_ccb *mc;
282 struct mlx_enquiry_old *meo;
283 struct mlx_enquiry2 *me2;
284 struct mlx_cinfo *ci;
285 int rv, fwminor, hscode, hserr, hsparam1, hsparam2, hsmsg;
286 int size, i, rseg;
287 const char *wantfwstr;
288 bus_dma_segment_t seg;
289
290 SIMPLEQ_INIT(&mlx->mlx_ccb_queue);
291 SLIST_INIT(&mlx->mlx_ccb_freelist);
292 TAILQ_INIT(&mlx->mlx_ccb_worklist);
293
294 if (intrstr != NULL)
295 printf("%s: interrupting at %s\n", mlx->mlx_dv.dv_xname,
296 intrstr);
297
298 /*
299 * Allocate the scatter/gather lists.
300 */
301 size = MLX_SGL_SIZE * MLX_MAX_QUEUECNT;
302
303 if ((rv = bus_dmamem_alloc(mlx->mlx_dmat, size, PAGE_SIZE, 0, &seg, 1,
304 &rseg, BUS_DMA_NOWAIT)) != 0) {
305 printf("%s: unable to allocate sglists, rv = %d\n",
306 mlx->mlx_dv.dv_xname, rv);
307 return;
308 }
309
310 if ((rv = bus_dmamem_map(mlx->mlx_dmat, &seg, rseg, size,
311 (caddr_t *)&mlx->mlx_sgls,
312 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
313 printf("%s: unable to map sglists, rv = %d\n",
314 mlx->mlx_dv.dv_xname, rv);
315 return;
316 }
317
318 if ((rv = bus_dmamap_create(mlx->mlx_dmat, size, 1, size, 0,
319 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mlx->mlx_dmamap)) != 0) {
320 printf("%s: unable to create sglist DMA map, rv = %d\n",
321 mlx->mlx_dv.dv_xname, rv);
322 return;
323 }
324
325 if ((rv = bus_dmamap_load(mlx->mlx_dmat, mlx->mlx_dmamap,
326 mlx->mlx_sgls, size, NULL, BUS_DMA_NOWAIT)) != 0) {
327 printf("%s: unable to load sglist DMA map, rv = %d\n",
328 mlx->mlx_dv.dv_xname, rv);
329 return;
330 }
331
332 mlx->mlx_sgls_paddr = mlx->mlx_dmamap->dm_segs[0].ds_addr;
333 memset(mlx->mlx_sgls, 0, size);
334
335 /*
336 * Allocate and initialize the CCBs.
337 */
338 mc = malloc(sizeof(*mc) * MLX_MAX_QUEUECNT, M_DEVBUF, M_NOWAIT);
339 mlx->mlx_ccbs = mc;
340
341 for (i = 0; i < MLX_MAX_QUEUECNT; i++, mc++) {
342 mc->mc_ident = i;
343 rv = bus_dmamap_create(mlx->mlx_dmat, MLX_MAX_XFER,
344 MLX_MAX_SEGS, MLX_MAX_XFER, 0,
345 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
346 &mc->mc_xfer_map);
347 if (rv != 0)
348 break;
349 mlx->mlx_nccbs++;
350 mlx_ccb_free(mlx, mc);
351 }
352 if (mlx->mlx_nccbs != MLX_MAX_QUEUECNT)
353 printf("%s: %d/%d CCBs usable\n", mlx->mlx_dv.dv_xname,
354 mlx->mlx_nccbs, MLX_MAX_QUEUECNT);
355
356 /* Disable interrupts before we start talking to the controller */
357 (*mlx->mlx_intaction)(mlx, 0);
358
359 /* If we've got a reset routine, then reset the controller now. */
360 if (mlx->mlx_reset != NULL) {
361 printf("%s: resetting controller...\n", mlx->mlx_dv.dv_xname);
362 if ((*mlx->mlx_reset)(mlx) != 0) {
363 printf("%s: reset failed\n", mlx->mlx_dv.dv_xname);
364 return;
365 }
366 }
367
368 /*
369 * Wait for the controller to come ready, handshaking with the
370 * firmware if required. This is typically only necessary on
371 * platforms where the controller BIOS does not run.
372 */
373 hsmsg = 0;
374
375 for (;;) {
376 hscode = (*mlx->mlx_fw_handshake)(mlx, &hserr, &hsparam1,
377 &hsparam2);
378 if (hscode == 0) {
379 if (hsmsg != 0)
380 printf("%s: initialization complete\n",
381 mlx->mlx_dv.dv_xname);
382 break;
383 }
384
385 /* Report first time around... */
386 if (hsmsg == 0) {
387 printf("%s: initializing (may take some time)...\n",
388 mlx->mlx_dv.dv_xname);
389 hsmsg = 1;
390 }
391
392 /* Did we get a real message? */
393 if (hscode == 2) {
394 hscode = mlx_fw_message(mlx, hserr, hsparam1, hsparam2);
395
396 /* Fatal initialisation error? */
397 if (hscode != 0)
398 return;
399 }
400 }
401
402 /*
403 * Do quirk/feature related things.
404 */
405 ci = &mlx->mlx_ci;
406
407 if (ci->ci_iftype > 1) {
408 me2 = mlx_enquire(mlx, MLX_CMD_ENQUIRY2,
409 sizeof(struct mlx_enquiry2), NULL, 0);
410 if (me2 == NULL) {
411 printf("%s: ENQUIRY2 failed\n", mlx->mlx_dv.dv_xname);
412 return;
413 }
414
415 ci->ci_firmware_id[0] = me2->me_firmware_id[0];
416 ci->ci_firmware_id[1] = me2->me_firmware_id[1];
417 ci->ci_firmware_id[2] = me2->me_firmware_id[2];
418 ci->ci_firmware_id[3] = me2->me_firmware_id[3];
419 ci->ci_hardware_id = me2->me_hardware_id[0];
420 ci->ci_mem_size = le32toh(me2->me_mem_size);
421 ci->ci_max_sg = le16toh(me2->me_max_sg);
422 ci->ci_max_commands = le16toh(me2->me_max_commands);
423 ci->ci_nchan = me2->me_actual_channels;
424
425 free(me2, M_DEVBUF);
426 }
427
428 if (ci->ci_iftype <= 2) {
429 /*
430 * These controllers may not report the firmware version in
431 * the ENQUIRY2 response, or may not even support it.
432 */
433 meo = mlx_enquire(mlx, MLX_CMD_ENQUIRY_OLD,
434 sizeof(struct mlx_enquiry_old), NULL, 0);
435 if (meo == NULL) {
436 printf("%s: ENQUIRY_OLD failed\n", mlx->mlx_dv.dv_xname);
437 return;
438 }
439 ci->ci_firmware_id[0] = meo->me_fwmajor;
440 ci->ci_firmware_id[1] = meo->me_fwminor;
441 ci->ci_firmware_id[2] = 0;
442 ci->ci_firmware_id[3] = '';
443
444 if (ci->ci_iftype == 1) {
445 ci->ci_hardware_id = 0; /* XXX */
446 ci->ci_mem_size = 0; /* XXX */
447 ci->ci_max_sg = 17; /* XXX */
448 ci->ci_max_commands = meo->me_max_commands;
449 }
450
451 free(meo, M_DEVBUF);
452 }
453
454 wantfwstr = NULL;
455 fwminor = ci->ci_firmware_id[1];
456
457 switch (ci->ci_firmware_id[0]) {
458 case 2:
459 if (ci->ci_iftype == 1) {
460 if (fwminor < 14)
461 wantfwstr = "2.14";
462 } else if (fwminor < 42)
463 wantfwstr = "2.42";
464 break;
465
466 case 3:
467 if (fwminor < 51)
468 wantfwstr = "3.51";
469 break;
470
471 case 4:
472 if (fwminor < 6)
473 wantfwstr = "4.06";
474 break;
475
476 case 5:
477 if (fwminor < 7)
478 wantfwstr = "5.07";
479 break;
480 }
481
482 /* Print a little information about the controller. */
483 mlx_describe(mlx);
484
485 if (wantfwstr != NULL) {
486 printf("%s: WARNING: this f/w revision is not recommended\n",
487 mlx->mlx_dv.dv_xname);
488 printf("%s: WARNING: use revision %s or later\n",
489 mlx->mlx_dv.dv_xname, wantfwstr);
490 }
491
492 /* We don't (yet) know where the event log is up to. */
493 mlx->mlx_currevent = -1;
494
495 /* No user-requested background operation is in progress. */
496 mlx->mlx_bg = 0;
497 mlx->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
498
499 /* Set maximum number of queued commands for `regular' operations. */
500 mlx->mlx_max_queuecnt =
501 min(ci->ci_max_commands, MLX_MAX_QUEUECNT) -
502 MLX_NCCBS_CONTROL;
503 #ifdef DIAGNOSTIC
504 if (mlx->mlx_max_queuecnt < MLX_NCCBS_CONTROL + MLX_MAX_DRIVES)
505 printf("%s: WARNING: few CCBs available\n",
506 mlx->mlx_dv.dv_xname);
507 if (ci->ci_max_sg < MLX_MAX_SEGS) {
508 printf("%s: oops, not enough S/G segments\n",
509 mlx->mlx_dv.dv_xname);
510 return;
511 }
512 #endif
513
514 if (mlx_sdh == NULL) {
515 /*
516 * Set our `shutdownhook' before we start any device
517 * activity.
518 */
519 mlx_sdh = shutdownhook_establish(mlx_shutdown, NULL);
520
521 /* Arrange to create a status monitoring thread. */
522 kthread_create(mlx_periodic_create, NULL);
523 }
524
525 /* Finally, attach child devices and enable interrupts. */
526 mlx_configure(mlx, 0);
527 (*mlx->mlx_intaction)(mlx, 1);
528
529 mlx->mlx_flags |= MLXF_INITOK;
530 }
531
532 /*
533 * Tell the world about the controller.
534 */
535 static void
536 mlx_describe(struct mlx_softc *mlx)
537 {
538 struct mlx_cinfo *ci;
539 static char buf[80];
540 const char *model;
541 int i;
542
543 model = NULL;
544 ci = &mlx->mlx_ci;
545
546 for (i = 0; i < sizeof(mlx_cname) / sizeof(mlx_cname[0]); i++)
547 if (ci->ci_hardware_id == mlx_cname[i].hwid) {
548 model = mlx_cname[i].name;
549 break;
550 }
551
552 if (model == NULL) {
553 snprintf(buf, sizeof(buf), " model 0x%x", ci->ci_hardware_id);
554 model = buf;
555 }
556
557 printf("%s: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d",
558 mlx->mlx_dv.dv_xname, model, ci->ci_nchan,
559 ci->ci_nchan > 1 ? "s" : "",
560 ci->ci_firmware_id[0], ci->ci_firmware_id[1],
561 ci->ci_firmware_id[3], ci->ci_firmware_id[2]);
562 if (ci->ci_mem_size != 0)
563 printf(", %dMB RAM", ci->ci_mem_size >> 20);
564 printf("\n");
565 }
566
567 /*
568 * Locate disk resources and attach children to them.
569 */
570 static void
571 mlx_configure(struct mlx_softc *mlx, int waitok)
572 {
573 struct mlx_enquiry *me;
574 struct mlx_enquiry_old *meo;
575 struct mlx_enq_sys_drive *mes;
576 struct mlx_sysdrive *ms;
577 struct mlx_attach_args mlxa;
578 int i, nunits;
579 u_int size;
580 int help[2];
581 locdesc_t *ldesc = (void *)help; /* XXX */
582
583 mlx->mlx_flags |= MLXF_RESCANNING;
584
585 if (mlx->mlx_ci.ci_iftype <= 2) {
586 meo = mlx_enquire(mlx, MLX_CMD_ENQUIRY_OLD,
587 sizeof(struct mlx_enquiry_old), NULL, waitok);
588 if (meo == NULL) {
589 printf("%s: ENQUIRY_OLD failed\n",
590 mlx->mlx_dv.dv_xname);
591 goto out;
592 }
593 mlx->mlx_numsysdrives = meo->me_num_sys_drvs;
594 free(meo, M_DEVBUF);
595 } else {
596 me = mlx_enquire(mlx, MLX_CMD_ENQUIRY,
597 sizeof(struct mlx_enquiry), NULL, waitok);
598 if (me == NULL) {
599 printf("%s: ENQUIRY failed\n", mlx->mlx_dv.dv_xname);
600 goto out;
601 }
602 mlx->mlx_numsysdrives = me->me_num_sys_drvs;
603 free(me, M_DEVBUF);
604 }
605
606 mes = mlx_enquire(mlx, MLX_CMD_ENQSYSDRIVE,
607 sizeof(*mes) * MLX_MAX_DRIVES, NULL, waitok);
608 if (mes == NULL) {
609 printf("%s: error fetching drive status\n",
610 mlx->mlx_dv.dv_xname);
611 goto out;
612 }
613
614 /* Allow 1 queued command per unit while re-configuring. */
615 mlx_adjqparam(mlx, 1, 0);
616
617 ms = &mlx->mlx_sysdrive[0];
618 nunits = 0;
619 for (i = 0; i < MLX_MAX_DRIVES; i++, ms++) {
620 size = le32toh(mes[i].sd_size);
621 ms->ms_state = mes[i].sd_state;
622
623 /*
624 * If an existing device has changed in some way (e.g. no
625 * longer present) then detach it.
626 */
627 if (ms->ms_dv != NULL && (size != ms->ms_size ||
628 (mes[i].sd_raidlevel & 0xf) != ms->ms_raidlevel))
629 config_detach(ms->ms_dv, DETACH_FORCE);
630
631 ms->ms_size = size;
632 ms->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
633 ms->ms_state = mes[i].sd_state;
634 ms->ms_dv = NULL;
635
636 if (i >= mlx->mlx_numsysdrives)
637 continue;
638 if (size == 0xffffffffU || size == 0)
639 continue;
640
641 /*
642 * Attach a new device.
643 */
644 mlxa.mlxa_unit = i;
645
646 ldesc->len = 1;
647 ldesc->locs[MLXCF_UNIT] = i;
648
649 ms->ms_dv = config_found_sm_loc(&mlx->mlx_dv, "mlx", NULL, &mlxa, mlx_print,
650 mlx_submatch);
651 nunits += (ms->ms_dv != NULL);
652 }
653
654 free(mes, M_DEVBUF);
655
656 if (nunits != 0)
657 mlx_adjqparam(mlx, mlx->mlx_max_queuecnt / nunits,
658 mlx->mlx_max_queuecnt % nunits);
659 out:
660 mlx->mlx_flags &= ~MLXF_RESCANNING;
661 }
662
663 /*
664 * Print autoconfiguration message for a sub-device.
665 */
666 static int
667 mlx_print(void *aux, const char *pnp)
668 {
669 struct mlx_attach_args *mlxa;
670
671 mlxa = (struct mlx_attach_args *)aux;
672
673 if (pnp != NULL)
674 aprint_normal("block device at %s", pnp);
675 aprint_normal(" unit %d", mlxa->mlxa_unit);
676 return (UNCONF);
677 }
678
679 /*
680 * Match a sub-device.
681 */
682 static int
683 mlx_submatch(struct device *parent, struct cfdata *cf,
684 const locdesc_t *ldesc, void *aux)
685 {
686
687 if (cf->cf_loc[MLXCF_UNIT] != MLXCF_UNIT_DEFAULT &&
688 cf->cf_loc[MLXCF_UNIT] != ldesc->locs[MLXCF_UNIT])
689 return (0);
690
691 return (config_match(parent, cf, aux));
692 }
693
694 /*
695 * Shut down all configured `mlx' devices.
696 */
697 static void
698 mlx_shutdown(void *cookie)
699 {
700 struct mlx_softc *mlx;
701 int i;
702
703 for (i = 0; i < mlx_cd.cd_ndevs; i++)
704 if ((mlx = device_lookup(&mlx_cd, i)) != NULL)
705 mlx_flush(mlx, 0);
706 }
707
708 /*
709 * Adjust queue parameters for all child devices.
710 */
711 static void
712 mlx_adjqparam(struct mlx_softc *mlx, int mpu, int slop)
713 {
714 #if NLD > 0
715 extern struct cfdriver ld_cd;
716 struct ld_softc *ld;
717 int i;
718
719 for (i = 0; i < ld_cd.cd_ndevs; i++) {
720 if ((ld = device_lookup(&ld_cd, i)) == NULL)
721 continue;
722 if (ld->sc_dv.dv_parent != &mlx->mlx_dv)
723 continue;
724 ldadjqparam(ld, mpu + (slop-- > 0));
725 }
726 #endif
727 }
728
729 /*
730 * Accept an open operation on the control device.
731 */
732 int
733 mlxopen(dev_t dev, int flag, int mode, struct proc *p)
734 {
735 struct mlx_softc *mlx;
736
737 if ((mlx = device_lookup(&mlx_cd, minor(dev))) == NULL)
738 return (ENXIO);
739 if ((mlx->mlx_flags & MLXF_INITOK) == 0)
740 return (ENXIO);
741 if ((mlx->mlx_flags & MLXF_OPEN) != 0)
742 return (EBUSY);
743
744 mlx->mlx_flags |= MLXF_OPEN;
745 return (0);
746 }
747
748 /*
749 * Accept the last close on the control device.
750 */
751 int
752 mlxclose(dev_t dev, int flag, int mode, struct proc *p)
753 {
754 struct mlx_softc *mlx;
755
756 mlx = device_lookup(&mlx_cd, minor(dev));
757 mlx->mlx_flags &= ~MLXF_OPEN;
758 return (0);
759 }
760
761 /*
762 * Handle control operations.
763 */
764 int
765 mlxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
766 {
767 struct mlx_softc *mlx;
768 struct mlx_rebuild_request *rb;
769 struct mlx_rebuild_status *rs;
770 struct mlx_pause *mp;
771 struct mlx_sysdrive *ms;
772 int i, rv, *arg, result;
773
774 if (securelevel >= 2)
775 return (EPERM);
776
777 mlx = device_lookup(&mlx_cd, minor(dev));
778
779 rb = (struct mlx_rebuild_request *)data;
780 rs = (struct mlx_rebuild_status *)data;
781 arg = (int *)data;
782 rv = 0;
783
784 switch (cmd) {
785 case MLX_RESCAN_DRIVES:
786 /*
787 * Scan the controller to see whether new drives have
788 * appeared, or old ones disappeared.
789 */
790 mlx_configure(mlx, 1);
791 return (0);
792
793 case MLX_PAUSE_CHANNEL:
794 /*
795 * Pause one or more SCSI channels for a period of time, to
796 * assist in the process of hot-swapping devices.
797 *
798 * Note that at least the 3.51 firmware on the DAC960PL
799 * doesn't seem to do this right.
800 */
801 if ((mlx->mlx_flags & MLXF_PAUSEWORKS) == 0)
802 return (EOPNOTSUPP);
803
804 mp = (struct mlx_pause *)data;
805
806 if ((mp->mp_which == MLX_PAUSE_CANCEL) &&
807 (mlx->mlx_pause.mp_when != 0)) {
808 /* Cancel a pending pause operation. */
809 mlx->mlx_pause.mp_which = 0;
810 break;
811 }
812
813 /* Fix for legal channels. */
814 mp->mp_which &= ((1 << mlx->mlx_ci.ci_nchan) -1);
815
816 /* Check time values. */
817 if (mp->mp_when < 0 || mp->mp_when > 3600 ||
818 mp->mp_howlong < 1 || mp->mp_howlong > (0xf * 30)) {
819 rv = EINVAL;
820 break;
821 }
822
823 /* Check for a pause currently running. */
824 if ((mlx->mlx_pause.mp_which != 0) &&
825 (mlx->mlx_pause.mp_when == 0)) {
826 rv = EBUSY;
827 break;
828 }
829
830 /* Looks ok, go with it. */
831 mlx->mlx_pause.mp_which = mp->mp_which;
832 mlx->mlx_pause.mp_when = mlx_curtime() + mp->mp_when;
833 mlx->mlx_pause.mp_howlong =
834 mlx->mlx_pause.mp_when + mp->mp_howlong;
835
836 return (0);
837
838 case MLX_COMMAND:
839 /*
840 * Accept a command passthrough-style.
841 */
842 return (mlx_user_command(mlx, (struct mlx_usercommand *)data));
843
844 case MLX_REBUILDASYNC:
845 /*
846 * Start a rebuild on a given SCSI disk
847 */
848 if (mlx->mlx_bg != 0) {
849 rb->rr_status = 0x0106;
850 rv = EBUSY;
851 break;
852 }
853
854 rb->rr_status = mlx_rebuild(mlx, rb->rr_channel, rb->rr_target);
855 switch (rb->rr_status) {
856 case 0:
857 rv = 0;
858 break;
859 case 0x10000:
860 rv = ENOMEM; /* Couldn't set up the command. */
861 break;
862 case 0x0002:
863 rv = EBUSY;
864 break;
865 case 0x0104:
866 rv = EIO;
867 break;
868 case 0x0105:
869 rv = ERANGE;
870 break;
871 case 0x0106:
872 rv = EBUSY;
873 break;
874 default:
875 rv = EINVAL;
876 break;
877 }
878
879 if (rv == 0)
880 mlx->mlx_bg = MLX_BG_REBUILD;
881
882 return (0);
883
884 case MLX_REBUILDSTAT:
885 /*
886 * Get the status of the current rebuild or consistency check.
887 */
888 *rs = mlx->mlx_rebuildstat;
889 return (0);
890
891 case MLX_GET_SYSDRIVE:
892 /*
893 * Return the system drive number matching the `ld' device
894 * unit in (arg), if it happens to belong to us.
895 */
896 for (i = 0; i < MLX_MAX_DRIVES; i++) {
897 ms = &mlx->mlx_sysdrive[i];
898 if (ms->ms_dv != NULL)
899 if (ms->ms_dv->dv_xname[2] == '' + *arg) {
900 *arg = i;
901 return (0);
902 }
903 }
904 return (ENOENT);
905
906 case MLX_GET_CINFO:
907 /*
908 * Return controller info.
909 */
910 memcpy(arg, &mlx->mlx_ci, sizeof(mlx->mlx_ci));
911 return (0);
912 }
913
914 switch (cmd) {
915 case MLXD_DETACH:
916 case MLXD_STATUS:
917 case MLXD_CHECKASYNC:
918 if ((u_int)*arg >= MLX_MAX_DRIVES)
919 return (EINVAL);
920 ms = &mlx->mlx_sysdrive[*arg];
921 if (*arg > MLX_MAX_DRIVES || ms->ms_dv == NULL)
922 return (ENOENT);
923 break;
924
925 default:
926 return (ENOTTY);
927 }
928
929 switch (cmd) {
930 case MLXD_DETACH:
931 /*
932 * Disconnect from the specified drive; it may be about to go
933 * away.
934 */
935 return (config_detach(ms->ms_dv, 0));
936
937 case MLXD_STATUS:
938 /*
939 * Return the current status of this drive.
940 */
941 *arg = ms->ms_state;
942 return (0);
943
944 case MLXD_CHECKASYNC:
945 /*
946 * Start a background consistency check on this drive.
947 */
948 if (mlx->mlx_bg != 0) {
949 *arg = 0x0106;
950 return (EBUSY);
951 }
952
953 switch (result = mlx_check(mlx, *arg)) {
954 case 0:
955 rv = 0;
956 break;
957 case 0x10000:
958 rv = ENOMEM; /* Couldn't set up the command. */
959 break;
960 case 0x0002:
961 rv = EIO;
962 break;
963 case 0x0105:
964 rv = ERANGE;
965 break;
966 case 0x0106:
967 rv = EBUSY;
968 break;
969 default:
970 rv = EINVAL;
971 break;
972 }
973
974 if (rv == 0)
975 mlx->mlx_bg = MLX_BG_CHECK;
976 *arg = result;
977 return (rv);
978 }
979
980 return (ENOTTY); /* XXX shut up gcc */
981 }
982
983 /*
984 * Fire off commands to periodically check the status of connected drives.
985 * Check for commands that have timed out.
986 */
987 static void
988 mlx_periodic_create(void *cookie)
989 {
990 int rv;
991
992 rv = kthread_create1(mlx_periodic_thread, NULL, &mlx_periodic_proc,
993 "mlxtask");
994 if (rv == 0)
995 return;
996
997 printf("mlx_periodic_create: unable to create thread (%d)\n", rv);
998 }
999
1000 static void
1001 mlx_periodic_thread(void *cookie)
1002 {
1003 struct mlx_softc *mlx;
1004 int i;
1005
1006 for (;;) {
1007 for (i = 0; i < mlx_cd.cd_ndevs; i++)
1008 if ((mlx = device_lookup(&mlx_cd, i)) != NULL)
1009 if (mlx->mlx_ci.ci_iftype > 1)
1010 mlx_periodic(mlx);
1011
1012 tsleep(mlx_periodic_thread, PWAIT, "mlxzzz", hz * 2);
1013 }
1014 }
1015
1016 static void
1017 mlx_periodic(struct mlx_softc *mlx)
1018 {
1019 struct mlx_ccb *mc, *nmc;
1020 int etype, s;
1021 time_t ct;
1022
1023 ct = mlx_curtime();
1024
1025 if ((mlx->mlx_pause.mp_which != 0) &&
1026 (mlx->mlx_pause.mp_when > 0) &&
1027 (ct >= mlx->mlx_pause.mp_when)) {
1028 /*
1029 * Start bus pause.
1030 */
1031 mlx_pause_action(mlx);
1032 mlx->mlx_pause.mp_when = 0;
1033 } else if ((mlx->mlx_pause.mp_which != 0) &&
1034 (mlx->mlx_pause.mp_when == 0)) {
1035 /*
1036 * Stop pause if required.
1037 */
1038 if (ct >= mlx->mlx_pause.mp_howlong) {
1039 mlx_pause_action(mlx);
1040 mlx->mlx_pause.mp_which = 0;
1041 }
1042 } else if (ct > (mlx->mlx_lastpoll + 10)) {
1043 /*
1044 * Run normal periodic activities...
1045 */
1046 mlx->mlx_lastpoll = ct;
1047
1048 /*
1049 * Check controller status.
1050 */
1051 if ((mlx->mlx_flags & MLXF_PERIODIC_CTLR) == 0) {
1052 mlx->mlx_flags |= MLXF_PERIODIC_CTLR;
1053
1054 if (mlx->mlx_ci.ci_iftype <= 2)
1055 etype = MLX_CMD_ENQUIRY_OLD;
1056 else
1057 etype = MLX_CMD_ENQUIRY;
1058
1059 mlx_enquire(mlx, etype, max(sizeof(struct mlx_enquiry),
1060 sizeof(struct mlx_enquiry_old)),
1061 mlx_periodic_enquiry, 1);
1062 }
1063
1064 /*
1065 * Check system drive status.
1066 */
1067 if ((mlx->mlx_flags & MLXF_PERIODIC_DRIVE) == 0) {
1068 mlx->mlx_flags |= MLXF_PERIODIC_DRIVE;
1069 mlx_enquire(mlx, MLX_CMD_ENQSYSDRIVE,
1070 sizeof(struct mlx_enq_sys_drive) * MLX_MAX_DRIVES,
1071 mlx_periodic_enquiry, 1);
1072 }
1073 }
1074
1075 /*
1076 * Get drive rebuild/check status.
1077 */
1078 if ((mlx->mlx_flags & MLXF_PERIODIC_REBUILD) == 0) {
1079 mlx->mlx_flags |= MLXF_PERIODIC_REBUILD;
1080 mlx_enquire(mlx, MLX_CMD_REBUILDSTAT,
1081 sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild, 1);
1082 }
1083
1084 /*
1085 * Time-out busy CCBs.
1086 */
1087 s = splbio();
1088 for (mc = TAILQ_FIRST(&mlx->mlx_ccb_worklist); mc != NULL; mc = nmc) {
1089 nmc = TAILQ_NEXT(mc, mc_chain.tailq);
1090 if (mc->mc_expiry > ct) {
1091 /*
1092 * The remaining CCBs will expire after this one, so
1093 * there's no point in going further.
1094 */
1095 break;
1096 }
1097 TAILQ_REMOVE(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq);
1098 mc->mc_status = MLX_STATUS_LOST;
1099 if (mc->mc_mx.mx_handler != NULL)
1100 (*mc->mc_mx.mx_handler)(mc);
1101 else if ((mc->mc_flags & MC_WAITING) != 0)
1102 wakeup(mc);
1103 }
1104 splx(s);
1105 }
1106
1107 /*
1108 * Handle the result of an ENQUIRY command instigated by periodic status
1109 * polling.
1110 */
1111 static void
1112 mlx_periodic_enquiry(struct mlx_ccb *mc)
1113 {
1114 struct mlx_softc *mlx;
1115 struct mlx_enquiry *me;
1116 struct mlx_enquiry_old *meo;
1117 struct mlx_enq_sys_drive *mes;
1118 struct mlx_sysdrive *dr;
1119 const char *statestr;
1120 int i, j;
1121 u_int lsn;
1122
1123 mlx = (struct mlx_softc *)mc->mc_mx.mx_dv;
1124 mlx_ccb_unmap(mlx, mc);
1125
1126 /*
1127 * Command completed OK?
1128 */
1129 if (mc->mc_status != 0) {
1130 printf("%s: periodic enquiry failed - %s\n",
1131 mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc));
1132 goto out;
1133 }
1134
1135 /*
1136 * Respond to command.
1137 */
1138 switch (mc->mc_mbox[0]) {
1139 case MLX_CMD_ENQUIRY_OLD:
1140 /*
1141 * This is currently a bit fruitless, as we don't know how
1142 * to extract the eventlog pointer yet.
1143 */
1144 me = (struct mlx_enquiry *)mc->mc_mx.mx_context;
1145 meo = (struct mlx_enquiry_old *)mc->mc_mx.mx_context;
1146
1147 /* Convert data in-place to new format */
1148 i = sizeof(me->me_dead) / sizeof(me->me_dead[0]);
1149 while (--i >= 0) {
1150 me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
1151 me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
1152 }
1153
1154 me->me_misc_flags = 0;
1155 me->me_rebuild_count = meo->me_rebuild_count;
1156 me->me_dead_count = meo->me_dead_count;
1157 me->me_critical_sd_count = meo->me_critical_sd_count;
1158 me->me_event_log_seq_num = 0;
1159 me->me_offline_sd_count = meo->me_offline_sd_count;
1160 me->me_max_commands = meo->me_max_commands;
1161 me->me_rebuild_flag = meo->me_rebuild_flag;
1162 me->me_fwmajor = meo->me_fwmajor;
1163 me->me_fwminor = meo->me_fwminor;
1164 me->me_status_flags = meo->me_status_flags;
1165 me->me_flash_age = meo->me_flash_age;
1166
1167 i = sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0]);
1168 j = sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0]);
1169
1170 while (--i >= 0) {
1171 if (i >= j)
1172 me->me_drvsize[i] = 0;
1173 else
1174 me->me_drvsize[i] = meo->me_drvsize[i];
1175 }
1176
1177 me->me_num_sys_drvs = meo->me_num_sys_drvs;
1178
1179 /* FALLTHROUGH */
1180
1181 case MLX_CMD_ENQUIRY:
1182 /*
1183 * Generic controller status update. We could do more with
1184 * this than just checking the event log.
1185 */
1186 me = (struct mlx_enquiry *)mc->mc_mx.mx_context;
1187 lsn = le16toh(me->me_event_log_seq_num);
1188
1189 if (mlx->mlx_currevent == -1) {
1190 /* Initialise our view of the event log. */
1191 mlx->mlx_currevent = lsn;
1192 mlx->mlx_lastevent = lsn;
1193 } else if (lsn != mlx->mlx_lastevent &&
1194 (mlx->mlx_flags & MLXF_EVENTLOG_BUSY) == 0) {
1195 /* Record where current events are up to */
1196 mlx->mlx_currevent = lsn;
1197
1198 /* Mark the event log as busy. */
1199 mlx->mlx_flags |= MLXF_EVENTLOG_BUSY;
1200
1201 /* Drain new eventlog entries. */
1202 mlx_periodic_eventlog_poll(mlx);
1203 }
1204 break;
1205
1206 case MLX_CMD_ENQSYSDRIVE:
1207 /*
1208 * Perform drive status comparison to see if something
1209 * has failed. Don't perform the comparison if we're
1210 * reconfiguring, since the system drive table will be
1211 * changing.
1212 */
1213 if ((mlx->mlx_flags & MLXF_RESCANNING) != 0)
1214 break;
1215
1216 mes = (struct mlx_enq_sys_drive *)mc->mc_mx.mx_context;
1217 dr = &mlx->mlx_sysdrive[0];
1218
1219 for (i = 0; i < mlx->mlx_numsysdrives; i++) {
1220 /* Has state been changed by controller? */
1221 if (dr->ms_state != mes[i].sd_state) {
1222 switch (mes[i].sd_state) {
1223 case MLX_SYSD_OFFLINE:
1224 statestr = "offline";
1225 break;
1226
1227 case MLX_SYSD_ONLINE:
1228 statestr = "online";
1229 break;
1230
1231 case MLX_SYSD_CRITICAL:
1232 statestr = "critical";
1233 break;
1234
1235 default:
1236 statestr = "unknown";
1237 break;
1238 }
1239
1240 printf("%s: unit %d %s\n", mlx->mlx_dv.dv_xname,
1241 i, statestr);
1242
1243 /* Save new state. */
1244 dr->ms_state = mes[i].sd_state;
1245 }
1246 }
1247 break;
1248
1249 #ifdef DIAGNOSTIC
1250 default:
1251 printf("%s: mlx_periodic_enquiry: eh?\n",
1252 mlx->mlx_dv.dv_xname);
1253 break;
1254 #endif
1255 }
1256
1257 out:
1258 if (mc->mc_mbox[0] == MLX_CMD_ENQSYSDRIVE)
1259 mlx->mlx_flags &= ~MLXF_PERIODIC_DRIVE;
1260 else
1261 mlx->mlx_flags &= ~MLXF_PERIODIC_CTLR;
1262
1263 free(mc->mc_mx.mx_context, M_DEVBUF);
1264 mlx_ccb_free(mlx, mc);
1265 }
1266
1267 /*
1268 * Instigate a poll for one event log message on (mlx). We only poll for
1269 * one message at a time, to keep our command usage down.
1270 */
1271 static void
1272 mlx_periodic_eventlog_poll(struct mlx_softc *mlx)
1273 {
1274 struct mlx_ccb *mc;
1275 void *result;
1276 int rv;
1277
1278 result = NULL;
1279
1280 if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0)
1281 goto out;
1282
1283 if ((result = malloc(1024, M_DEVBUF, M_WAITOK)) == NULL) {
1284 rv = ENOMEM;
1285 goto out;
1286 }
1287 if ((rv = mlx_ccb_map(mlx, mc, result, 1024, MC_XFER_IN)) != 0)
1288 goto out;
1289 if (mc->mc_nsgent != 1) {
1290 mlx_ccb_unmap(mlx, mc);
1291 printf("mlx_periodic_eventlog_poll: too many segs\n");
1292 goto out;
1293 }
1294
1295 /* Build the command to get one log entry. */
1296 mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
1297 mlx->mlx_lastevent, 0, 0, mc->mc_xfer_phys, 0);
1298
1299 mc->mc_mx.mx_handler = mlx_periodic_eventlog_respond;
1300 mc->mc_mx.mx_dv = &mlx->mlx_dv;
1301 mc->mc_mx.mx_context = result;
1302
1303 /* Start the command. */
1304 mlx_ccb_enqueue(mlx, mc);
1305
1306 out:
1307 if (rv != 0) {
1308 if (mc != NULL)
1309 mlx_ccb_free(mlx, mc);
1310 if (result != NULL)
1311 free(result, M_DEVBUF);
1312 }
1313 }
1314
1315 /*
1316 * Handle the result of polling for a log message, generate diagnostic
1317 * output. If this wasn't the last message waiting for us, we'll go collect
1318 * another.
1319 */
1320 static void
1321 mlx_periodic_eventlog_respond(struct mlx_ccb *mc)
1322 {
1323 struct mlx_softc *mlx;
1324 struct mlx_eventlog_entry *el;
1325 const char *reason;
1326 u_int8_t sensekey, chan, targ;
1327
1328 mlx = (struct mlx_softc *)mc->mc_mx.mx_dv;
1329 el = mc->mc_mx.mx_context;
1330 mlx_ccb_unmap(mlx, mc);
1331
1332 mlx->mlx_lastevent++;
1333
1334 if (mc->mc_status == 0) {
1335 switch (el->el_type) {
1336 case MLX_LOGMSG_SENSE: /* sense data */
1337 sensekey = el->el_sense & 0x0f;
1338 chan = (el->el_target >> 4) & 0x0f;
1339 targ = el->el_target & 0x0f;
1340
1341 /*
1342 * This is the only sort of message we understand at
1343 * the moment. The tests here are probably
1344 * incomplete.
1345 */
1346
1347 /*
1348 * Mylex vendor-specific message indicating a drive
1349 * was killed?
1350 */
1351 if (sensekey == 9 && el->el_asc == 0x80) {
1352 if (el->el_asq < sizeof(mlx_sense_msgs) /
1353 sizeof(mlx_sense_msgs[0]))
1354 reason = mlx_sense_msgs[el->el_asq];
1355 else
1356 reason = "for unknown reason";
1357
1358 printf("%s: physical drive %d:%d killed %s\n",
1359 mlx->mlx_dv.dv_xname, chan, targ, reason);
1360 }
1361
1362 /*
1363 * SCSI drive was reset?
1364 */
1365 if (sensekey == 6 && el->el_asc == 0x29)
1366 printf("%s: physical drive %d:%d reset\n",
1367 mlx->mlx_dv.dv_xname, chan, targ);
1368
1369 /*
1370 * SCSI drive error?
1371 */
1372 if (!(sensekey == 0 ||
1373 (sensekey == 2 &&
1374 el->el_asc == 0x04 &&
1375 (el->el_asq == 0x01 || el->el_asq == 0x02)))) {
1376 printf("%s: physical drive %d:%d error log: "
1377 "sense = %d asc = %x asq = %x\n",
1378 mlx->mlx_dv.dv_xname, chan, targ, sensekey,
1379 el->el_asc, el->el_asq);
1380 printf("%s: info = %d:%d:%d:%d "
1381 " csi = %d:%d:%d:%d\n",
1382 mlx->mlx_dv.dv_xname,
1383 el->el_information[0],
1384 el->el_information[1],
1385 el->el_information[2],
1386 el->el_information[3],
1387 el->el_csi[0], el->el_csi[1],
1388 el->el_csi[2], el->el_csi[3]);
1389 }
1390
1391 break;
1392
1393 default:
1394 printf("%s: unknown log message type 0x%x\n",
1395 mlx->mlx_dv.dv_xname, el->el_type);
1396 break;
1397 }
1398 } else {
1399 printf("%s: error reading message log - %s\n",
1400 mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc));
1401
1402 /*
1403 * Give up on all the outstanding messages, as we may have
1404 * come unsynched.
1405 */
1406 mlx->mlx_lastevent = mlx->mlx_currevent;
1407 }
1408
1409 free(mc->mc_mx.mx_context, M_DEVBUF);
1410 mlx_ccb_free(mlx, mc);
1411
1412 /*
1413 * Is there another message to obtain?
1414 */
1415 if (mlx->mlx_lastevent != mlx->mlx_currevent)
1416 mlx_periodic_eventlog_poll(mlx);
1417 else
1418 mlx->mlx_flags &= ~MLXF_EVENTLOG_BUSY;
1419 }
1420
1421 /*
1422 * Handle check/rebuild operations in progress.
1423 */
1424 static void
1425 mlx_periodic_rebuild(struct mlx_ccb *mc)
1426 {
1427 struct mlx_softc *mlx;
1428 const char *opstr;
1429 struct mlx_rebuild_status *mr;
1430
1431 mlx = (struct mlx_softc *)mc->mc_mx.mx_dv;
1432 mr = mc->mc_mx.mx_context;
1433 mlx_ccb_unmap(mlx, mc);
1434
1435 switch (mc->mc_status) {
1436 case 0:
1437 /*
1438 * Operation running, update stats.
1439 */
1440 mlx->mlx_rebuildstat = *mr;
1441
1442 /* Spontaneous rebuild/check? */
1443 if (mlx->mlx_bg == 0) {
1444 mlx->mlx_bg = MLX_BG_SPONTANEOUS;
1445 printf("%s: background check/rebuild started\n",
1446 mlx->mlx_dv.dv_xname);
1447 }
1448 break;
1449
1450 case 0x0105:
1451 /*
1452 * Nothing running, finalise stats and report.
1453 */
1454 switch (mlx->mlx_bg) {
1455 case MLX_BG_CHECK:
1456 /* XXX Print drive? */
1457 opstr = "consistency check";
1458 break;
1459
1460 case MLX_BG_REBUILD:
1461 /* XXX Print channel:target? */
1462 opstr = "drive rebuild";
1463 break;
1464
1465 case MLX_BG_SPONTANEOUS:
1466 default:
1467 /*
1468 * If we have previously been non-idle, report the
1469 * transition
1470 */
1471 if (mlx->mlx_rebuildstat.rs_code !=
1472 MLX_REBUILDSTAT_IDLE)
1473 opstr = "background check/rebuild";
1474 else
1475 opstr = NULL;
1476 }
1477
1478 if (opstr != NULL)
1479 printf("%s: %s completed\n", mlx->mlx_dv.dv_xname,
1480 opstr);
1481
1482 mlx->mlx_bg = 0;
1483 mlx->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
1484 break;
1485 }
1486
1487 free(mc->mc_mx.mx_context, M_DEVBUF);
1488 mlx_ccb_free(mlx, mc);
1489 mlx->mlx_flags &= ~MLXF_PERIODIC_REBUILD;
1490 }
1491
1492 /*
1493 * It's time to perform a channel pause action for (mlx), either start or
1494 * stop the pause.
1495 */
1496 static void
1497 mlx_pause_action(struct mlx_softc *mlx)
1498 {
1499 struct mlx_ccb *mc;
1500 int failsafe, i, cmd;
1501 time_t ct;
1502
1503 ct = mlx_curtime();
1504
1505 /* What are we doing here? */
1506 if (mlx->mlx_pause.mp_when == 0) {
1507 cmd = MLX_CMD_STARTCHANNEL;
1508 failsafe = 0;
1509 } else {
1510 cmd = MLX_CMD_STOPCHANNEL;
1511
1512 /*
1513 * Channels will always start again after the failsafe
1514 * period, which is specified in multiples of 30 seconds.
1515 * This constrains us to a maximum pause of 450 seconds.
1516 */
1517 failsafe = ((mlx->mlx_pause.mp_howlong - ct) + 5) / 30;
1518
1519 if (failsafe > 0xf) {
1520 failsafe = 0xf;
1521 mlx->mlx_pause.mp_howlong = ct + (0xf * 30) - 5;
1522 }
1523 }
1524
1525 /* Build commands for every channel requested. */
1526 for (i = 0; i < mlx->mlx_ci.ci_nchan; i++) {
1527 if ((1 << i) & mlx->mlx_pause.mp_which) {
1528 if (mlx_ccb_alloc(mlx, &mc, 1) != 0) {
1529 printf("%s: %s failed for channel %d\n",
1530 mlx->mlx_dv.dv_xname,
1531 cmd == MLX_CMD_STOPCHANNEL ?
1532 "pause" : "resume", i);
1533 continue;
1534 }
1535
1536 /* Build the command. */
1537 mlx_make_type2(mc, cmd, (failsafe << 4) | i, 0, 0,
1538 0, 0, 0, 0, 0);
1539 mc->mc_mx.mx_handler = mlx_pause_done;
1540 mc->mc_mx.mx_dv = &mlx->mlx_dv;
1541
1542 mlx_ccb_enqueue(mlx, mc);
1543 }
1544 }
1545 }
1546
1547 static void
1548 mlx_pause_done(struct mlx_ccb *mc)
1549 {
1550 struct mlx_softc *mlx;
1551 int command, channel;
1552
1553 mlx = (struct mlx_softc *)mc->mc_mx.mx_dv;
1554 command = mc->mc_mbox[0];
1555 channel = mc->mc_mbox[2] & 0xf;
1556
1557 if (mc->mc_status != 0)
1558 printf("%s: %s command failed - %s\n", mlx->mlx_dv.dv_xname,
1559 command == MLX_CMD_STOPCHANNEL ? "pause" : "resume",
1560 mlx_ccb_diagnose(mc));
1561 else if (command == MLX_CMD_STOPCHANNEL)
1562 printf("%s: channel %d pausing for %ld seconds\n",
1563 mlx->mlx_dv.dv_xname, channel,
1564 (long)(mlx->mlx_pause.mp_howlong - mlx_curtime()));
1565 else
1566 printf("%s: channel %d resuming\n", mlx->mlx_dv.dv_xname,
1567 channel);
1568
1569 mlx_ccb_free(mlx, mc);
1570 }
1571
1572 /*
1573 * Perform an Enquiry command using a type-3 command buffer and a return a
1574 * single linear result buffer. If the completion function is specified, it
1575 * will be called with the completed command (and the result response will
1576 * not be valid until that point). Otherwise, the command will either be
1577 * busy-waited for (interrupts must be blocked), or slept for.
1578 */
1579 static void *
1580 mlx_enquire(struct mlx_softc *mlx, int command, size_t bufsize,
1581 void (*handler)(struct mlx_ccb *mc), int waitok)
1582 {
1583 struct mlx_ccb *mc;
1584 void *result;
1585 int rv, mapped;
1586
1587 result = NULL;
1588 mapped = 0;
1589
1590 if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0)
1591 goto out;
1592
1593 result = malloc(bufsize, M_DEVBUF, waitok ? M_WAITOK : M_NOWAIT);
1594 if (result == NULL) {
1595 printf("mlx_enquire: malloc() failed\n");
1596 goto out;
1597 }
1598 if ((rv = mlx_ccb_map(mlx, mc, result, bufsize, MC_XFER_IN)) != 0)
1599 goto out;
1600 mapped = 1;
1601 if (mc->mc_nsgent != 1) {
1602 printf("mlx_enquire: too many segs\n");
1603 goto out;
1604 }
1605
1606 /* Build an enquiry command. */
1607 mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_xfer_phys, 0);
1608
1609 /* Do we want a completion callback? */
1610 if (handler != NULL) {
1611 mc->mc_mx.mx_context = result;
1612 mc->mc_mx.mx_dv = &mlx->mlx_dv;
1613 mc->mc_mx.mx_handler = handler;
1614 mlx_ccb_enqueue(mlx, mc);
1615 } else {
1616 /* Run the command in either polled or wait mode. */
1617 if (waitok)
1618 rv = mlx_ccb_wait(mlx, mc);
1619 else
1620 rv = mlx_ccb_poll(mlx, mc, 5000);
1621 }
1622
1623 out:
1624 /* We got a command, but nobody else will free it. */
1625 if (handler == NULL && mc != NULL) {
1626 if (mapped)
1627 mlx_ccb_unmap(mlx, mc);
1628 mlx_ccb_free(mlx, mc);
1629 }
1630
1631 /* We got an error, and we allocated a result. */
1632 if (rv != 0 && result != NULL) {
1633 if (handler != NULL && mc != NULL) {
1634 if (mapped)
1635 mlx_ccb_unmap(mlx, mc);
1636 mlx_ccb_free(mlx, mc);
1637 }
1638 free(result, M_DEVBUF);
1639 result = NULL;
1640 }
1641
1642 return (result);
1643 }
1644
1645 /*
1646 * Perform a Flush command on the nominated controller.
1647 *
1648 * May be called with interrupts enabled or disabled; will not return until
1649 * the flush operation completes or fails.
1650 */
1651 int
1652 mlx_flush(struct mlx_softc *mlx, int async)
1653 {
1654 struct mlx_ccb *mc;
1655 int rv;
1656
1657 if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0)
1658 goto out;
1659
1660 /* Build a flush command and fire it off. */
1661 mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
1662
1663 if (async)
1664 rv = mlx_ccb_wait(mlx, mc);
1665 else
1666 rv = mlx_ccb_poll(mlx, mc, MLX_TIMEOUT * 1000);
1667 if (rv != 0)
1668 goto out;
1669
1670 /* Command completed OK? */
1671 if (mc->mc_status != 0) {
1672 printf("%s: FLUSH failed - %s\n", mlx->mlx_dv.dv_xname,
1673 mlx_ccb_diagnose(mc));
1674 rv = EIO;
1675 }
1676 out:
1677 if (mc != NULL)
1678 mlx_ccb_free(mlx, mc);
1679
1680 return (rv);
1681 }
1682
1683 /*
1684 * Start a background consistency check on (drive).
1685 */
1686 static int
1687 mlx_check(struct mlx_softc *mlx, int drive)
1688 {
1689 struct mlx_ccb *mc;
1690 int rv;
1691
1692 /* Get ourselves a command buffer. */
1693 rv = 0x10000;
1694
1695 if (mlx_ccb_alloc(mlx, &mc, 1) != 0)
1696 goto out;
1697
1698 /* Build a checkasync command, set the "fix it" flag. */
1699 mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80,
1700 0, 0);
1701
1702 /* Start the command and wait for it to be returned. */
1703 if (mlx_ccb_wait(mlx, mc) != 0)
1704 goto out;
1705
1706 /* Command completed OK? */
1707 if (mc->mc_status != 0)
1708 printf("%s: CHECK ASYNC failed - %s\n", mlx->mlx_dv.dv_xname,
1709 mlx_ccb_diagnose(mc));
1710 else
1711 printf("%s: consistency check started",
1712 mlx->mlx_sysdrive[drive].ms_dv->dv_xname);
1713
1714 rv = mc->mc_status;
1715 out:
1716 if (mc != NULL)
1717 mlx_ccb_free(mlx, mc);
1718
1719 return (rv);
1720 }
1721
1722 /*
1723 * Start a background rebuild of the physical drive at (channel),(target).
1724 *
1725 * May be called with interrupts enabled or disabled; will return as soon as
1726 * the operation has started or been refused.
1727 */
1728 static int
1729 mlx_rebuild(struct mlx_softc *mlx, int channel, int target)
1730 {
1731 struct mlx_ccb *mc;
1732 int error;
1733
1734 error = 0x10000;
1735 if (mlx_ccb_alloc(mlx, &mc, 1) != 0)
1736 goto out;
1737
1738 /* Build a rebuildasync command, set the "fix it" flag. */
1739 mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0,
1740 0, 0);
1741
1742 /* Start the command and wait for it to be returned. */
1743 if (mlx_ccb_wait(mlx, mc) != 0)
1744 goto out;
1745
1746 /* Command completed OK? */
1747 printf("%s: ", mlx->mlx_dv.dv_xname);
1748 if (mc->mc_status != 0)
1749 printf("REBUILD ASYNC failed - %s\n", mlx_ccb_diagnose(mc));
1750 else
1751 printf("rebuild started for %d:%d\n", channel, target);
1752
1753 error = mc->mc_status;
1754
1755 out:
1756 if (mc != NULL)
1757 mlx_ccb_free(mlx, mc);
1758
1759 return (error);
1760 }
1761
1762 /*
1763 * Take a command from user-space and try to run it.
1764 *
1765 * XXX Note that this can't perform very much in the way of error checking,
1766 * XXX and as such, applications _must_ be considered trustworthy.
1767 *
1768 * XXX Commands using S/G for data are not supported.
1769 */
1770 static int
1771 mlx_user_command(struct mlx_softc *mlx, struct mlx_usercommand *mu)
1772 {
1773 struct mlx_ccb *mc;
1774 struct mlx_dcdb *dcdb;
1775 void *kbuf;
1776 int rv, mapped;
1777
1778 if ((mu->mu_bufdir & ~MU_XFER_MASK) != 0)
1779 return (EINVAL);
1780
1781 kbuf = NULL;
1782 dcdb = NULL;
1783 mapped = 0;
1784
1785 /* Get ourselves a command and copy in from user space. */
1786 if ((rv = mlx_ccb_alloc(mlx, &mc, 1)) != 0) {
1787 DPRINTF(("mlx_user_command: mlx_ccb_alloc = %d\n", rv));
1788 goto out;
1789 }
1790
1791 memcpy(mc->mc_mbox, mu->mu_command, sizeof(mc->mc_mbox));
1792
1793 /*
1794 * If we need a buffer for data transfer, allocate one and copy in
1795 * its initial contents.
1796 */
1797 if (mu->mu_datasize > 0) {
1798 if (mu->mu_datasize > MAXPHYS)
1799 return (EINVAL);
1800
1801 kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK);
1802 if (kbuf == NULL) {
1803 DPRINTF(("mlx_user_command: malloc = NULL\n"));
1804 rv = ENOMEM;
1805 goto out;
1806 }
1807
1808 if ((mu->mu_bufdir & MU_XFER_OUT) != 0) {
1809 rv = copyin(mu->mu_buf, kbuf, mu->mu_datasize);
1810 if (rv != 0) {
1811 DPRINTF(("mlx_user_command: copyin = %d\n",
1812 rv));
1813 goto out;
1814 }
1815 }
1816
1817 /* Map the buffer so the controller can see it. */
1818 rv = mlx_ccb_map(mlx, mc, kbuf, mu->mu_datasize, mu->mu_bufdir);
1819 if (rv != 0) {
1820 DPRINTF(("mlx_user_command: mlx_ccb_map = %d\n", rv));
1821 goto out;
1822 }
1823 if (mc->mc_nsgent > 1) {
1824 DPRINTF(("mlx_user_command: too many s/g entries\n"));
1825 rv = EFBIG;
1826 goto out;
1827 }
1828 mapped = 1;
1829 }
1830
1831 /*
1832 * If this is a passthrough SCSI command, the DCDB is packed at the
1833 * beginning of the data area. Fix up the DCDB to point to the correct physical
1834 * address and override any bufptr supplied by the caller since we know
1835 * what it's meant to be.
1836 */
1837 if (mc->mc_mbox[0] == MLX_CMD_DIRECT_CDB) {
1838 dcdb = (struct mlx_dcdb *)kbuf;
1839 dcdb->dcdb_physaddr = mc->mc_xfer_phys + sizeof(*dcdb);
1840 mu->mu_bufptr = 8;
1841 }
1842
1843 /*
1844 * If there's a data buffer, fix up the command's buffer pointer.
1845 */
1846 if (mu->mu_datasize > 0) {
1847 /* Range check the pointer to physical buffer address. */
1848 if (mu->mu_bufptr < 0 ||
1849 mu->mu_bufptr > sizeof(mu->mu_command) - 4) {
1850 DPRINTF(("mlx_user_command: bufptr botch\n"));
1851 rv = EINVAL;
1852 goto out;
1853 }
1854
1855 mc->mc_mbox[mu->mu_bufptr] = mc->mc_xfer_phys;
1856 mc->mc_mbox[mu->mu_bufptr+1] = mc->mc_xfer_phys >> 8;
1857 mc->mc_mbox[mu->mu_bufptr+2] = mc->mc_xfer_phys >> 16;
1858 mc->mc_mbox[mu->mu_bufptr+3] = mc->mc_xfer_phys >> 24;
1859 }
1860
1861 /* Submit the command and wait. */
1862 if ((rv = mlx_ccb_wait(mlx, mc)) != 0) {
1863 #ifdef DEBUG
1864 printf("mlx_user_command: mlx_ccb_wait = %d\n", rv);
1865 #endif
1866 }
1867
1868 out:
1869 if (mc != NULL) {
1870 if (mapped)
1871 mlx_ccb_unmap(mlx, mc);
1872 mlx_ccb_free(mlx, mc);
1873 }
1874
1875 /* Copy out status and data */
1876 mu->mu_status = mc->mc_status;
1877
1878 if (kbuf != NULL) {
1879 if (mu->mu_datasize > 0 && (mu->mu_bufdir & MU_XFER_IN) != 0) {
1880 rv = copyout(kbuf, mu->mu_buf, mu->mu_datasize);
1881 #ifdef DIAGNOSTIC
1882 if (rv != 0)
1883 printf("mlx_user_command: copyout = %d\n", rv);
1884 #endif
1885 }
1886 }
1887 if (kbuf != NULL)
1888 free(kbuf, M_DEVBUF);
1889
1890 return (rv);
1891 }
1892
1893 /*
1894 * Allocate and initialise a CCB.
1895 */
1896 int
1897 mlx_ccb_alloc(struct mlx_softc *mlx, struct mlx_ccb **mcp, int control)
1898 {
1899 struct mlx_ccb *mc;
1900 int s;
1901
1902 s = splbio();
1903 mc = SLIST_FIRST(&mlx->mlx_ccb_freelist);
1904 if (control) {
1905 if (mlx->mlx_nccbs_ctrl >= MLX_NCCBS_CONTROL) {
1906 splx(s);
1907 *mcp = NULL;
1908 return (EAGAIN);
1909 }
1910 mc->mc_flags |= MC_CONTROL;
1911 mlx->mlx_nccbs_ctrl++;
1912 }
1913 SLIST_REMOVE_HEAD(&mlx->mlx_ccb_freelist, mc_chain.slist);
1914 splx(s);
1915
1916 *mcp = mc;
1917 return (0);
1918 }
1919
1920 /*
1921 * Free a CCB.
1922 */
1923 void
1924 mlx_ccb_free(struct mlx_softc *mlx, struct mlx_ccb *mc)
1925 {
1926 int s;
1927
1928 s = splbio();
1929 if ((mc->mc_flags & MC_CONTROL) != 0)
1930 mlx->mlx_nccbs_ctrl--;
1931 mc->mc_flags = 0;
1932 SLIST_INSERT_HEAD(&mlx->mlx_ccb_freelist, mc, mc_chain.slist);
1933 splx(s);
1934 }
1935
1936 /*
1937 * If a CCB is specified, enqueue it. Pull CCBs off the software queue in
1938 * the order that they were enqueued and try to submit their mailboxes to
1939 * the controller for execution.
1940 */
1941 void
1942 mlx_ccb_enqueue(struct mlx_softc *mlx, struct mlx_ccb *mc)
1943 {
1944 int s;
1945
1946 s = splbio();
1947
1948 if (mc != NULL)
1949 SIMPLEQ_INSERT_TAIL(&mlx->mlx_ccb_queue, mc, mc_chain.simpleq);
1950
1951 while ((mc = SIMPLEQ_FIRST(&mlx->mlx_ccb_queue)) != NULL) {
1952 if (mlx_ccb_submit(mlx, mc) != 0)
1953 break;
1954 SIMPLEQ_REMOVE_HEAD(&mlx->mlx_ccb_queue, mc_chain.simpleq);
1955 TAILQ_INSERT_TAIL(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq);
1956 }
1957
1958 splx(s);
1959 }
1960
1961 /*
1962 * Map the specified CCB's data buffer onto the bus, and fill the
1963 * scatter-gather list.
1964 */
1965 int
1966 mlx_ccb_map(struct mlx_softc *mlx, struct mlx_ccb *mc, void *data, int size,
1967 int dir)
1968 {
1969 struct mlx_sgentry *sge;
1970 int nsegs, i, rv, sgloff;
1971 bus_dmamap_t xfer;
1972
1973 xfer = mc->mc_xfer_map;
1974
1975 rv = bus_dmamap_load(mlx->mlx_dmat, xfer, data, size, NULL,
1976 BUS_DMA_NOWAIT | BUS_DMA_STREAMING |
1977 ((dir & MC_XFER_IN) ? BUS_DMA_READ : BUS_DMA_WRITE));
1978 if (rv != 0)
1979 return (rv);
1980
1981 nsegs = xfer->dm_nsegs;
1982 mc->mc_xfer_size = size;
1983 mc->mc_flags |= dir;
1984 mc->mc_nsgent = nsegs;
1985 mc->mc_xfer_phys = xfer->dm_segs[0].ds_addr;
1986
1987 sgloff = MLX_SGL_SIZE * mc->mc_ident;
1988 sge = (struct mlx_sgentry *)((caddr_t)mlx->mlx_sgls + sgloff);
1989
1990 for (i = 0; i < nsegs; i++, sge++) {
1991 sge->sge_addr = htole32(xfer->dm_segs[i].ds_addr);
1992 sge->sge_count = htole32(xfer->dm_segs[i].ds_len);
1993 }
1994
1995 if ((dir & MC_XFER_OUT) != 0)
1996 i = BUS_DMASYNC_PREWRITE;
1997 else
1998 i = 0;
1999 if ((dir & MC_XFER_IN) != 0)
2000 i |= BUS_DMASYNC_PREREAD;
2001
2002 bus_dmamap_sync(mlx->mlx_dmat, xfer, 0, mc->mc_xfer_size, i);
2003 bus_dmamap_sync(mlx->mlx_dmat, mlx->mlx_dmamap, sgloff,
2004 MLX_SGL_SIZE, BUS_DMASYNC_PREWRITE);
2005
2006 return (0);
2007 }
2008
2009 /*
2010 * Unmap the specified CCB's data buffer.
2011 */
2012 void
2013 mlx_ccb_unmap(struct mlx_softc *mlx, struct mlx_ccb *mc)
2014 {
2015 int i;
2016
2017 bus_dmamap_sync(mlx->mlx_dmat, mlx->mlx_dmamap,
2018 MLX_SGL_SIZE * mc->mc_ident, MLX_SGL_SIZE,
2019 BUS_DMASYNC_POSTWRITE);
2020
2021 if ((mc->mc_flags & MC_XFER_OUT) != 0)
2022 i = BUS_DMASYNC_POSTWRITE;
2023 else
2024 i = 0;
2025 if ((mc->mc_flags & MC_XFER_IN) != 0)
2026 i |= BUS_DMASYNC_POSTREAD;
2027
2028 bus_dmamap_sync(mlx->mlx_dmat, mc->mc_xfer_map, 0, mc->mc_xfer_size, i);
2029 bus_dmamap_unload(mlx->mlx_dmat, mc->mc_xfer_map);
2030 }
2031
2032 /*
2033 * Submit the CCB, and busy-wait for it to complete. Return non-zero on
2034 * timeout or submission error. Must be called with interrupts blocked.
2035 */
2036 int
2037 mlx_ccb_poll(struct mlx_softc *mlx, struct mlx_ccb *mc, int timo)
2038 {
2039 int rv;
2040
2041 mc->mc_mx.mx_handler = NULL;
2042
2043 if ((rv = mlx_ccb_submit(mlx, mc)) != 0)
2044 return (rv);
2045 TAILQ_INSERT_TAIL(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq);
2046
2047 for (timo *= 10; timo != 0; timo--) {
2048 mlx_intr(mlx);
2049 if (mc->mc_status != MLX_STATUS_BUSY)
2050 break;
2051 DELAY(100);
2052 }
2053
2054 if (timo != 0) {
2055 if (mc->mc_status != 0) {
2056 printf("%s: command failed - %s\n",
2057 mlx->mlx_dv.dv_xname, mlx_ccb_diagnose(mc));
2058 rv = EIO;
2059 } else
2060 rv = 0;
2061 } else {
2062 printf("%s: command timed out\n", mlx->mlx_dv.dv_xname);
2063 rv = EIO;
2064 }
2065
2066 return (rv);
2067 }
2068
2069 /*
2070 * Enqueue the CCB, and sleep until it completes. Return non-zero on
2071 * timeout or error.
2072 */
2073 int
2074 mlx_ccb_wait(struct mlx_softc *mlx, struct mlx_ccb *mc)
2075 {
2076 int s;
2077
2078 mc->mc_flags |= MC_WAITING;
2079 mc->mc_mx.mx_handler = NULL;
2080
2081 s = splbio();
2082 mlx_ccb_enqueue(mlx, mc);
2083 tsleep(mc, PRIBIO, "mlxwccb", 0);
2084 splx(s);
2085
2086 if (mc->mc_status != 0) {
2087 printf("%s: command failed - %s\n", mlx->mlx_dv.dv_xname,
2088 mlx_ccb_diagnose(mc));
2089 return (EIO);
2090 }
2091
2092 return (0);
2093 }
2094
2095 /*
2096 * Try to submit a CCB's mailbox to the controller for execution. Return
2097 * non-zero on timeout or error. Must be called with interrupts blocked.
2098 */
2099 static int
2100 mlx_ccb_submit(struct mlx_softc *mlx, struct mlx_ccb *mc)
2101 {
2102 int i, s, r;
2103
2104 /* Save the ident so we can handle this command when complete. */
2105 mc->mc_mbox[1] = (u_int8_t)(mc->mc_ident + 1);
2106
2107 /* Mark the command as currently being processed. */
2108 mc->mc_status = MLX_STATUS_BUSY;
2109 mc->mc_expiry = mlx_curtime() + MLX_TIMEOUT;
2110
2111 /* Spin waiting for the mailbox. */
2112 for (i = 100; i != 0; i--) {
2113 s = splbio();
2114 r = (*mlx->mlx_submit)(mlx, mc);
2115 splx(s);
2116 if (r != 0)
2117 break;
2118 DELAY(100);
2119 }
2120 if (i != 0)
2121 return (0);
2122
2123 DPRINTF(("mlx_ccb_submit: rejected; queueing\n"));
2124 mc->mc_status = MLX_STATUS_WEDGED;
2125 return (EIO);
2126 }
2127
2128 /*
2129 * Return a string that describes why a command has failed.
2130 */
2131 const char *
2132 mlx_ccb_diagnose(struct mlx_ccb *mc)
2133 {
2134 static char buf[80];
2135 int i;
2136
2137 for (i = 0; i < sizeof(mlx_msgs) / sizeof(mlx_msgs[0]); i++)
2138 if ((mc->mc_mbox[0] == mlx_msgs[i].command ||
2139 mlx_msgs[i].command == 0) &&
2140 mc->mc_status == mlx_msgs[i].status) {
2141 snprintf(buf, sizeof(buf), "%s (0x%x)",
2142 mlx_status_msgs[mlx_msgs[i].msg], mc->mc_status);
2143 return (buf);
2144 }
2145
2146 snprintf(buf, sizeof(buf), "unknown response 0x%x for command 0x%x",
2147 (int)mc->mc_status, (int)mc->mc_mbox[0]);
2148
2149 return (buf);
2150 }
2151
2152 /*
2153 * Poll the controller for completed commands. Returns non-zero if one or
2154 * more commands were completed. Must be called with interrupts blocked.
2155 */
2156 int
2157 mlx_intr(void *cookie)
2158 {
2159 struct mlx_softc *mlx;
2160 struct mlx_ccb *mc;
2161 int result;
2162 u_int ident, status;
2163
2164 mlx = cookie;
2165 result = 0;
2166
2167 while ((*mlx->mlx_findcomplete)(mlx, &ident, &status) != 0) {
2168 result = 1;
2169 ident--;
2170
2171 if (ident >= MLX_MAX_QUEUECNT) {
2172 printf("%s: bad completion returned\n",
2173 mlx->mlx_dv.dv_xname);
2174 continue;
2175 }
2176
2177 mc = mlx->mlx_ccbs + ident;
2178
2179 if (mc->mc_status != MLX_STATUS_BUSY) {
2180 printf("%s: bad completion returned\n",
2181 mlx->mlx_dv.dv_xname);
2182 continue;
2183 }
2184
2185 TAILQ_REMOVE(&mlx->mlx_ccb_worklist, mc, mc_chain.tailq);
2186
2187 /* Record status and notify the initiator, if requested. */
2188 mc->mc_status = status;
2189 if (mc->mc_mx.mx_handler != NULL)
2190 (*mc->mc_mx.mx_handler)(mc);
2191 else if ((mc->mc_flags & MC_WAITING) != 0)
2192 wakeup(mc);
2193 }
2194
2195 /* If we've completed any commands, try posting some more. */
2196 if (result)
2197 mlx_ccb_enqueue(mlx, NULL);
2198
2199 return (result);
2200 }
2201
2202 /*
2203 * Emit a string describing the firmware handshake status code, and return a
2204 * flag indicating whether the code represents a fatal error.
2205 *
2206 * Error code interpretations are from the Linux driver, and don't directly
2207 * match the messages printed by Mylex's BIOS. This may change if
2208 * documentation on the codes is forthcoming.
2209 */
2210 static int
2211 mlx_fw_message(struct mlx_softc *mlx, int error, int param1, int param2)
2212 {
2213 const char *fmt;
2214
2215 switch (error) {
2216 case 0x00:
2217 fmt = "physical drive %d:%d not responding";
2218 break;
2219
2220 case 0x08:
2221 /*
2222 * We could be neater about this and give some indication
2223 * when we receive more of them.
2224 */
2225 if ((mlx->mlx_flags & MLXF_SPINUP_REPORTED) == 0) {
2226 printf("%s: spinning up drives...\n",
2227 mlx->mlx_dv.dv_xname);
2228 mlx->mlx_flags |= MLXF_SPINUP_REPORTED;
2229 }
2230 return (0);
2231
2232 case 0x30:
2233 fmt = "configuration checksum error";
2234 break;
2235
2236 case 0x60:
2237 fmt = "mirror race recovery failed";
2238 break;
2239
2240 case 0x70:
2241 fmt = "mirror race recovery in progress";
2242 break;
2243
2244 case 0x90:
2245 fmt = "physical drive %d:%d COD mismatch";
2246 break;
2247
2248 case 0xa0:
2249 fmt = "logical drive installation aborted";
2250 break;
2251
2252 case 0xb0:
2253 fmt = "mirror race on a critical system drive";
2254 break;
2255
2256 case 0xd0:
2257 fmt = "new controller configuration found";
2258 break;
2259
2260 case 0xf0:
2261 printf("%s: FATAL MEMORY PARITY ERROR\n",
2262 mlx->mlx_dv.dv_xname);
2263 return (1);
2264
2265 default:
2266 printf("%s: unknown firmware init error %02x:%02x:%02x\n",
2267 mlx->mlx_dv.dv_xname, error, param1, param2);
2268 return (0);
2269 }
2270
2271 printf("%s: ", mlx->mlx_dv.dv_xname);
2272 printf(fmt, param2, param1);
2273 printf("\n");
2274
2275 return (0);
2276 }
Cache object: 5b82b2d857ecf74cbf4f2deee8515802
|