2000-04-11 03:01:45 +00:00
|
|
|
/*-
|
|
|
|
* 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 <fcntl.h>
|
|
|
|
#include <paths.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <err.h>
|
|
|
|
|
2000-04-11 23:04:17 +00:00
|
|
|
#include <dev/mlx/mlxio.h>
|
|
|
|
#include <dev/mlx/mlxreg.h>
|
2000-04-11 03:01:45 +00:00
|
|
|
|
|
|
|
#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] [<drive>...]\n"
|
|
|
|
" Display status for <drive> 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 <controller> [<controller>...]\n"
|
|
|
|
" Rescan <controller> for system drives.\n"
|
|
|
|
" rescan -a\n"
|
|
|
|
" Rescan all controllers for system drives."},
|
|
|
|
{"detach", cmd_detach,
|
|
|
|
"detach system drives",
|
|
|
|
" detach <drive> [<drive>...]\n"
|
|
|
|
" Detaches <drive> from the controller.\n"
|
|
|
|
" detach -a <controller>\n"
|
|
|
|
" Detaches all drives on <controller>."},
|
|
|
|
{"check", cmd_check,
|
|
|
|
"consistency-check a system drive",
|
|
|
|
" check <drive>\n"
|
|
|
|
" Requests a check and rebuild of the parity information on <drive>.\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 <controller> <physdrive>\n"
|
|
|
|
" All system drives using space on the physical drive <physdrive>\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 <howlong>] [-d <delay>] <controller> [<channel>...]\n"
|
|
|
|
" Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
|
|
|
|
" all channels are paused.\n"
|
|
|
|
" <howlong> How long (seconds) to pause for (default 30).\n"
|
|
|
|
" <delay> How long (seconds) to wait before pausing (default 30).\n"
|
|
|
|
" pause <controller> -c\n"
|
|
|
|
" Cancels any pending pause operation on <controller>."},
|
|
|
|
#endif
|
|
|
|
{"config", cmd_config,
|
|
|
|
"examine and update controller configuration",
|
|
|
|
" config <controller>\n"
|
|
|
|
" Print configuration for <controller>."},
|
|
|
|
{"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] [<device> ...]
|
|
|
|
* Prints status for <device>, 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 <controller> [<controller>...]
|
|
|
|
* 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 <drive> [<drive>...]
|
|
|
|
* Detach <drive>.
|
|
|
|
*
|
|
|
|
* detach -a <controller> [<controller>...]
|
|
|
|
* Detach all drives on <controller>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
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 [<drive>]
|
|
|
|
* Start a check of <drive>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
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 <controller> <channel>:<target>
|
|
|
|
* Start a rebuild of <controller>:<channel>:<target>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
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 <delay>] [-t <time>] <controller> [<channel>...]
|
|
|
|
* Pauses <channel> (or all channels) for <time> seconds after a
|
|
|
|
* delay of <delay> seconds.
|
|
|
|
* pause <controller> -c
|
|
|
|
* Cancels pending pause
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
cmd_pause(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct mlx_pause mp;
|
|
|
|
int unit, i, ch, fd, cancel = 0;
|
|
|
|
char *cp;
|
|
|
|
int oargc = argc;
|
|
|
|
char **oargv = argv;
|
|
|
|
|
|
|
|
mp.mp_which = 0;
|
|
|
|
mp.mp_when = 30;
|
|
|
|
mp.mp_howlong = 30;
|
|
|
|
optreset = 1;
|
|
|
|
optind = 1;
|
|
|
|
while ((ch = getopt(argc, argv, "cd:t:")) != -1)
|
|
|
|
switch(ch) {
|
|
|
|
case 'c':
|
|
|
|
cancel = 1;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
mp.mp_when = strtol(optarg, &cp, 0);
|
|
|
|
if (*cp != 0)
|
|
|
|
return(cmd_help(argc, argv));
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
mp.mp_howlong = strtol(optarg, &cp, 0);
|
|
|
|
if (*cp != 0)
|
|
|
|
return(cmd_help(argc, argv));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return(cmd_help(argc, argv));
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* get controller unit number that we're working on */
|
|
|
|
if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
|
|
|
|
return(cmd_help(oargc, oargv));
|
|
|
|
|
|
|
|
/* Get the device */
|
|
|
|
if ((fd = open(ctrlrpath(unit), 0)) < 0) {
|
|
|
|
warn("can't open %s", ctrlrpath(unit));
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 1) {
|
|
|
|
/* controller-wide pause/cancel */
|
|
|
|
mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
|
|
|
|
} else {
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
ch = strtol(argv[i], &cp, 0);
|
|
|
|
if (*cp != 0) {
|
|
|
|
warnx("bad channel number '%s'", argv[i]);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
mp.mp_which |= (1 << ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
|
|
|
|
warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
|
|
|
|
close(fd);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
#endif /* SUPPORT_PAUSE */
|
|
|
|
|