2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 1999 Michael Smith
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include <dev/mlx/mlxio.h>
40 #include <dev/mlx/mlxreg.h>
42 #include "mlxcontrol.h"
44 static int cmd_status(int argc, char *argv[]);
45 static int cmd_rescan(int argc, char *argv[]);
46 static int cmd_detach(int argc, char *argv[]);
47 static int cmd_check(int argc, char *argv[]);
48 static int cmd_rebuild(int argc, char *argv[]);
50 static int cmd_pause(int argc, char *argv[]);
52 static int cmd_help(int argc, char *argv[]);
54 extern int cmd_config(int argc, char *argv[]);
60 int (*func)(int argc, char *argv[]);
64 {"status", cmd_status,
65 "displays device status",
66 " status [-qv] [<drive>...]\n"
67 " Display status for <drive> or all drives if none is listed\n"
68 " -q Suppress output.\n"
69 " -v Display verbose information.\n"
70 " Returns 0 if all drives tested are online, 1 if one or more are\n"
71 " critical, and 2 if one or more are offline."},
72 {"rescan", cmd_rescan,
73 "scan for new system drives",
74 " rescan <controller> [<controller>...]\n"
75 " Rescan <controller> for system drives.\n"
77 " Rescan all controllers for system drives."},
78 {"detach", cmd_detach,
79 "detach system drives",
80 " detach <drive> [<drive>...]\n"
81 " Detaches <drive> from the controller.\n"
82 " detach -a <controller>\n"
83 " Detaches all drives on <controller>."},
85 "consistency-check a system drive",
87 " Requests a check and rebuild of the parity information on <drive>.\n"
88 " Note that each controller can only check one system drive at a time."},
89 {"rebuild", cmd_rebuild,
90 "initiate a rebuild of a dead physical drive",
91 " rebuild <controller> <physdrive>\n"
92 " All system drives using space on the physical drive <physdrive>\n"
93 " are rebuilt, reconstructing all data on the drive.\n"
94 " Note that each controller can only perform one rebuild at a time."},
97 "pauses controller channels",
98 " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
99 " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
100 " all channels are paused.\n"
101 " <howlong> How long (seconds) to pause for (default 30).\n"
102 " <delay> How long (seconds) to wait before pausing (default 30).\n"
103 " pause <controller> -c\n"
104 " Cancels any pending pause operation on <controller>."},
106 {"config", cmd_config,
107 "examine and update controller configuration",
108 " config <controller>\n"
109 " Print configuration for <controller>."},
111 "give help on usage",
113 {NULL, NULL, NULL, NULL}
116 /********************************************************************************
117 * Command dispatch and global options parsing.
121 main(int argc, char *argv[])
128 while ((ch = getopt(argc, argv, "")) != -1)
131 return(cmd_help(0, NULL));
138 for (i = 0; commands[i].cmd != NULL; i++)
139 if (!strcmp(argv[0], commands[i].cmd))
140 return(commands[i].func(argc, argv));
142 return(cmd_help(oargc, oargv));
145 /********************************************************************************
149 cmd_help(int argc, char *argv[])
154 for (i = 0; commands[i].cmd != NULL; i++)
155 if (!strcmp(argv[1], commands[i].cmd)) {
156 fprintf(stderr, "%s\n", commands[i].text);
162 fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
163 fprintf(stderr, "Valid commands are:\n");
164 for (i = 0; commands[i].cmd != NULL; i++)
165 fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
170 /********************************************************************************
173 * status [-qv] [<device> ...]
174 * Prints status for <device>, or all if none listed.
176 * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
177 * more devices are critical, 2 if one or more devices are offline.
179 static struct mlx_rebuild_status rs;
180 static int rs_ctrlr = -1;
181 static int status_result = 0;
183 /* XXX more verbosity! */
185 status_print(int unit, void *arg)
187 int verbosity = *(int *)arg;
188 int fd, result, ctrlr, sysdrive, statvalid;
190 /* Find which controller and what system drive we are */
192 if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
193 warnx("couldn't get controller/drive for %s", drivepath(unit));
195 /* If we don't have rebuild stats for this controller, get them */
196 if (rs_ctrlr == ctrlr) {
199 if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
200 warn("can't open %s", ctrlrpath(ctrlr));
202 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
203 warn("ioctl MLX_REBUILDSTAT");
214 if ((fd = open(drivepath(unit), 0)) < 0) {
215 warn("can't open %s", drivepath(unit));
220 if (ioctl(fd, MLXD_STATUS, &result) < 0) {
221 warn("ioctl MLXD_STATUS");
224 case MLX_SYSD_ONLINE:
226 printf("%s: online", drivename(unit));
228 case MLX_SYSD_CRITICAL:
230 printf("%s: critical", drivename(unit));
231 if (status_result < 1)
234 case MLX_SYSD_OFFLINE:
236 printf("%s: offline", drivename(unit));
237 if (status_result < 2)
242 printf("%s: unknown status 0x%x", drivename(unit), result);
246 /* rebuild/check in progress on this drive? */
247 if (statvalid && (rs_ctrlr == ctrlr) &&
248 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
250 case MLX_REBUILDSTAT_REBUILDCHECK:
251 printf(" [consistency check");
253 case MLX_REBUILDSTAT_ADDCAPACITY:
254 printf(" [add capacity");
256 case MLX_REBUILDSTAT_ADDCAPACITYINIT:
257 printf(" [add capacity init");
260 printf(" [unknown operation");
262 printf(": %d/%d, %d%% complete]",
263 rs.rs_remaining, rs.rs_size,
264 ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
276 } mlx_controller_names[] = {
291 controller_print(int unit, void *arg)
293 struct mlx_enquiry2 enq;
294 struct mlx_phys_drv pd;
295 int verbosity = *(int *)arg;
298 int i, channel, target;
303 /* fetch and print controller data */
304 if (mlx_enquiry(unit, &enq)) {
305 printf("mlx%d: error submitting ENQUIRY2\n", unit);
308 for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
309 if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
310 model = mlx_controller_names[i].name;
315 sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
319 printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
321 enq.me_actual_channels,
322 enq.me_actual_channels > 1 ? "s" : "",
323 enq.me_firmware_id & 0xff,
324 (enq.me_firmware_id >> 8) & 0xff,
325 (enq.me_firmware_id >> 16),
326 (enq.me_firmware_id >> 24) & 0xff,
327 enq.me_mem_size / (1024 * 1024));
330 printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
331 printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
332 printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
333 enq.me_actual_channels);
334 printf(" Max Targets %d\n", enq.me_max_targets);
335 printf(" Max Tags %d\n", enq.me_max_tags);
336 printf(" Max System Drives %d\n", enq.me_max_sys_drives);
337 printf(" Max Arms %d\n", enq.me_max_arms);
338 printf(" Max Spans %d\n", enq.me_max_spans);
339 printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
340 enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
341 printf(" DRAM type %d\n", enq.me_mem_type);
342 printf(" Clock Speed %dns\n", enq.me_clock_speed);
343 printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
344 printf(" Max Commands %d\n", enq.me_max_commands);
345 printf(" Max SG Entries %d\n", enq.me_max_sg);
346 printf(" Max DP %d\n", enq.me_max_dp);
347 printf(" Max IOD %d\n", enq.me_max_iod);
348 printf(" Max Comb %d\n", enq.me_max_comb);
349 printf(" Latency %ds\n", enq.me_latency);
350 printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
351 printf(" Min Free Lines %d\n", enq.me_min_freelines);
352 printf(" Rate Constant %d\n", enq.me_rate_const);
353 printf(" MAXBLK %d\n", enq.me_maxblk);
354 printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
355 printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
356 printf(" SCSI Capability %s%dMHz, %d bit\n",
357 enq.me_scsi_cap & (1<<4) ? "differential " : "",
358 (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
359 8 << (enq.me_scsi_cap & 0x3));
360 printf(" Firmware Build Number %d\n", enq.me_firmware_build);
361 printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
363 printf(" Features %b\n", enq.me_firmware_features,
364 "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
368 /* fetch and print physical drive data */
369 for (channel = 0; channel < enq.me_configured_channels; channel++) {
370 for (target = 0; target < enq.me_max_targets; target++) {
371 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
372 (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
373 mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
375 /* XXX print device statistics? */
384 cmd_status(int argc, char *argv[])
386 int ch, verbosity = 1, i, unit;
390 while ((ch = getopt(argc, argv, "qv")) != -1)
399 return(cmd_help(argc, argv));
405 mlx_foreach(controller_print, &verbosity);
406 mlxd_foreach(status_print, &verbosity);
408 for (i = 0; i < argc; i++) {
409 if ((unit = driveunit(argv[i])) == -1) {
410 warnx("'%s' is not a valid drive", argv[i]);
412 status_print(unit, &verbosity);
416 return(status_result);
419 /********************************************************************************
420 * Recscan for system drives on one or more controllers.
422 * rescan <controller> [<controller>...]
426 rescan_ctrlr(int unit, void *junk)
431 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
432 warn("can't open %s", ctrlrpath(unit));
436 if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
437 warn("can't rescan %s", ctrlrname(unit));
442 cmd_rescan(int argc, char *argv[])
444 int all = 0, i, ch, unit;
448 while ((ch = getopt(argc, argv, "a")) != -1)
454 return(cmd_help(argc, argv));
460 mlx_foreach(rescan_ctrlr, NULL);
462 for (i = 0; i < argc; i++) {
463 if ((unit = ctrlrunit(argv[i])) == -1) {
464 warnx("'%s' is not a valid controller", argv[i]);
466 rescan_ctrlr(unit, NULL);
473 /********************************************************************************
474 * Detach one or more system drives from a controller.
476 * detach <drive> [<drive>...]
479 * detach -a <controller> [<controller>...]
480 * Detach all drives on <controller>.
484 detach_drive(int unit, void *arg)
489 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
490 warn("can't open %s", ctrlrpath(unit));
494 if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
495 warn("can't detach %s", drivename(unit));
500 cmd_detach(int argc, char *argv[])
502 struct mlxd_foreach_action ma;
503 int all = 0, i, ch, unit;
507 while ((ch = getopt(argc, argv, "a")) != -1)
513 return(cmd_help(argc, argv));
519 ma.func = detach_drive;
521 for (i = 0; i < argc; i++) {
522 if ((unit = ctrlrunit(argv[i])) == -1) {
523 warnx("'%s' is not a valid controller", argv[i]);
525 mlxd_foreach_ctrlr(unit, &ma);
529 for (i = 0; i < argc; i++) {
530 if ((unit = driveunit(argv[i])) == -1) {
531 warnx("'%s' is not a valid drive", argv[i]);
533 /* run across all controllers to find this drive */
534 mlx_foreach(detach_drive, &unit);
541 /********************************************************************************
542 * Initiate a consistency check on a system drive.
545 * Start a check of <drive>
549 cmd_check(int argc, char *argv[])
551 int unit, fd, result;
554 return(cmd_help(argc, argv));
556 if ((unit = driveunit(argv[1])) == -1) {
557 warnx("'%s' is not a valid drive", argv[1]);
561 if ((fd = open(drivepath(unit), 0)) < 0) {
562 warn("can't open %s", drivepath(unit));
564 /* Try to start the check */
565 if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
568 warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
571 warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
574 warnx("drive rebuild or consistency check is already in progress on this controller");
577 warn("ioctl MLXD_CHECKASYNC");
585 /********************************************************************************
586 * Initiate a physical drive rebuild
588 * rebuild <controller> <channel>:<target>
589 * Start a rebuild of <controller>:<channel>:<target>
593 cmd_rebuild(int argc, char *argv[])
595 struct mlx_rebuild_request rb;
599 return(cmd_help(argc, argv));
601 /* parse arguments */
602 if ((unit = ctrlrunit(argv[1])) == -1) {
603 warnx("'%s' is not a valid controller", argv[1]);
606 /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
607 if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
608 (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
609 warnx("'%s' is not a valid physical drive", argv[2]);
613 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
614 warn("can't open %s", ctrlrpath(unit));
617 /* try to start the rebuild */
618 if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
619 switch(rb.rr_status) {
621 warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
624 warnx("drive failed during rebuild");
627 warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
630 warnx("drive rebuild or consistency check is already in progress on this controller");
633 warn("ioctl MLXD_REBUILDASYNC");
640 /********************************************************************************
641 * Pause one or more channels on a controller
643 * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
644 * Pauses <channel> (or all channels) for <time> seconds after a
645 * delay of <delay> seconds.
646 * pause <controller> -c
647 * Cancels pending pause
650 cmd_pause(int argc, char *argv[])
653 int unit, i, ch, fd, cancel = 0;
663 while ((ch = getopt(argc, argv, "cd:t:")) != -1)
669 mp.mp_when = strtol(optarg, &cp, 0);
671 return(cmd_help(argc, argv));
674 mp.mp_howlong = strtol(optarg, &cp, 0);
676 return(cmd_help(argc, argv));
679 return(cmd_help(argc, argv));
684 /* get controller unit number that we're working on */
685 if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
686 return(cmd_help(oargc, oargv));
689 if ((fd = open(ctrlrpath(unit), 0)) < 0) {
690 warn("can't open %s", ctrlrpath(unit));
695 /* controller-wide pause/cancel */
696 mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
698 for (i = 1; i < argc; i++) {
699 ch = strtol(argv[i], &cp, 0);
701 warnx("bad channel number '%s'", argv[i]);
704 mp.mp_which |= (1 << ch);
708 if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
709 warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
713 #endif /* SUPPORT_PAUSE */