MFC r1.6 r1.30 r1.28 (HEAD):
[dragonfly.git] / usr.sbin / mlxcontrol / command.c
blob2c11bfdf80a0a0f5425bdf2b5b923a3cc67b4f62
1 /*-
2 * Copyright (c) 1999 Michael Smith
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $FreeBSD: src/usr.sbin/mlxcontrol/command.c,v 1.2.2.1 2000/04/24 19:44:46 msmith Exp $
27 * $DragonFly: src/usr.sbin/mlxcontrol/command.c,v 1.3 2003/08/08 04:18:46 dillon Exp $
30 #include <fcntl.h>
31 #include <paths.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <err.h>
38 #include <dev/raid/mlx/mlxio.h>
39 #include <dev/raid/mlx/mlxreg.h>
41 #include "mlxcontrol.h"
43 static int cmd_status(int argc, char *argv[]);
44 static int cmd_rescan(int argc, char *argv[]);
45 static int cmd_detach(int argc, char *argv[]);
46 static int cmd_check(int argc, char *argv[]);
47 static int cmd_rebuild(int argc, char *argv[]);
48 #ifdef SUPPORT_PAUSE
49 static int cmd_pause(int argc, char *argv[]);
50 #endif
51 static int cmd_help(int argc, char *argv[]);
53 extern int cmd_config(int argc, char *argv[]);
56 struct
58 char *cmd;
59 int (*func)(int argc, char *argv[]);
60 char *desc;
61 char *text;
62 } commands[] = {
63 {"status", cmd_status,
64 "displays device status",
65 " status [-qv] [<drive>...]\n"
66 " Display status for <drive> or all drives if none is listed\n"
67 " -q Suppress output.\n"
68 " -v Display verbose information.\n"
69 " Returns 0 if all drives tested are online, 1 if one or more are\n"
70 " critical, and 2 if one or more are offline."},
71 {"rescan", cmd_rescan,
72 "scan for new system drives",
73 " rescan <controller> [<controller>...]\n"
74 " Rescan <controller> for system drives.\n"
75 " rescan -a\n"
76 " Rescan all controllers for system drives."},
77 {"detach", cmd_detach,
78 "detach system drives",
79 " detach <drive> [<drive>...]\n"
80 " Detaches <drive> from the controller.\n"
81 " detach -a <controller>\n"
82 " Detaches all drives on <controller>."},
83 {"check", cmd_check,
84 "consistency-check a system drive",
85 " check <drive>\n"
86 " Requests a check and rebuild of the parity information on <drive>.\n"
87 " Note that each controller can only check one system drive at a time."},
88 {"rebuild", cmd_rebuild,
89 "initiate a rebuild of a dead physical drive",
90 " rebuild <controller> <physdrive>\n"
91 " All system drives using space on the physical drive <physdrive>\n"
92 " are rebuilt, reconstructing all data on the drive.\n"
93 " Note that each controller can only perform one rebuild at a time."},
94 #ifdef SUPPORT_PAUSE
95 {"pause", cmd_pause,
96 "pauses controller channels",
97 " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
98 " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
99 " all channels are paused.\n"
100 " <howlong> How long (seconds) to pause for (default 30).\n"
101 " <delay> How long (seconds) to wait before pausing (default 30).\n"
102 " pause <controller> -c\n"
103 " Cancels any pending pause operation on <controller>."},
104 #endif
105 {"config", cmd_config,
106 "examine and update controller configuration",
107 " config <controller>\n"
108 " Print configuration for <controller>."},
109 {"help", cmd_help,
110 "give help on usage",
111 ""},
112 {NULL, NULL, NULL, NULL}
115 /********************************************************************************
116 * Command dispatch and global options parsing.
120 main(int argc, char *argv[])
122 int ch, i, oargc;
123 char **oargv;
125 oargc = argc;
126 oargv = argv;
127 while ((ch = getopt(argc, argv, "")) != -1)
128 switch(ch) {
129 default:
130 return(cmd_help(0, NULL));
133 argc -= optind;
134 argv += optind;
136 if (argc > 0)
137 for (i = 0; commands[i].cmd != NULL; i++)
138 if (!strcmp(argv[0], commands[i].cmd))
139 return(commands[i].func(argc, argv));
141 return(cmd_help(oargc, oargv));
144 /********************************************************************************
145 * Helptext output
147 static int
148 cmd_help(int argc, char *argv[])
150 int i;
152 if (argc > 1)
153 for (i = 0; commands[i].cmd != NULL; i++)
154 if (!strcmp(argv[1], commands[i].cmd)) {
155 fprintf(stderr, "%s\n", commands[i].text);
156 fflush(stderr);
157 return(0);
160 if (argv != NULL)
161 fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
162 fprintf(stderr, "Valid commands are:\n");
163 for (i = 0; commands[i].cmd != NULL; i++)
164 fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
165 fflush(stderr);
166 return(0);
169 /********************************************************************************
170 * Status output
172 * status [-qv] [<device> ...]
173 * Prints status for <device>, or all if none listed.
175 * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
176 * more devices are critical, 2 if one or more devices are offline.
178 static struct mlx_rebuild_status rs;
179 static int rs_ctrlr = -1;
180 static int status_result = 0;
182 /* XXX more verbosity! */
183 static void
184 status_print(int unit, void *arg)
186 int verbosity = *(int *)arg;
187 int fd, result, ctrlr, sysdrive, statvalid;
189 /* Find which controller and what system drive we are */
190 statvalid = 0;
191 if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
192 warnx("couldn't get controller/drive for %s", drivepath(unit));
193 } else {
194 /* If we don't have rebuild stats for this controller, get them */
195 if (rs_ctrlr == ctrlr) {
196 statvalid = 1;
197 } else {
198 if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
199 warn("can't open %s", ctrlrpath(ctrlr));
200 } else {
201 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
202 warn("ioctl MLX_REBUILDSTAT");
203 } else {
204 rs_ctrlr = ctrlr;
205 statvalid = 1;
207 close(fd);
212 /* Get the device */
213 if ((fd = open(drivepath(unit), 0)) < 0) {
214 warn("can't open %s", drivepath(unit));
215 return;
218 /* Get its status */
219 if (ioctl(fd, MLXD_STATUS, &result) < 0) {
220 warn("ioctl MLXD_STATUS");
221 } else {
222 switch(result) {
223 case MLX_SYSD_ONLINE:
224 if (verbosity > 0)
225 printf("%s: online", drivename(unit));
226 break;
227 case MLX_SYSD_CRITICAL:
228 if (verbosity > 0)
229 printf("%s: critical", drivename(unit));
230 if (status_result < 1)
231 status_result = 1;
232 break;
233 case MLX_SYSD_OFFLINE:
234 if (verbosity > 0)
235 printf("%s: offline", drivename(unit));
236 if (status_result < 2)
237 status_result = 2;
238 break;
239 default:
240 if (verbosity > 0) {
241 printf("%s: unknown status 0x%x", drivename(unit), result);
244 if (verbosity > 0) {
245 /* rebuild/check in progress on this drive? */
246 if (statvalid && (rs_ctrlr == ctrlr) &&
247 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
248 switch(rs.rs_code) {
249 case MLX_REBUILDSTAT_REBUILDCHECK:
250 printf(" [consistency check");
251 break;
252 case MLX_REBUILDSTAT_ADDCAPACITY:
253 printf(" [add capacity");
254 break;
255 case MLX_REBUILDSTAT_ADDCAPACITYINIT:
256 printf(" [add capacity init");
257 break;
258 default:
259 printf(" [unknown operation");
261 printf(": %d/%d, %d%% complete]",
262 rs.rs_remaining, rs.rs_size,
263 ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
265 printf("\n");
268 close(fd);
271 static struct
273 int hwid;
274 char *name;
275 } mlx_controller_names[] = {
276 {0x01, "960P/PD"},
277 {0x02, "960PL"},
278 {0x10, "960PG"},
279 {0x11, "960PJ"},
280 {0x12, "960PR"},
281 {0x13, "960PT"},
282 {0x14, "960PTL0"},
283 {0x15, "960PRL"},
284 {0x16, "960PTL1"},
285 {0x20, "1100PVX"},
286 {-1, NULL}
289 static void
290 controller_print(int unit, void *arg)
292 struct mlx_enquiry2 enq;
293 struct mlx_phys_drv pd;
294 int verbosity = *(int *)arg;
295 static char buf[80];
296 char *model;
297 int i, channel, target;
299 if (verbosity == 0)
300 return;
302 /* fetch and print controller data */
303 if (mlx_enquiry(unit, &enq)) {
304 printf("mlx%d: error submitting ENQUIRY2\n", unit);
305 } else {
307 for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
308 if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
309 model = mlx_controller_names[i].name;
310 break;
313 if (model == NULL) {
314 sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
315 model = buf;
318 printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
319 unit, model,
320 enq.me_actual_channels,
321 enq.me_actual_channels > 1 ? "s" : "",
322 enq.me_firmware_id & 0xff,
323 (enq.me_firmware_id >> 8) & 0xff,
324 (enq.me_firmware_id >> 16),
325 (enq.me_firmware_id >> 24) & 0xff,
326 enq.me_mem_size / (1024 * 1024));
328 if (verbosity > 1) {
329 printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
330 printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
331 printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
332 enq.me_actual_channels);
333 printf(" Max Targets %d\n", enq.me_max_targets);
334 printf(" Max Tags %d\n", enq.me_max_tags);
335 printf(" Max System Drives %d\n", enq.me_max_sys_drives);
336 printf(" Max Arms %d\n", enq.me_max_arms);
337 printf(" Max Spans %d\n", enq.me_max_spans);
338 printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
339 enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
340 printf(" DRAM type %d\n", enq.me_mem_type);
341 printf(" Clock Speed %dns\n", enq.me_clock_speed);
342 printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
343 printf(" Max Commands %d\n", enq.me_max_commands);
344 printf(" Max SG Entries %d\n", enq.me_max_sg);
345 printf(" Max DP %d\n", enq.me_max_dp);
346 printf(" Max IOD %d\n", enq.me_max_iod);
347 printf(" Max Comb %d\n", enq.me_max_comb);
348 printf(" Latency %ds\n", enq.me_latency);
349 printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
350 printf(" Min Free Lines %d\n", enq.me_min_freelines);
351 printf(" Rate Constant %d\n", enq.me_rate_const);
352 printf(" MAXBLK %d\n", enq.me_maxblk);
353 printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
354 printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
355 printf(" SCSI Capability %s%dMHz, %d bit\n",
356 enq.me_scsi_cap & (1<<4) ? "differential " : "",
357 (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
358 8 << (enq.me_scsi_cap & 0x3));
359 printf(" Firmware Build Number %d\n", enq.me_firmware_build);
360 printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
361 #if 0
362 printf(" Features %b\n", enq.me_firmware_features,
363 "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
364 #endif
367 /* fetch and print physical drive data */
368 for (channel = 0; channel < enq.me_configured_channels; channel++) {
369 for (target = 0; target < enq.me_max_targets; target++) {
370 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
371 (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
372 mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
373 if (verbosity > 1) {
374 /* XXX print device statistics? */
382 static int
383 cmd_status(int argc, char *argv[])
385 int ch, verbosity = 1, i, unit;
387 optreset = 1;
388 optind = 1;
389 while ((ch = getopt(argc, argv, "qv")) != -1)
390 switch(ch) {
391 case 'q':
392 verbosity = 0;
393 break;
394 case 'v':
395 verbosity = 2;
396 break;
397 default:
398 return(cmd_help(argc, argv));
400 argc -= optind;
401 argv += optind;
403 if (argc < 1) {
404 mlx_foreach(controller_print, &verbosity);
405 mlxd_foreach(status_print, &verbosity);
406 } else {
407 for (i = 0; i < argc; i++) {
408 if ((unit = driveunit(argv[i])) == -1) {
409 warnx("'%s' is not a valid drive", argv[i]);
410 } else {
411 status_print(unit, &verbosity);
415 return(status_result);
418 /********************************************************************************
419 * Recscan for system drives on one or more controllers.
421 * rescan <controller> [<controller>...]
422 * rescan -a
424 static void
425 rescan_ctrlr(int unit, void *junk)
427 int fd;
429 /* Get the device */
430 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
431 warn("can't open %s", ctrlrpath(unit));
432 return;
435 if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
436 warn("can't rescan %s", ctrlrname(unit));
437 close(fd);
440 static int
441 cmd_rescan(int argc, char *argv[])
443 int all = 0, i, ch, unit;
445 optreset = 1;
446 optind = 1;
447 while ((ch = getopt(argc, argv, "a")) != -1)
448 switch(ch) {
449 case 'a':
450 all = 1;
451 break;
452 default:
453 return(cmd_help(argc, argv));
455 argc -= optind;
456 argv += optind;
458 if (all) {
459 mlx_foreach(rescan_ctrlr, NULL);
460 } else {
461 for (i = 0; i < argc; i++) {
462 if ((unit = ctrlrunit(argv[i])) == -1) {
463 warnx("'%s' is not a valid controller", argv[i]);
464 } else {
465 rescan_ctrlr(unit, NULL);
469 return(0);
472 /********************************************************************************
473 * Detach one or more system drives from a controller.
475 * detach <drive> [<drive>...]
476 * Detach <drive>.
478 * detach -a <controller> [<controller>...]
479 * Detach all drives on <controller>.
482 static void
483 detach_drive(int unit, void *arg)
485 int fd;
487 /* Get the device */
488 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
489 warn("can't open %s", ctrlrpath(unit));
490 return;
493 if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
494 warn("can't detach %s", drivename(unit));
495 close(fd);
498 static int
499 cmd_detach(int argc, char *argv[])
501 struct mlxd_foreach_action ma;
502 int all = 0, i, ch, unit;
504 optreset = 1;
505 optind = 1;
506 while ((ch = getopt(argc, argv, "a")) != -1)
507 switch(ch) {
508 case 'a':
509 all = 1;
510 break;
511 default:
512 return(cmd_help(argc, argv));
514 argc -= optind;
515 argv += optind;
517 if (all) {
518 ma.func = detach_drive;
519 ma.arg = &unit;
520 for (i = 0; i < argc; i++) {
521 if ((unit = ctrlrunit(argv[i])) == -1) {
522 warnx("'%s' is not a valid controller", argv[i]);
523 } else {
524 mlxd_foreach_ctrlr(unit, &ma);
527 } else {
528 for (i = 0; i < argc; i++) {
529 if ((unit = driveunit(argv[i])) == -1) {
530 warnx("'%s' is not a valid drive", argv[i]);
531 } else {
532 /* run across all controllers to find this drive */
533 mlx_foreach(detach_drive, &unit);
537 return(0);
540 /********************************************************************************
541 * Initiate a consistency check on a system drive.
543 * check [<drive>]
544 * Start a check of <drive>
547 static int
548 cmd_check(int argc, char *argv[])
550 int unit, fd, result;
552 if (argc != 2)
553 return(cmd_help(argc, argv));
555 if ((unit = driveunit(argv[1])) == -1) {
556 warnx("'%s' is not a valid drive", argv[1]);
557 } else {
559 /* Get the device */
560 if ((fd = open(drivepath(unit), 0)) < 0) {
561 warn("can't open %s", drivepath(unit));
562 } else {
563 /* Try to start the check */
564 if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
565 switch(result) {
566 case 0x0002:
567 warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
568 break;
569 case 0x0105:
570 warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
571 break;
572 case 0x0106:
573 warnx("drive rebuild or consistency check is already in progress on this controller");
574 break;
575 default:
576 warn("ioctl MLXD_CHECKASYNC");
581 return(0);
584 /********************************************************************************
585 * Initiate a physical drive rebuild
587 * rebuild <controller> <channel>:<target>
588 * Start a rebuild of <controller>:<channel>:<target>
591 static int
592 cmd_rebuild(int argc, char *argv[])
594 struct mlx_rebuild_request rb;
595 int unit, fd;
597 if (argc != 3)
598 return(cmd_help(argc, argv));
600 /* parse arguments */
601 if ((unit = ctrlrunit(argv[1])) == -1) {
602 warnx("'%s' is not a valid controller", argv[1]);
603 return(1);
605 /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
606 if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
607 (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
608 warnx("'%s' is not a valid physical drive", argv[2]);
609 return(1);
611 /* get the device */
612 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
613 warn("can't open %s", ctrlrpath(unit));
614 return(1);
616 /* try to start the rebuild */
617 if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
618 switch(rb.rr_status) {
619 case 0x0002:
620 warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
621 break;
622 case 0x0004:
623 warnx("drive failed during rebuild");
624 break;
625 case 0x0105:
626 warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
627 break;
628 case 0x0106:
629 warnx("drive rebuild or consistency check is already in progress on this controller");
630 break;
631 default:
632 warn("ioctl MLXD_CHECKASYNC");
635 return(0);
638 #ifdef SUPPORT_PAUSE
639 /********************************************************************************
640 * Pause one or more channels on a controller
642 * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
643 * Pauses <channel> (or all channels) for <time> seconds after a
644 * delay of <delay> seconds.
645 * pause <controller> -c
646 * Cancels pending pause
648 static int
649 cmd_pause(int argc, char *argv[])
651 struct mlx_pause mp;
652 int unit, i, ch, fd, cancel = 0;
653 char *cp;
654 int oargc = argc;
655 char **oargv = argv;
657 mp.mp_which = 0;
658 mp.mp_when = 30;
659 mp.mp_howlong = 30;
660 optreset = 1;
661 optind = 1;
662 while ((ch = getopt(argc, argv, "cd:t:")) != -1)
663 switch(ch) {
664 case 'c':
665 cancel = 1;
666 break;
667 case 'd':
668 mp.mp_when = strtol(optarg, &cp, 0);
669 if (*cp != 0)
670 return(cmd_help(argc, argv));
671 break;
672 case 't':
673 mp.mp_howlong = strtol(optarg, &cp, 0);
674 if (*cp != 0)
675 return(cmd_help(argc, argv));
676 break;
677 default:
678 return(cmd_help(argc, argv));
680 argc -= optind;
681 argv += optind;
683 /* get controller unit number that we're working on */
684 if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
685 return(cmd_help(oargc, oargv));
687 /* Get the device */
688 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
689 warn("can't open %s", ctrlrpath(unit));
690 return(1);
693 if (argc == 1) {
694 /* controller-wide pause/cancel */
695 mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
696 } else {
697 for (i = 1; i < argc; i++) {
698 ch = strtol(argv[i], &cp, 0);
699 if (*cp != 0) {
700 warnx("bad channel number '%s'", argv[i]);
701 continue;
702 } else {
703 mp.mp_which |= (1 << ch);
707 if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
708 warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
709 close(fd);
710 return(0);
712 #endif /* SUPPORT_PAUSE */