summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mfiutil/mfi_foreign.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mfiutil/mfi_foreign.c')
-rw-r--r--usr.sbin/mfiutil/mfi_foreign.c364
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);
OpenPOWER on IntegriCloud