2 * Copyright (c) 1999 Michael Smith
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
26 * $FreeBSD: src/usr.sbin/mlxcontrol/command.c,v 1.3 2008/09/12 17:40:17 sepotvin Exp $
37 #include <dev/raid/mlx/mlxio.h>
38 #include <dev/raid/mlx/mlxreg.h>
40 #include "mlxcontrol.h"
42 static int cmd_status(int argc
, char *argv
[]);
43 static int cmd_rescan(int argc
, char *argv
[]);
44 static int cmd_detach(int argc
, char *argv
[]);
45 static int cmd_check(int argc
, char *argv
[]);
46 static int cmd_rebuild(int argc
, char *argv
[]);
48 static int cmd_pause(int argc
, char *argv
[]);
50 static int cmd_help(int argc
, char *argv
[]);
51 static int cmd_config(int argc
, char *argv
[]);
53 static void print_span(struct mlx_sys_drv_span
*span
, int arms
);
54 static void print_sys_drive(struct conf_config
*conf
, int drvno
);
55 static void print_phys_drive(struct conf_config
*conf
, int chn
, int targ
);
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
, statvalid
;
189 int ctrlr
= -1, sysdrive
= -1;
191 /* Find which controller and what system drive we are */
193 if (mlxd_find_ctrlr(unit
, &ctrlr
, &sysdrive
)) {
194 warnx("couldn't get controller/drive for %s", drivepath(unit
));
196 /* If we don't have rebuild stats for this controller, get them */
197 if (rs_ctrlr
== ctrlr
) {
200 if ((fd
= open(ctrlrpath(ctrlr
), 0)) < 0) {
201 warn("can't open %s", ctrlrpath(ctrlr
));
203 if (ioctl(fd
, MLX_REBUILDSTAT
, &rs
) < 0) {
204 warn("ioctl MLX_REBUILDSTAT");
215 if ((fd
= open(drivepath(unit
), 0)) < 0) {
216 warn("can't open %s", drivepath(unit
));
221 if (ioctl(fd
, MLXD_STATUS
, &result
) < 0) {
222 warn("ioctl MLXD_STATUS");
225 case MLX_SYSD_ONLINE
:
227 printf("%s: online", drivename(unit
));
229 case MLX_SYSD_CRITICAL
:
231 printf("%s: critical", drivename(unit
));
232 if (status_result
< 1)
235 case MLX_SYSD_OFFLINE
:
237 printf("%s: offline", drivename(unit
));
238 if (status_result
< 2)
243 printf("%s: unknown status 0x%x", drivename(unit
), result
);
247 /* rebuild/check in progress on this drive? */
248 if (statvalid
&& (rs_ctrlr
== ctrlr
) &&
249 (rs
.rs_drive
== sysdrive
) && (rs
.rs_code
!= MLX_REBUILDSTAT_IDLE
)) {
251 case MLX_REBUILDSTAT_REBUILDCHECK
:
252 printf(" [consistency check");
254 case MLX_REBUILDSTAT_ADDCAPACITY
:
255 printf(" [add capacity");
257 case MLX_REBUILDSTAT_ADDCAPACITYINIT
:
258 printf(" [add capacity init");
261 printf(" [unknown operation");
263 printf(": %d/%d, %d%% complete]",
264 rs
.rs_remaining
, rs
.rs_size
,
265 ((rs
.rs_size
- rs
.rs_remaining
) / (rs
.rs_size
/ 100)));
273 /********************************************************************************
274 * Print details for the system drive (drvno) in a format that we will be
275 * able to parse later.
277 * drive?? <raidlevel> <writemode>
278 * span? 0x????????-0x???????? ????MB on <disk> [...]
282 print_span(struct mlx_sys_drv_span
*span
, int arms
)
286 printf("0x%08x-0x%08x %uMB on", span
->sp_start_lba
, span
->sp_start_lba
+ span
->sp_nblks
, span
->sp_nblks
/ 2048);
287 for (i
= 0; i
< arms
; i
++)
288 printf(" disk%02d%02d", span
->sp_arm
[i
] >> 4, span
->sp_arm
[i
] & 0x0f);
293 print_sys_drive(struct conf_config
*conf
, int drvno
)
295 struct mlx_sys_drv
*drv
= &conf
->cc_cfg
.cc_sys_drives
[drvno
];
298 printf("drive%02d ", drvno
);
299 switch(drv
->sd_raidlevel
& 0xf) {
300 case MLX_SYS_DRV_RAID0
:
303 case MLX_SYS_DRV_RAID1
:
306 case MLX_SYS_DRV_RAID3
:
309 case MLX_SYS_DRV_RAID5
:
312 case MLX_SYS_DRV_RAID6
:
315 case MLX_SYS_DRV_JBOD
:
321 printf(" write%s\n", drv
->sd_raidlevel
& MLX_SYS_DRV_WRITEBACK
? "back" : "through");
323 for (i
= 0; i
< drv
->sd_valid_spans
; i
++) {
324 printf(" span%d ", i
);
325 print_span(&drv
->sd_span
[i
], drv
->sd_valid_arms
);
329 /********************************************************************************
330 * Print details for the physical drive at chn/targ in a format suitable for
333 * <type>CCTT (<state>) "<vendor>/<model>"
338 print_phys_drive(struct conf_config
*conf
, int chn
, int targ
)
340 struct mlx_phys_drv
*drv
= &conf
->cc_cfg
.cc_phys_drives
[chn
* 16 + targ
];
342 /* if the drive isn't present, don't print it */
343 if (!(drv
->pd_flags1
& MLX_PHYS_DRV_PRESENT
))
346 mlx_print_phys_drv(drv
, chn
, targ
, "# ", 1);
353 } mlx_controller_names
[] = {
368 controller_print(int unit
, void *arg
)
370 struct mlx_enquiry2 enq
;
371 struct mlx_phys_drv pd
;
372 int verbosity
= *(int *)arg
;
375 int i
, channel
, target
;
380 /* fetch and print controller data */
381 if (mlx_enquiry(unit
, &enq
)) {
382 printf("mlx%d: error submitting ENQUIRY2\n", unit
);
385 for (i
= 0, model
= NULL
; mlx_controller_names
[i
].name
!= NULL
; i
++) {
386 if ((int)(enq
.me_hardware_id
& 0xff) == mlx_controller_names
[i
].hwid
) {
387 model
= mlx_controller_names
[i
].name
;
392 sprintf(buf
, " model 0x%x", enq
.me_hardware_id
& 0xff);
396 printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
398 enq
.me_actual_channels
,
399 enq
.me_actual_channels
> 1 ? "s" : "",
400 enq
.me_firmware_id
& 0xff,
401 (enq
.me_firmware_id
>> 8) & 0xff,
402 (enq
.me_firmware_id
>> 16),
403 (enq
.me_firmware_id
>> 24) & 0xff,
404 enq
.me_mem_size
/ (1024 * 1024));
407 printf(" Hardware ID 0x%08x\n", enq
.me_hardware_id
);
408 printf(" Firmware ID 0x%08x\n", enq
.me_firmware_id
);
409 printf(" Configured/Actual channels %d/%d\n", enq
.me_configured_channels
,
410 enq
.me_actual_channels
);
411 printf(" Max Targets %d\n", enq
.me_max_targets
);
412 printf(" Max Tags %d\n", enq
.me_max_tags
);
413 printf(" Max System Drives %d\n", enq
.me_max_sys_drives
);
414 printf(" Max Arms %d\n", enq
.me_max_arms
);
415 printf(" Max Spans %d\n", enq
.me_max_spans
);
416 printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq
.me_mem_size
,
417 enq
.me_cache_size
, enq
.me_flash_size
, enq
.me_nvram_size
);
418 printf(" DRAM type %d\n", enq
.me_mem_type
);
419 printf(" Clock Speed %dns\n", enq
.me_clock_speed
);
420 printf(" Hardware Speed %dns\n", enq
.me_hardware_speed
);
421 printf(" Max Commands %d\n", enq
.me_max_commands
);
422 printf(" Max SG Entries %d\n", enq
.me_max_sg
);
423 printf(" Max DP %d\n", enq
.me_max_dp
);
424 printf(" Max IOD %d\n", enq
.me_max_iod
);
425 printf(" Max Comb %d\n", enq
.me_max_comb
);
426 printf(" Latency %ds\n", enq
.me_latency
);
427 printf(" SCSI Timeout %ds\n", enq
.me_scsi_timeout
);
428 printf(" Min Free Lines %d\n", enq
.me_min_freelines
);
429 printf(" Rate Constant %d\n", enq
.me_rate_const
);
430 printf(" MAXBLK %d\n", enq
.me_maxblk
);
431 printf(" Blocking Factor %d sectors\n", enq
.me_blocking_factor
);
432 printf(" Cache Line Size %d blocks\n", enq
.me_cacheline
);
433 printf(" SCSI Capability %s%dMHz, %d bit\n",
434 enq
.me_scsi_cap
& (1<<4) ? "differential " : "",
435 (1 << ((enq
.me_scsi_cap
>> 2) & 3)) * 10,
436 8 << (enq
.me_scsi_cap
& 0x3));
437 printf(" Firmware Build Number %d\n", enq
.me_firmware_build
);
438 printf(" Fault Management Type %d\n", enq
.me_fault_mgmt_type
);
440 printf(" Features %b\n", enq
.me_firmware_features
,
441 "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
445 /* fetch and print physical drive data */
446 for (channel
= 0; channel
< enq
.me_configured_channels
; channel
++) {
447 for (target
= 0; target
< enq
.me_max_targets
; target
++) {
448 if ((mlx_get_device_state(unit
, channel
, target
, &pd
) == 0) &&
449 (pd
.pd_flags1
& MLX_PHYS_DRV_PRESENT
)) {
450 mlx_print_phys_drv(&pd
, channel
, target
, " ", verbosity
- 1);
452 /* XXX print device statistics? */
461 cmd_status(int argc
, char *argv
[])
463 int ch
, verbosity
= 1, i
, unit
;
467 while ((ch
= getopt(argc
, argv
, "qv")) != -1)
476 return(cmd_help(argc
, argv
));
482 mlx_foreach(controller_print
, &verbosity
);
483 mlxd_foreach(status_print
, &verbosity
);
485 for (i
= 0; i
< argc
; i
++) {
486 if ((unit
= driveunit(argv
[i
])) == -1) {
487 warnx("'%s' is not a valid drive", argv
[i
]);
489 status_print(unit
, &verbosity
);
493 return(status_result
);
496 /********************************************************************************
497 * Recscan for system drives on one or more controllers.
499 * rescan <controller> [<controller>...]
503 rescan_ctrlr(int unit
, void *junk __unused
)
508 if ((fd
= open(ctrlrpath(unit
), 0)) < 0) {
509 warn("can't open %s", ctrlrpath(unit
));
513 if (ioctl(fd
, MLX_RESCAN_DRIVES
) < 0)
514 warn("can't rescan %s", ctrlrname(unit
));
519 cmd_rescan(int argc
, char *argv
[])
521 int all
= 0, i
, ch
, unit
;
525 while ((ch
= getopt(argc
, argv
, "a")) != -1)
531 return(cmd_help(argc
, argv
));
537 mlx_foreach(rescan_ctrlr
, NULL
);
539 for (i
= 0; i
< argc
; i
++) {
540 if ((unit
= ctrlrunit(argv
[i
])) == -1) {
541 warnx("'%s' is not a valid controller", argv
[i
]);
543 rescan_ctrlr(unit
, NULL
);
550 /********************************************************************************
551 * Detach one or more system drives from a controller.
553 * detach <drive> [<drive>...]
556 * detach -a <controller> [<controller>...]
557 * Detach all drives on <controller>.
561 detach_drive(int unit
, void *arg __unused
)
566 if ((fd
= open(ctrlrpath(unit
), 0)) < 0) {
567 warn("can't open %s", ctrlrpath(unit
));
571 if (ioctl(fd
, MLX_DETACH_DRIVE
, &unit
) < 0)
572 warn("can't detach %s", drivename(unit
));
577 cmd_detach(int argc
, char *argv
[])
579 struct mlxd_foreach_action ma
;
580 int all
= 0, i
, ch
, unit
;
584 while ((ch
= getopt(argc
, argv
, "a")) != -1)
590 return(cmd_help(argc
, argv
));
596 ma
.func
= detach_drive
;
598 for (i
= 0; i
< argc
; i
++) {
599 if ((unit
= ctrlrunit(argv
[i
])) == -1) {
600 warnx("'%s' is not a valid controller", argv
[i
]);
602 mlxd_foreach_ctrlr(unit
, &ma
);
606 for (i
= 0; i
< argc
; i
++) {
607 if ((unit
= driveunit(argv
[i
])) == -1) {
608 warnx("'%s' is not a valid drive", argv
[i
]);
610 /* run across all controllers to find this drive */
611 mlx_foreach(detach_drive
, &unit
);
618 /********************************************************************************
619 * Initiate a consistency check on a system drive.
622 * Start a check of <drive>
626 cmd_check(int argc
, char *argv
[])
628 int unit
, fd
, result
;
631 return(cmd_help(argc
, argv
));
633 if ((unit
= driveunit(argv
[1])) == -1) {
634 warnx("'%s' is not a valid drive", argv
[1]);
638 if ((fd
= open(drivepath(unit
), 0)) < 0) {
639 warn("can't open %s", drivepath(unit
));
641 /* Try to start the check */
642 if ((ioctl(fd
, MLXD_CHECKASYNC
, &result
)) < 0) {
645 warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv
[1]);
648 warnx("drive %s is invalid, or not a drive which can be checked", argv
[1]);
651 warnx("drive rebuild or consistency check is already in progress on this controller");
654 warn("ioctl MLXD_CHECKASYNC");
662 /********************************************************************************
663 * Initiate a physical drive rebuild
665 * rebuild <controller> <channel>:<target>
666 * Start a rebuild of <controller>:<channel>:<target>
670 cmd_rebuild(int argc
, char *argv
[])
672 struct mlx_rebuild_request rb
;
676 return(cmd_help(argc
, argv
));
678 /* parse arguments */
679 if ((unit
= ctrlrunit(argv
[1])) == -1) {
680 warnx("'%s' is not a valid controller", argv
[1]);
683 /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
684 if ((sscanf(argv
[2], "disk%2d%2d", &rb
.rr_channel
, &rb
.rr_target
) != 2) &&
685 (sscanf(argv
[2], "unknown%2d%2d", &rb
.rr_channel
, &rb
.rr_target
) != 2)) {
686 warnx("'%s' is not a valid physical drive", argv
[2]);
690 if ((fd
= open(ctrlrpath(unit
), 0)) < 0) {
691 warn("can't open %s", ctrlrpath(unit
));
694 /* try to start the rebuild */
695 if ((ioctl(fd
, MLX_REBUILDASYNC
, &rb
)) < 0) {
696 switch(rb
.rr_status
) {
698 warnx("the drive at %d:%d is already ONLINE", rb
.rr_channel
, rb
.rr_target
);
701 warnx("drive failed during rebuild");
704 warnx("there is no drive at channel %d, target %d", rb
.rr_channel
, rb
.rr_target
);
707 warnx("drive rebuild or consistency check is already in progress on this controller");
710 warn("ioctl MLXD_REBUILDASYNC");
716 /********************************************************************************
717 * Get the configuration from the selected controller.
719 * config <controller>
720 * Print the configuration for <controller>
722 * XXX update to support adding/deleting drives.
726 cmd_config(int argc __unused
, char *argv
[] __unused
)
728 struct conf_config conf
;
729 int unit
= 0; /* XXX */
732 bzero(&conf
.cc_cfg
, sizeof(conf
.cc_cfg
));
733 if (mlx_read_configuration(unit
, &conf
.cc_cfg
)) {
734 printf("mlx%d: error submitting READ CONFIGURATION\n", unit
);
737 printf("# Controller <INSERT DETAILS HERE>\n");
738 printf("#\n# Physical devices connected:\n");
739 for (i
= 0; i
< 5; i
++)
740 for (j
= 0; j
< 16; j
++)
741 print_phys_drive(&conf
, i
, j
);
742 printf("#\n# System Drives defined:\n");
744 for (i
= 0; i
< conf
.cc_cfg
.cc_num_sys_drives
; i
++)
745 print_sys_drive(&conf
, i
);
751 /********************************************************************************
752 * Pause one or more channels on a controller
754 * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
755 * Pauses <channel> (or all channels) for <time> seconds after a
756 * delay of <delay> seconds.
757 * pause <controller> -c
758 * Cancels pending pause
761 cmd_pause(int argc
, char *argv
[])
764 int unit
, i
, ch
, fd
, cancel
= 0;
774 while ((ch
= getopt(argc
, argv
, "cd:t:")) != -1)
780 mp
.mp_when
= strtol(optarg
, &cp
, 0);
782 return(cmd_help(argc
, argv
));
785 mp
.mp_howlong
= strtol(optarg
, &cp
, 0);
787 return(cmd_help(argc
, argv
));
790 return(cmd_help(argc
, argv
));
795 /* get controller unit number that we're working on */
796 if ((argc
< 1) || ((unit
= ctrlrunit(argv
[0])) == -1))
797 return(cmd_help(oargc
, oargv
));
800 if ((fd
= open(ctrlrpath(unit
), 0)) < 0) {
801 warn("can't open %s", ctrlrpath(unit
));
806 /* controller-wide pause/cancel */
807 mp
.mp_which
= cancel
? MLX_PAUSE_CANCEL
: MLX_PAUSE_ALL
;
809 for (i
= 1; i
< argc
; i
++) {
810 ch
= strtol(argv
[i
], &cp
, 0);
812 warnx("bad channel number '%s'", argv
[i
]);
815 mp
.mp_which
|= (1 << ch
);
819 if ((ioctl(fd
, MLX_PAUSE_CHANNEL
, &mp
)) < 0)
820 warn("couldn't %s %s", cancel
? "cancel pause on" : "pause", ctrlrname(unit
));
824 #endif /* SUPPORT_PAUSE */