diff --git a/usr.sbin/mlxcontrol/Makefile b/usr.sbin/mlxcontrol/Makefile new file mode 100644 index 000000000000..3c16b511026a --- /dev/null +++ b/usr.sbin/mlxcontrol/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= mlxcontrol +SRCS= command.c config.c interface.c util.c +MAN8= mlxcontrol.8 + +.include + diff --git a/usr.sbin/mlxcontrol/command.c b/usr.sbin/mlxcontrol/command.c new file mode 100644 index 000000000000..507f75a0123c --- /dev/null +++ b/usr.sbin/mlxcontrol/command.c @@ -0,0 +1,717 @@ +/*- + * Copyright (c) 1999 Michael Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#include +#else +#include "../sys/dev/mlx/mlxio.h" +#include "../sys/dev/mlx/mlxreg.h" +#endif + +#include "mlxcontrol.h" + +static int cmd_status(int argc, char *argv[]); +static int cmd_rescan(int argc, char *argv[]); +static int cmd_detach(int argc, char *argv[]); +static int cmd_check(int argc, char *argv[]); +static int cmd_rebuild(int argc, char *argv[]); +#ifdef SUPPORT_PAUSE +static int cmd_pause(int argc, char *argv[]); +#endif +static int cmd_help(int argc, char *argv[]); + +extern int cmd_config(int argc, char *argv[]); + + +struct +{ + char *cmd; + int (*func)(int argc, char *argv[]); + char *desc; + char *text; +} commands[] = { + {"status", cmd_status, + "displays device status", + " status [-qv] [...]\n" + " Display status for or all drives if none is listed\n" + " -q Suppress output.\n" + " -v Display verbose information.\n" + " Returns 0 if all drives tested are online, 1 if one or more are\n" + " critical, and 2 if one or more are offline."}, + {"rescan", cmd_rescan, + "scan for new system drives", + " rescan [...]\n" + " Rescan for system drives.\n" + " rescan -a\n" + " Rescan all controllers for system drives."}, + {"detach", cmd_detach, + "detach system drives", + " detach [...]\n" + " Detaches from the controller.\n" + " detach -a \n" + " Detaches all drives on ."}, + {"check", cmd_check, + "consistency-check a system drive", + " check \n" + " Requests a check and rebuild of the parity information on .\n" + " Note that each controller can only check one system drive at a time."}, + {"rebuild", cmd_rebuild, + "initiate a rebuild of a dead physical drive", + " rebuild \n" + " All system drives using space on the physical drive \n" + " are rebuilt, reconstructing all data on the drive.\n" + " Note that each controller can only perform one rebuild at a time."}, +#ifdef SUPPORT_PAUSE + {"pause", cmd_pause, + "pauses controller channels", + " pause [-t ] [-d ] [...]\n" + " Pauses SCSI I/O on and . If no channel is specified,\n" + " all channels are paused.\n" + " How long (seconds) to pause for (default 30).\n" + " How long (seconds) to wait before pausing (default 30).\n" + " pause -c\n" + " Cancels any pending pause operation on ."}, +#endif + {"config", cmd_config, + "examine and update controller configuration", + " config \n" + " Print configuration for ."}, + {"help", cmd_help, + "give help on usage", + ""}, + {NULL, NULL, NULL, NULL} +}; + +/******************************************************************************** + * Command dispatch and global options parsing. + */ + +int +main(int argc, char *argv[]) +{ + int ch, i, oargc; + char **oargv; + + oargc = argc; + oargv = argv; + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + default: + return(cmd_help(0, NULL)); + } + + argc -= optind; + argv += optind; + + if (argc > 0) + for (i = 0; commands[i].cmd != NULL; i++) + if (!strcmp(argv[0], commands[i].cmd)) + return(commands[i].func(argc, argv)); + + return(cmd_help(oargc, oargv)); +} + +/******************************************************************************** + * Helptext output + */ +static int +cmd_help(int argc, char *argv[]) +{ + int i; + + if (argc > 1) + for (i = 0; commands[i].cmd != NULL; i++) + if (!strcmp(argv[1], commands[i].cmd)) { + fprintf(stderr, "%s\n", commands[i].text); + fflush(stderr); + return(0); + } + + if (argv != NULL) + fprintf(stderr, "Unknown command '%s'.\n", argv[1]); + fprintf(stderr, "Valid commands are:\n"); + for (i = 0; commands[i].cmd != NULL; i++) + fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc); + fflush(stderr); + return(0); +} + +/******************************************************************************** + * Status output + * + * status [-qv] [ ...] + * Prints status for , or all if none listed. + * + * -q Suppresses output, command returns 0 if devices are OK, 1 if one or + * more devices are critical, 2 if one or more devices are offline. + */ +static struct mlx_rebuild_status rs; +static int rs_ctrlr = -1; +static int status_result = 0; + +/* XXX more verbosity! */ +static void +status_print(int unit, void *arg) +{ + int verbosity = *(int *)arg; + int fd, result, ctrlr, sysdrive, statvalid; + + /* Find which controller and what system drive we are */ + statvalid = 0; + if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) { + warnx("couldn't get controller/drive for %s", drivepath(unit)); + } else { + /* If we don't have rebuild stats for this controller, get them */ + if (rs_ctrlr == ctrlr) { + statvalid = 1; + } else { + if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) { + warn("can't open %s", ctrlrpath(ctrlr)); + } else { + if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) { + warn("ioctl MLX_REBUILDSTAT"); + } else { + rs_ctrlr = ctrlr; + statvalid = 1; + } + close(fd); + } + } + } + + /* Get the device */ + if ((fd = open(drivepath(unit), 0)) < 0) { + warn("can't open %s", drivepath(unit)); + return; + } + + /* Get its status */ + if (ioctl(fd, MLXD_STATUS, &result) < 0) { + warn("ioctl MLXD_STATUS"); + } else { + switch(result) { + case MLX_SYSD_ONLINE: + if (verbosity > 0) + printf("%s: online", drivename(unit)); + break; + case MLX_SYSD_CRITICAL: + if (verbosity > 0) + printf("%s: critical", drivename(unit)); + if (status_result < 1) + status_result = 1; + break; + case MLX_SYSD_OFFLINE: + if (verbosity > 0) + printf("%s: offline", drivename(unit)); + if (status_result < 2) + status_result = 2; + break; + default: + if (verbosity > 0) { + printf("%s: unknown status 0x%x", drivename(unit), result); + } + } + if (verbosity > 0) { + /* rebuild/check in progress on this drive? */ + if (statvalid && (rs_ctrlr == ctrlr) && + (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) { + switch(rs.rs_code) { + case MLX_REBUILDSTAT_REBUILDCHECK: + printf(" [consistency check"); + break; + case MLX_REBUILDSTAT_ADDCAPACITY: + printf(" [add capacity"); + break; + case MLX_REBUILDSTAT_ADDCAPACITYINIT: + printf(" [add capacity init"); + break; + default: + printf(" [unknown operation"); + } + printf(": %d/%d, %d%% complete]", + rs.rs_remaining, rs.rs_size, + ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100))); + } + printf("\n"); + } + } + close(fd); +} + +static struct +{ + int hwid; + char *name; +} mlx_controller_names[] = { + {0x01, "960P/PD"}, + {0x02, "960PL"}, + {0x10, "960PG"}, + {0x11, "960PJ"}, + {0x12, "960PR"}, + {0x13, "960PT"}, + {0x14, "960PTL0"}, + {0x15, "960PRL"}, + {0x16, "960PTL1"}, + {0x20, "1100PVX"}, + {-1, NULL} +}; + +static void +controller_print(int unit, void *arg) +{ + struct mlx_enquiry2 enq; + struct mlx_phys_drv pd; + int verbosity = *(int *)arg; + static char buf[80]; + char *model; + int i, channel, target; + + if (verbosity == 0) + return; + + /* fetch and print controller data */ + if (mlx_enquiry(unit, &enq)) { + printf("mlx%d: error submitting ENQUIRY2\n", unit); + } else { + + for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { + if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { + model = mlx_controller_names[i].name; + break; + } + } + if (model == NULL) { + sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff); + model = buf; + } + + printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", + unit, model, + enq.me_actual_channels, + enq.me_actual_channels > 1 ? "s" : "", + enq.me_firmware_id & 0xff, + (enq.me_firmware_id >> 8) & 0xff, + (enq.me_firmware_id >> 16), + (enq.me_firmware_id >> 24) & 0xff, + enq.me_mem_size / (1024 * 1024)); + + if (verbosity > 1) { + printf(" Hardware ID 0x%08x\n", enq.me_hardware_id); + printf(" Firmware ID 0x%08x\n", enq.me_firmware_id); + printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels, + enq.me_actual_channels); + printf(" Max Targets %d\n", enq.me_max_targets); + printf(" Max Tags %d\n", enq.me_max_tags); + printf(" Max System Drives %d\n", enq.me_max_sys_drives); + printf(" Max Arms %d\n", enq.me_max_arms); + printf(" Max Spans %d\n", enq.me_max_spans); + printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size, + enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size); + printf(" DRAM type %d\n", enq.me_mem_type); + printf(" Clock Speed %dns\n", enq.me_clock_speed); + printf(" Hardware Speed %dns\n", enq.me_hardware_speed); + printf(" Max Commands %d\n", enq.me_max_commands); + printf(" Max SG Entries %d\n", enq.me_max_sg); + printf(" Max DP %d\n", enq.me_max_dp); + printf(" Max IOD %d\n", enq.me_max_iod); + printf(" Max Comb %d\n", enq.me_max_comb); + printf(" Latency %ds\n", enq.me_latency); + printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout); + printf(" Min Free Lines %d\n", enq.me_min_freelines); + printf(" Rate Constant %d\n", enq.me_rate_const); + printf(" MAXBLK %d\n", enq.me_maxblk); + printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor); + printf(" Cache Line Size %d blocks\n", enq.me_cacheline); + printf(" SCSI Capability %s%dMHz, %d bit\n", + enq.me_scsi_cap & (1<<4) ? "differential " : "", + (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10, + 8 << (enq.me_scsi_cap & 0x3)); + printf(" Firmware Build Number %d\n", enq.me_firmware_build); + printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type); +#if 0 + printf(" Features %b\n", enq.me_firmware_features, + "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); +#endif + } + + /* fetch and print physical drive data */ + for (channel = 0; channel < enq.me_configured_channels; channel++) { + for (target = 0; target < enq.me_max_targets; target++) { + if ((mlx_get_device_state(unit, channel, target, &pd) == 0) && + (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) { + mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1); + if (verbosity > 1) { + /* XXX print device statistics? */ + } + } + } + } + } +} + +static int +cmd_status(int argc, char *argv[]) +{ + int ch, verbosity = 1, i, unit; + + optreset = 1; + optind = 1; + while ((ch = getopt(argc, argv, "qv")) != -1) + switch(ch) { + case 'q': + verbosity = 0; + break; + case 'v': + verbosity = 2; + break; + default: + return(cmd_help(argc, argv)); + } + argc -= optind; + argv += optind; + + if (argc < 1) { + mlx_foreach(controller_print, &verbosity); + mlxd_foreach(status_print, &verbosity); + } else { + for (i = 0; i < argc; i++) { + if ((unit = driveunit(argv[i])) == -1) { + warnx("'%s' is not a valid drive", argv[i]); + } else { + status_print(unit, &verbosity); + } + } + } + return(status_result); +} + +/******************************************************************************** + * Recscan for system drives on one or more controllers. + * + * rescan [...] + * rescan -a + */ +static void +rescan_ctrlr(int unit, void *junk) +{ + int fd; + + /* Get the device */ + if ((fd = open(ctrlrpath(unit), 0)) < 0) { + warn("can't open %s", ctrlrpath(unit)); + return; + } + + if (ioctl(fd, MLX_RESCAN_DRIVES) < 0) + warn("can't rescan %s", ctrlrname(unit)); + close(fd); +} + +static int +cmd_rescan(int argc, char *argv[]) +{ + int all = 0, i, ch, unit; + + optreset = 1; + optind = 1; + while ((ch = getopt(argc, argv, "a")) != -1) + switch(ch) { + case 'a': + all = 1; + break; + default: + return(cmd_help(argc, argv)); + } + argc -= optind; + argv += optind; + + if (all) { + mlx_foreach(rescan_ctrlr, NULL); + } else { + for (i = 0; i < argc; i++) { + if ((unit = ctrlrunit(argv[i])) == -1) { + warnx("'%s' is not a valid controller", argv[i]); + } else { + rescan_ctrlr(unit, NULL); + } + } + } + return(0); +} + +/******************************************************************************** + * Detach one or more system drives from a controller. + * + * detach [...] + * Detach . + * + * detach -a [...] + * Detach all drives on . + * + */ +static void +detach_drive(int unit, void *arg) +{ + int fd; + + /* Get the device */ + if ((fd = open(ctrlrpath(unit), 0)) < 0) { + warn("can't open %s", ctrlrpath(unit)); + return; + } + + if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0) + warn("can't detach %s", drivename(unit)); + close(fd); +} + +static int +cmd_detach(int argc, char *argv[]) +{ + struct mlxd_foreach_action ma; + int all = 0, i, ch, unit; + + optreset = 1; + optind = 1; + while ((ch = getopt(argc, argv, "a")) != -1) + switch(ch) { + case 'a': + all = 1; + break; + default: + return(cmd_help(argc, argv)); + } + argc -= optind; + argv += optind; + + if (all) { + ma.func = detach_drive; + ma.arg = &unit; + for (i = 0; i < argc; i++) { + if ((unit = ctrlrunit(argv[i])) == -1) { + warnx("'%s' is not a valid controller", argv[i]); + } else { + mlxd_foreach_ctrlr(unit, &ma); + } + } + } else { + for (i = 0; i < argc; i++) { + if ((unit = driveunit(argv[i])) == -1) { + warnx("'%s' is not a valid drive", argv[i]); + } else { + /* run across all controllers to find this drive */ + mlx_foreach(detach_drive, &unit); + } + } + } + return(0); +} + +/******************************************************************************** + * Initiate a consistency check on a system drive. + * + * check [] + * Start a check of + * + */ +static int +cmd_check(int argc, char *argv[]) +{ + int unit, fd, result; + + if (argc != 2) + return(cmd_help(argc, argv)); + + if ((unit = driveunit(argv[1])) == -1) { + warnx("'%s' is not a valid drive", argv[1]); + } else { + + /* Get the device */ + if ((fd = open(drivepath(unit), 0)) < 0) { + warn("can't open %s", drivepath(unit)); + } else { + /* Try to start the check */ + if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) { + switch(result) { + case 0x0002: + warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]); + break; + case 0x0105: + warnx("drive %s is invalid, or not a drive which can be checked", argv[1]); + break; + case 0x0106: + warnx("drive rebuild or consistency check is already in progress on this controller"); + break; + default: + warn("ioctl MLXD_CHECKASYNC"); + } + } + } + } + return(0); +} + +/******************************************************************************** + * Initiate a physical drive rebuild + * + * rebuild : + * Start a rebuild of :: + * + */ +static int +cmd_rebuild(int argc, char *argv[]) +{ + struct mlx_rebuild_request rb; + int unit, fd; + + if (argc != 3) + return(cmd_help(argc, argv)); + + /* parse arguments */ + if ((unit = ctrlrunit(argv[1])) == -1) { + warnx("'%s' is not a valid controller", argv[1]); + return(1); + } + /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */ + if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) && + (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) { + warnx("'%s' is not a valid physical drive", argv[2]); + return(1); + } + /* get the device */ + if ((fd = open(ctrlrpath(unit), 0)) < 0) { + warn("can't open %s", ctrlrpath(unit)); + return(1); + } + /* try to start the rebuild */ + if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) { + switch(rb.rr_status) { + case 0x0002: + warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target); + break; + case 0x0004: + warnx("drive failed during rebuild"); + break; + case 0x0105: + warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target); + break; + case 0x0106: + warnx("drive rebuild or consistency check is already in progress on this controller"); + break; + default: + warn("ioctl MLXD_CHECKASYNC"); + } + } + return(0); +} + +#ifdef SUPPORT_PAUSE +/******************************************************************************** + * Pause one or more channels on a controller + * + * pause [-d ] [-t