freebsd-skq/usr.sbin/mfiutil/mfi_foreign.c
sbruno 37b928c8dd Implement foreign volume handling. Allows admins to view foreign metadata
and clear or import it for use.

PR:     kern/172091
Submitted by:   smh@freebsd.org
Reviewed by:    jhb@freebsd.org
MFC after:      2 weeks
2013-06-08 02:54:59 +00:00

365 lines
9.1 KiB
C

/*
* Copyright (c) 2013 smh@freebsd.org
* 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 <sys/param.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libutil.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mfiutil.h"
MFI_TABLE(top, foreign);
static int
foreign_clear(__unused int ac, __unused char **av)
{
int ch, error, fd;
fd = mfi_open(mfi_unit, O_RDWR);
if (fd < 0) {
error = errno;
warn("mfi_open");
return (error);
}
printf(
"Are you sure you wish to clear ALL foreign configurations"
" on mfi%u? [y/N] ", mfi_unit);
ch = getchar();
if (ch != 'y' && ch != 'Y') {
printf("\nAborting\n");
close(fd);
return (0);
}
if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL,
0, NULL) < 0) {
error = errno;
warn("Failed to clear foreign configuration");
close(fd);
return (error);
}
printf("mfi%d: Foreign configuration cleared\n", mfi_unit);
close(fd);
return (0);
}
MFI_COMMAND(foreign, clear, foreign_clear);
static int
foreign_scan(__unused int ac, __unused char **av)
{
struct mfi_foreign_scan_info info;
int error, fd;
fd = mfi_open(mfi_unit, O_RDONLY);
if (fd < 0) {
error = errno;
warn("mfi_open");
return (error);
}
if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
sizeof(info), NULL, 0, NULL) < 0) {
error = errno;
warn("Failed to scan foreign configuration");
close(fd);
return (error);
}
printf("mfi%d: Found %d foreign configurations\n", mfi_unit,
info.count);
close(fd);
return (0);
}
MFI_COMMAND(foreign, scan, foreign_scan);
static int
foreign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic)
{
struct mfi_config_data *config;
char prefix[26];
int error;
uint8_t mbox[4];
bzero(mbox, sizeof(mbox));
mbox[0] = cfgidx;
if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) {
error = errno;
warn("Failed to get foreign config %d", error);
close(fd);
return (error);
}
if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW)
sprintf(prefix, "Foreign configuration preview %d", cfgidx);
else
sprintf(prefix, "Foreign configuration %d", cfgidx);
/*
* MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by
* 0x1a721880 which returns what looks to be drive / volume info
* but we have no real information on what these are or what they do
* so we're currently relying solely on the config returned above
*/
if (diagnostic)
dump_config(fd, config, prefix);
else {
char *ld_list;
int i;
ld_list = (char *)(config->array);
printf("%s: %d arrays, %d volumes, %d spares\n", prefix,
config->array_count, config->log_drv_count,
config->spares_count);
for (i = 0; i < config->array_count; i++)
ld_list += config->array_size;
for (i = 0; i < config->log_drv_count; i++) {
const char *level;
char size[6], stripe[5];
struct mfi_ld_config *ld;
ld = (struct mfi_ld_config *)ld_list;
format_stripe(stripe, sizeof(stripe),
ld->params.stripe_size);
/*
* foreign configs don't seem to have a secondary raid level
* but, we can use span depth here as if a LD spans multiple
* arrays of disks (2 raid 1 sets for example), we will have an
* indication based on the spam depth. swb
*/
level = mfi_raid_level(ld->params.primary_raid_level,
(ld->params.span_depth - 1));
humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512,
"", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
printf(" ID%d ", i);
printf("(%6s) %-8s |",
size, level);
printf("volume spans %d %s\n", ld->params.span_depth,
(ld->params.span_depth > 1) ? "arrays" : "array");
for (int j = 0; j < ld->params.span_depth; j++) {
char *ar_list;
struct mfi_array *ar;
uint16_t device_id;
printf(" array %u @ ", ld->span[j].array_ref);
humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512,
"", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
printf("(%6s)\n",size);
ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size);
ar = (struct mfi_array *)ar_list;
for (int k = 0; k < ar->num_drives; k++) {
device_id = ar->pd[k].ref.v.device_id;
if (device_id == 0xffff)
printf(" drive MISSING\n");
else {
printf(" drive %u %s\n", device_id,
mfi_pdstate(ar->pd[k].fw_state));
}
}
}
ld_list += config->log_drv_size;
}
}
free(config);
return (0);
}
int
display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd)
{
struct mfi_foreign_scan_info info;
uint8_t i;
int error, fd;
if (ac > 2) {
warnx("foreign display: extra arguments");
return (EINVAL);
}
fd = mfi_open(mfi_unit, O_RDONLY);
if (fd < 0) {
error = errno;
warn("mfi_open");
return (error);
}
if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
sizeof(info), NULL, 0, NULL) < 0) {
error = errno;
warn("Failed to scan foreign configuration");
close(fd);
return (error);
}
if (info.count == 0) {
warnx("foreign display: no foreign configs found");
close(fd);
return (EINVAL);
}
if (ac == 1) {
for (i = 0; i < info.count; i++) {
error = foreign_show_cfg(fd,
display_cmd, i, diagnostic);
if(error != 0) {
close(fd);
return (error);
}
if (i < info.count - 1)
printf("\n");
}
} else if (ac == 2) {
error = foreign_show_cfg(fd,
display_cmd, atoi(av[1]), diagnostic);
if (error != 0) {
close(fd);
return (error);
}
}
close(fd);
return (0);
}
static int
foreign_display(int ac, char **av)
{
return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY));
}
MFI_COMMAND(foreign, diag, foreign_display);
static int
foreign_preview(int ac, char **av)
{
return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW));
}
MFI_COMMAND(foreign, preview, foreign_preview);
static int
foreign_import(int ac, char **av)
{
struct mfi_foreign_scan_info info;
int ch, error, fd;
uint8_t cfgidx;
uint8_t mbox[4];
if (ac > 2) {
warnx("foreign preview: extra arguments");
return (EINVAL);
}
fd = mfi_open(mfi_unit, O_RDWR);
if (fd < 0) {
error = errno;
warn("mfi_open");
return (error);
}
if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
sizeof(info), NULL, 0, NULL) < 0) {
error = errno;
warn("Failed to scan foreign configuration");
close(fd);
return (error);
}
if (info.count == 0) {
warnx("foreign import: no foreign configs found");
close(fd);
return (EINVAL);
}
if (ac == 1) {
cfgidx = 0xff;
printf("Are you sure you wish to import ALL foreign "
"configurations on mfi%u? [y/N] ", mfi_unit);
} else {
/*
* While this is docmmented for MegaCli this failed with
* exit code 0x03 on the test controller which was a Supermicro
* SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based
* controller.
*/
cfgidx = atoi(av[1]);
if (cfgidx >= info.count) {
warnx("Invalid foreign config %d specified max is %d",
cfgidx, info.count - 1);
close(fd);
return (EINVAL);
}
printf("Are you sure you wish to import the foreign "
"configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit);
}
ch = getchar();
if (ch != 'y' && ch != 'Y') {
printf("\nAborting\n");
close(fd);
return (0);
}
bzero(mbox, sizeof(mbox));
mbox[0] = cfgidx;
if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox,
sizeof(mbox), NULL) < 0) {
error = errno;
warn("Failed to import foreign configuration");
close(fd);
return (error);
}
if (ac == 1)
printf("mfi%d: All foreign configurations imported\n",
mfi_unit);
else
printf("mfi%d: Foreign configuration %d imported\n", mfi_unit,
cfgidx);
close(fd);
return (0);
}
MFI_COMMAND(foreign, import, foreign_import);