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 000000000000..81b9933937c6 --- /dev/null +++ b/usr.sbin/mfiutil/mfi_foreign.c @@ -0,0 +1,364 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * 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. + * + */ + +#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_device, O_RDWR); +	if (fd < 0) { +		error = errno; +		warn("mfi_open"); +		return (error); +	} + +	printf( +	    "Are you sure you wish to clear ALL foreign configurations" +	    " on %s? [y/N] ", mfi_device); + +	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("%s: Foreign configuration cleared\n", mfi_device); +	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_device, 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("%s: Found %d foreign configurations\n", mfi_device, +	       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[64]; +	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_device, 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_device, 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 %s? [y/N] ", mfi_device); +	} 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 %s? [y/N] ", cfgidx, mfi_device); +	} + +	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("%s: All foreign configurations imported\n", +		    mfi_device); +	else +		printf("%s: Foreign configuration %d imported\n", +		    mfi_device, cfgidx); +	close(fd); +	return (0); +} +MFI_COMMAND(foreign, import, foreign_import); | 
