xref: /dragonfly/usr.sbin/mlxcontrol/command.c (revision 5062ee70)
1 /*-
2  * Copyright (c) 1999 Michael Smith
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
26  *	$FreeBSD: src/usr.sbin/mlxcontrol/command.c,v 1.3 2008/09/12 17:40:17 sepotvin Exp $
27  */
28 
29 #include <fcntl.h>
30 #include <paths.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <err.h>
36 
37 #include <dev/raid/mlx/mlxio.h>
38 #include <dev/raid/mlx/mlxreg.h>
39 
40 #include "mlxcontrol.h"
41 
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[]);
47 #ifdef SUPPORT_PAUSE
48 static int	cmd_pause(int argc, char *argv[]);
49 #endif
50 static int	cmd_help(int argc, char *argv[]);
51 static int	cmd_config(int argc, char *argv[]);
52 
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);
56 
57 struct
58 {
59     const char	*cmd;
60     int		(*func)(int argc, char *argv[]);
61     const char	*desc;
62     const char	*text;
63 } commands[] = {
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"
76      "  rescan -a\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>."},
84     {"check",	cmd_check,
85      "consistency-check a system drive",
86      "  check <drive>\n"
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."},
95 #ifdef SUPPORT_PAUSE
96     {"pause",	cmd_pause,
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>."},
105 #endif
106     {"config",	cmd_config,
107      "examine and update controller configuration",
108      "  config <controller>\n"
109      "      Print configuration for <controller>."},
110     {"help",	cmd_help,
111      "give help on usage",
112      ""},
113     {NULL, NULL, NULL, NULL}
114 };
115 
116 /********************************************************************************
117  * Command dispatch and global options parsing.
118  */
119 
120 int
121 main(int argc, char *argv[])
122 {
123     int		ch, i, oargc;
124     char	**oargv;
125 
126     oargc = argc;
127     oargv = argv;
128     while ((ch = getopt(argc, argv, "")) != -1)
129 	switch(ch) {
130 	default:
131 	    return(cmd_help(0, NULL));
132 	}
133 
134     argc -= optind;
135     argv += optind;
136 
137     if (argc > 0)
138 	for (i = 0; commands[i].cmd != NULL; i++)
139 	    if (!strcmp(argv[0], commands[i].cmd))
140 		return(commands[i].func(argc, argv));
141 
142     return(cmd_help(oargc, oargv));
143 }
144 
145 /********************************************************************************
146  * Helptext output
147  */
148 static int
149 cmd_help(int argc, char *argv[])
150 {
151     int		i;
152 
153     if (argc > 1)
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);
157 		fflush(stderr);
158 		return(0);
159 	    }
160 
161     if (argv != NULL)
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);
166     fflush(stderr);
167     return(0);
168 }
169 
170 /********************************************************************************
171  * Status output
172  *
173  * status [-qv] [<device> ...]
174  *		Prints status for <device>, or all if none listed.
175  *
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.
178  */
179 static struct mlx_rebuild_status	rs;
180 static int				rs_ctrlr = -1;
181 static int				status_result = 0;
182 
183 /* XXX more verbosity! */
184 static void
185 status_print(int unit, void *arg)
186 {
187     int				verbosity = *(int *)arg;
188     int				fd, result, statvalid;
189     int				ctrlr = -1, sysdrive = -1;
190 
191     /* Find which controller and what system drive we are */
192     statvalid = 0;
193     if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
194 	warnx("couldn't get controller/drive for %s", drivepath(unit));
195     } else {
196 	/* If we don't have rebuild stats for this controller, get them */
197 	if (rs_ctrlr == ctrlr) {
198 	    statvalid = 1;
199 	} else {
200 	    if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
201 		warn("can't open %s", ctrlrpath(ctrlr));
202 	    } else {
203 		if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
204 		    warn("ioctl MLX_REBUILDSTAT");
205 		} else {
206 		    rs_ctrlr = ctrlr;
207 		    statvalid = 1;
208 		}
209 		close(fd);
210 	    }
211 	}
212     }
213 
214     /* Get the device */
215     if ((fd = open(drivepath(unit), 0)) < 0) {
216 	warn("can't open %s", drivepath(unit));
217 	return;
218     }
219 
220     /* Get its status */
221     if (ioctl(fd, MLXD_STATUS, &result) < 0) {
222 	warn("ioctl MLXD_STATUS");
223     } else {
224 	switch(result) {
225 	case MLX_SYSD_ONLINE:
226 	    if (verbosity > 0)
227 		printf("%s: online", drivename(unit));
228 	    break;
229 	case MLX_SYSD_CRITICAL:
230 	    if (verbosity > 0)
231 		printf("%s: critical", drivename(unit));
232 	    if (status_result < 1)
233 		status_result = 1;
234 	    break;
235 	case MLX_SYSD_OFFLINE:
236 	    if (verbosity > 0)
237 		printf("%s: offline", drivename(unit));
238 	    if (status_result < 2)
239 		status_result = 2;
240 	    break;
241 	default:
242 	    if (verbosity > 0) {
243 		printf("%s: unknown status 0x%x", drivename(unit), result);
244 	    }
245 	}
246 	if (verbosity > 0) {
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)) {
250 		switch(rs.rs_code) {
251 		case MLX_REBUILDSTAT_REBUILDCHECK:
252 		    printf(" [consistency check");
253 		    break;
254 		case MLX_REBUILDSTAT_ADDCAPACITY:
255 		    printf(" [add capacity");
256 		    break;
257 		case MLX_REBUILDSTAT_ADDCAPACITYINIT:
258 		    printf(" [add capacity init");
259 		    break;
260 		default:
261 		    printf(" [unknown operation");
262 		}
263 		printf(": %d/%d, %d%% complete]",
264 		       rs.rs_remaining, rs.rs_size,
265 		       ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
266 	    }
267 	    printf("\n");
268 	}
269     }
270     close(fd);
271 }
272 
273 /********************************************************************************
274  * Print details for the system drive (drvno) in a format that we will be
275  * able to parse later.
276  *
277  * drive?? <raidlevel> <writemode>
278  *   span? 0x????????-0x???????? ????MB on <disk> [...]
279  *   ...
280  */
281 static void
282 print_span(struct mlx_sys_drv_span *span, int arms)
283 {
284     int		i;
285 
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);
289     printf("\n");
290 }
291 
292 static void
293 print_sys_drive(struct conf_config *conf, int drvno)
294 {
295     struct mlx_sys_drv	*drv = &conf->cc_cfg.cc_sys_drives[drvno];
296     int			i;
297 
298     printf("drive%02d ", drvno);
299     switch(drv->sd_raidlevel & 0xf) {
300     case MLX_SYS_DRV_RAID0:
301 	printf("RAID0");
302 	break;
303     case MLX_SYS_DRV_RAID1:
304 	printf("RAID1");
305 	break;
306     case MLX_SYS_DRV_RAID3:
307 	printf("RAID3");
308 	break;
309     case MLX_SYS_DRV_RAID5:
310 	printf("RAID5");
311 	break;
312     case MLX_SYS_DRV_RAID6:
313 	printf("RAID6");
314 	break;
315     case MLX_SYS_DRV_JBOD:
316 	printf("JBOD");
317 	break;
318     default:
319 	printf("RAID?");
320     }
321     printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
322 
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);
326     }
327 }
328 
329 /********************************************************************************
330  * Print details for the physical drive at chn/targ in a format suitable for
331  * human consumption.
332  *
333  * <type>CCTT (<state>) "<vendor>/<model>"
334  *                       ????MB <features>
335  *
336  */
337 static void
338 print_phys_drive(struct conf_config *conf, int chn, int targ)
339 {
340     struct mlx_phys_drv		*drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
341 
342     /* if the drive isn't present, don't print it */
343     if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
344 	return;
345 
346     mlx_print_phys_drv(drv, chn, targ, "# ", 1);
347 }
348 
349 static struct
350 {
351     int		hwid;
352     const char	*name;
353 } mlx_controller_names[] = {
354     {0x01,	"960P/PD"},
355     {0x02,	"960PL"},
356     {0x10,	"960PG"},
357     {0x11,	"960PJ"},
358     {0x12,	"960PR"},
359     {0x13,	"960PT"},
360     {0x14,	"960PTL0"},
361     {0x15,	"960PRL"},
362     {0x16,	"960PTL1"},
363     {0x20,	"1100PVX"},
364     {-1, NULL}
365 };
366 
367 static void
368 controller_print(int unit, void *arg)
369 {
370     struct mlx_enquiry2	enq;
371     struct mlx_phys_drv	pd;
372     int			verbosity = *(int *)arg;
373     static char		buf[80];
374     const char		*model;
375     int			i, channel, target;
376 
377     if (verbosity == 0)
378 	return;
379 
380     /* fetch and print controller data */
381     if (mlx_enquiry(unit, &enq)) {
382 	printf("mlx%d: error submitting ENQUIRY2\n", unit);
383     } else {
384 
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;
388 		break;
389 	    }
390 	}
391 	if (model == NULL) {
392 	    sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
393 	    model = buf;
394 	}
395 
396 	printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
397 	       unit, model,
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));
405 
406 	if (verbosity > 1) {
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);
439 #if 0
440 	    printf("  Features                    %pb%i\n",
441 		      "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n",
442 		      enq.me_firmware_features);
443 #endif
444 	}
445 
446 	/* fetch and print physical drive data */
447 	for (channel = 0; channel < enq.me_configured_channels; channel++) {
448 	    for (target = 0; target < enq.me_max_targets; target++) {
449 		if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
450 		    (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
451 		    mlx_print_phys_drv(&pd, channel, target, "  ", verbosity - 1);
452 		    if (verbosity > 1) {
453 			/* XXX print device statistics? */
454 		    }
455 		}
456 	    }
457 	}
458     }
459 }
460 
461 static int
462 cmd_status(int argc, char *argv[])
463 {
464     int		ch, verbosity = 1, i, unit;
465 
466     optreset = 1;
467     optind = 1;
468     while ((ch = getopt(argc, argv, "qv")) != -1)
469 	switch(ch) {
470 	case 'q':
471 	    verbosity = 0;
472 	    break;
473 	case 'v':
474 	    verbosity = 2;
475 	    break;
476 	default:
477 	    return(cmd_help(argc, argv));
478 	}
479     argc -= optind;
480     argv += optind;
481 
482     if (argc < 1) {
483 	mlx_foreach(controller_print, &verbosity);
484 	mlxd_foreach(status_print, &verbosity);
485     } else {
486 	for (i = 0; i < argc; i++) {
487 	    if ((unit = driveunit(argv[i])) == -1) {
488 		warnx("'%s' is not a valid drive", argv[i]);
489 	    } else {
490 		status_print(unit, &verbosity);
491 	    }
492 	}
493     }
494     return(status_result);
495 }
496 
497 /********************************************************************************
498  * Recscan for system drives on one or more controllers.
499  *
500  * rescan <controller> [<controller>...]
501  * rescan -a
502  */
503 static void
504 rescan_ctrlr(int unit, void *junk __unused)
505 {
506     int		fd;
507 
508     /* Get the device */
509     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
510 	warn("can't open %s", ctrlrpath(unit));
511 	return;
512     }
513 
514     if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
515 	warn("can't rescan %s", ctrlrname(unit));
516     close(fd);
517 }
518 
519 static int
520 cmd_rescan(int argc, char *argv[])
521 {
522     int		all = 0, i, ch, unit;
523 
524     optreset = 1;
525     optind = 1;
526     while ((ch = getopt(argc, argv, "a")) != -1)
527 	switch(ch) {
528 	case 'a':
529 	    all = 1;
530 	    break;
531 	default:
532 	    return(cmd_help(argc, argv));
533 	}
534     argc -= optind;
535     argv += optind;
536 
537     if (all) {
538 	mlx_foreach(rescan_ctrlr, NULL);
539     } else {
540 	for (i = 0; i < argc; i++) {
541 	    if ((unit = ctrlrunit(argv[i])) == -1) {
542 		warnx("'%s' is not a valid controller", argv[i]);
543 	    } else {
544 		rescan_ctrlr(unit, NULL);
545 	    }
546 	}
547     }
548     return(0);
549 }
550 
551 /********************************************************************************
552  * Detach one or more system drives from a controller.
553  *
554  * detach <drive> [<drive>...]
555  *		Detach <drive>.
556  *
557  * detach -a <controller> [<controller>...]
558  *		Detach all drives on <controller>.
559  *
560  */
561 static void
562 detach_drive(int unit, void *arg __unused)
563 {
564     int		fd;
565 
566     /* Get the device */
567     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
568 	warn("can't open %s", ctrlrpath(unit));
569 	return;
570     }
571 
572     if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
573 	warn("can't detach %s", drivename(unit));
574     close(fd);
575 }
576 
577 static int
578 cmd_detach(int argc, char *argv[])
579 {
580     struct mlxd_foreach_action	ma;
581     int				all = 0, i, ch, unit;
582 
583     optreset = 1;
584     optind = 1;
585     while ((ch = getopt(argc, argv, "a")) != -1)
586 	switch(ch) {
587 	case 'a':
588 	    all = 1;
589 	    break;
590 	default:
591 	    return(cmd_help(argc, argv));
592 	}
593     argc -= optind;
594     argv += optind;
595 
596     if (all) {
597 	ma.func = detach_drive;
598 	ma.arg = &unit;
599 	for (i = 0; i < argc; i++) {
600 	    if ((unit = ctrlrunit(argv[i])) == -1) {
601 		warnx("'%s' is not a valid controller", argv[i]);
602 	    } else {
603 		mlxd_foreach_ctrlr(unit, &ma);
604 	    }
605 	}
606     } else {
607 	for (i = 0; i < argc; i++) {
608 	    if ((unit = driveunit(argv[i])) == -1) {
609 		warnx("'%s' is not a valid drive", argv[i]);
610 	    } else {
611 		/* run across all controllers to find this drive */
612 		mlx_foreach(detach_drive, &unit);
613 	    }
614 	}
615     }
616     return(0);
617 }
618 
619 /********************************************************************************
620  * Initiate a consistency check on a system drive.
621  *
622  * check [<drive>]
623  *	Start a check of <drive>
624  *
625  */
626 static int
627 cmd_check(int argc, char *argv[])
628 {
629     int		unit, fd, result;
630 
631     if (argc != 2)
632 	return(cmd_help(argc, argv));
633 
634     if ((unit = driveunit(argv[1])) == -1) {
635 	warnx("'%s' is not a valid drive", argv[1]);
636     } else {
637 
638 	/* Get the device */
639 	if ((fd = open(drivepath(unit), 0)) < 0) {
640 	    warn("can't open %s", drivepath(unit));
641 	} else {
642 	    /* Try to start the check */
643 	    if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
644 		switch(result) {
645 		case 0x0002:
646 		    warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
647 		    break;
648 		case 0x0105:
649 		    warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
650 		    break;
651 		case 0x0106:
652 		    warnx("drive rebuild or consistency check is already in progress on this controller");
653 		    break;
654 		default:
655 		    warn("ioctl MLXD_CHECKASYNC");
656 		}
657 	    }
658 	}
659     }
660     return(0);
661 }
662 
663 /********************************************************************************
664  * Initiate a physical drive rebuild
665  *
666  * rebuild <controller> <channel>:<target>
667  *	Start a rebuild of <controller>:<channel>:<target>
668  *
669  */
670 static int
671 cmd_rebuild(int argc, char *argv[])
672 {
673     struct mlx_rebuild_request	rb;
674     int				unit, fd;
675 
676     if (argc != 3)
677 	return(cmd_help(argc, argv));
678 
679     /* parse arguments */
680     if ((unit = ctrlrunit(argv[1])) == -1) {
681 	warnx("'%s' is not a valid controller", argv[1]);
682 	return(1);
683     }
684     /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
685     if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
686 	(sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
687 	warnx("'%s' is not a valid physical drive", argv[2]);
688 	return(1);
689     }
690     /* get the device */
691     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
692 	warn("can't open %s", ctrlrpath(unit));
693 	return(1);
694     }
695     /* try to start the rebuild */
696     if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
697 	switch(rb.rr_status) {
698 	case 0x0002:
699 	    warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
700 	    break;
701 	case 0x0004:
702 	    warnx("drive failed during rebuild");
703 	    break;
704 	case 0x0105:
705 	    warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
706 	    break;
707 	case 0x0106:
708 	    warnx("drive rebuild or consistency check is already in progress on this controller");
709 	    break;
710 	default:
711 	    warn("ioctl MLXD_REBUILDASYNC");
712 	}
713     }
714     return(0);
715 }
716 
717 /********************************************************************************
718  * Get the configuration from the selected controller.
719  *
720  * config <controller>
721  *		Print the configuration for <controller>
722  *
723  * XXX update to support adding/deleting drives.
724  */
725 
726 int
727 cmd_config(int argc __unused, char *argv[] __unused)
728 {
729     struct conf_config	conf;
730     int			unit = 0;	/* XXX */
731     int			i, j;
732 
733     bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
734     if (mlx_read_configuration(unit, &conf.cc_cfg)) {
735 	printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
736     } else {
737 
738 	printf("# Controller <INSERT DETAILS HERE>\n");
739 	printf("#\n# Physical devices connected:\n");
740 	for (i = 0; i < 5; i++)
741 	    for (j = 0; j < 16; j++)
742 	    print_phys_drive(&conf, i, j);
743 	printf("#\n# System Drives defined:\n");
744 
745 	for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
746 	    print_sys_drive(&conf, i);
747     }
748     return(0);
749 }
750 
751 #ifdef SUPPORT_PAUSE
752 /********************************************************************************
753  * Pause one or more channels on a controller
754  *
755  * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
756  *		Pauses <channel> (or all channels) for <time> seconds after a
757  *		delay of <delay> seconds.
758  * pause <controller> -c
759  *		Cancels pending pause
760  */
761 static int
762 cmd_pause(int argc, char *argv[])
763 {
764     struct mlx_pause	mp;
765     int			unit, i, ch, fd, cancel = 0;
766     char		*cp;
767     int			oargc = argc;
768     char		**oargv = argv;
769 
770     mp.mp_which = 0;
771     mp.mp_when = 30;
772     mp.mp_howlong = 30;
773     optreset = 1;
774     optind = 1;
775     while ((ch = getopt(argc, argv, "cd:t:")) != -1)
776 	switch(ch) {
777 	case 'c':
778 	    cancel = 1;
779 	    break;
780 	case 'd':
781 	    mp.mp_when = strtol(optarg, &cp, 0);
782 	    if (*cp != 0)
783 		return(cmd_help(argc, argv));
784 	    break;
785 	case 't':
786 	    mp.mp_howlong = strtol(optarg, &cp, 0);
787 	    if (*cp != 0)
788 		return(cmd_help(argc, argv));
789 	    break;
790 	default:
791 	    return(cmd_help(argc, argv));
792 	}
793     argc -= optind;
794     argv += optind;
795 
796     /* get controller unit number that we're working on */
797     if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
798 	return(cmd_help(oargc, oargv));
799 
800     /* Get the device */
801     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
802 	warn("can't open %s", ctrlrpath(unit));
803 	return(1);
804     }
805 
806     if (argc == 1) {
807 	/* controller-wide pause/cancel */
808 	mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
809     } else {
810 	for (i = 1; i < argc; i++) {
811 	    ch = strtol(argv[i], &cp, 0);
812 	    if (*cp != 0) {
813 		warnx("bad channel number '%s'", argv[i]);
814 		continue;
815 	    } else {
816 		mp.mp_which |= (1 << ch);
817 	    }
818 	}
819     }
820     if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
821 	warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
822     close(fd);
823     return(0);
824 }
825 #endif	/* SUPPORT_PAUSE */
826 
827