diff options
Diffstat (limited to 'usr.sbin/mfiutil/mfi_foreign.c')
-rw-r--r-- | usr.sbin/mfiutil/mfi_foreign.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/usr.sbin/mfiutil/mfi_foreign.c b/usr.sbin/mfiutil/mfi_foreign.c new file mode 100644 index 0000000..8c6183d --- /dev/null +++ b/usr.sbin/mfiutil/mfi_foreign.c @@ -0,0 +1,364 @@ +/* + * 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); |