summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mfiutil/mfi_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mfiutil/mfi_config.c')
-rw-r--r--usr.sbin/mfiutil/mfi_config.c1164
1 files changed, 0 insertions, 1164 deletions
diff --git a/usr.sbin/mfiutil/mfi_config.c b/usr.sbin/mfiutil/mfi_config.c
deleted file mode 100644
index 2837694..0000000
--- a/usr.sbin/mfiutil/mfi_config.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/*-
- * Copyright (c) 2008, 2009 Yahoo!, Inc.
- * 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.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- *
- * 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/types.h>
-#ifdef DEBUG
-#include <sys/sysctl.h>
-#endif
-#include <sys/errno.h>
-#include <err.h>
-#include <libutil.h>
-#ifdef DEBUG
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "mfiutil.h"
-
-#ifdef DEBUG
-static void dump_config(int fd, struct mfi_config_data *config);
-#endif
-
-static int add_spare(int ac, char **av);
-static int remove_spare(int ac, char **av);
-
-#define powerof2(x) ((((x)-1)&(x))==0)
-
-static long
-dehumanize(const char *value)
-{
- char *vtp;
- long iv;
-
- if (value == NULL)
- return (0);
- iv = strtoq(value, &vtp, 0);
- if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
- return (0);
- }
- switch (vtp[0]) {
- case 't': case 'T':
- iv *= 1024;
- case 'g': case 'G':
- iv *= 1024;
- case 'm': case 'M':
- iv *= 1024;
- case 'k': case 'K':
- iv *= 1024;
- case '\0':
- break;
- default:
- return (0);
- }
- return (iv);
-}
-int
-mfi_config_read(int fd, struct mfi_config_data **configp)
-{
- struct mfi_config_data *config;
- uint32_t config_size;
-
- /*
- * Keep fetching the config in a loop until we have a large enough
- * buffer to hold the entire configuration.
- */
- config = NULL;
- config_size = 1024;
-fetch:
- config = reallocf(config, config_size);
- if (config == NULL)
- return (-1);
- if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
- config_size, NULL, 0, NULL) < 0)
- return (-1);
-
- if (config->size > config_size) {
- config_size = config->size;
- goto fetch;
- }
-
- *configp = config;
- return (0);
-}
-
-static struct mfi_array *
-mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
-{
- struct mfi_array *ar;
- char *p;
- int i;
-
- p = (char *)config->array;
- for (i = 0; i < config->array_count; i++) {
- ar = (struct mfi_array *)p;
- if (ar->array_ref == array_ref)
- return (ar);
- p += config->array_size;
- }
-
- return (NULL);
-}
-
-static struct mfi_ld_config *
-mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
-{
- struct mfi_ld_config *ld;
- char *p;
- int i;
-
- p = (char *)config->array + config->array_count * config->array_size;
- for (i = 0; i < config->log_drv_count; i++) {
- ld = (struct mfi_ld_config *)p;
- if (ld->properties.ld.v.target_id == target_id)
- return (ld);
- p += config->log_drv_size;
- }
-
- return (NULL);
-}
-
-static int
-clear_config(int ac, char **av)
-{
- struct mfi_ld_list list;
- int ch, fd;
- u_int i;
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- if (!mfi_reconfig_supported()) {
- warnx("The current mfi(4) driver does not support "
- "configuration changes.");
- return (EOPNOTSUPP);
- }
-
- if (mfi_ld_get_list(fd, &list, NULL) < 0) {
- warn("Failed to get volume list");
- return (errno);
- }
-
- for (i = 0; i < list.ld_count; i++) {
- if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
- warnx("Volume %s is busy and cannot be deleted",
- mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
- return (EBUSY);
- }
- }
-
- printf(
- "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
- mfi_unit);
- ch = getchar();
- if (ch != 'y' && ch != 'Y') {
- printf("\nAborting\n");
- return (0);
- }
-
- if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
- warn("Failed to clear configuration");
- return (errno);
- }
-
- printf("mfi%d: Configuration cleared\n", mfi_unit);
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, clear, clear_config);
-
-#define MFI_ARRAY_SIZE 288
-#define MAX_DRIVES_PER_ARRAY \
- ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
-
-#define RT_RAID0 0
-#define RT_RAID1 1
-#define RT_RAID5 2
-#define RT_RAID6 3
-#define RT_JBOD 4
-#define RT_CONCAT 5
-#define RT_RAID10 6
-#define RT_RAID50 7
-#define RT_RAID60 8
-
-static int
-compare_int(const void *one, const void *two)
-{
- int first, second;
-
- first = *(const int *)one;
- second = *(const int *)two;
-
- return (first - second);
-}
-
-static struct raid_type_entry {
- const char *name;
- int raid_type;
-} raid_type_table[] = {
- { "raid0", RT_RAID0 },
- { "raid-0", RT_RAID0 },
- { "raid1", RT_RAID1 },
- { "raid-1", RT_RAID1 },
- { "mirror", RT_RAID1 },
- { "raid5", RT_RAID5 },
- { "raid-5", RT_RAID5 },
- { "raid6", RT_RAID6 },
- { "raid-6", RT_RAID6 },
- { "jbod", RT_JBOD },
- { "concat", RT_CONCAT },
- { "raid10", RT_RAID10 },
- { "raid1+0", RT_RAID10 },
- { "raid-10", RT_RAID10 },
- { "raid-1+0", RT_RAID10 },
- { "raid50", RT_RAID50 },
- { "raid5+0", RT_RAID50 },
- { "raid-50", RT_RAID50 },
- { "raid-5+0", RT_RAID50 },
- { "raid60", RT_RAID60 },
- { "raid6+0", RT_RAID60 },
- { "raid-60", RT_RAID60 },
- { "raid-6+0", RT_RAID60 },
- { NULL, 0 },
-};
-
-struct config_id_state {
- int array_count;
- int log_drv_count;
- int *arrays;
- int *volumes;
- uint16_t array_ref;
- uint8_t target_id;
-};
-
-struct array_info {
- int drive_count;
- struct mfi_pd_info *drives;
- struct mfi_array *array;
-};
-
-/* Parse a comma-separated list of drives for an array. */
-static int
-parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
-{
- struct mfi_pd_info *pinfo;
- uint16_t device_id;
- char *cp;
- u_int count;
- int error;
-
- cp = array_str;
- for (count = 0; cp != NULL; count++) {
- cp = strchr(cp, ',');
- if (cp != NULL) {
- cp++;
- if (*cp == ',') {
- warnx("Invalid drive list '%s'", array_str);
- return (EINVAL);
- }
- }
- }
-
- /* Validate the number of drives for this array. */
- if (count >= MAX_DRIVES_PER_ARRAY) {
- warnx("Too many drives for a single array: max is %zu",
- MAX_DRIVES_PER_ARRAY);
- return (EINVAL);
- }
- switch (raid_type) {
- case RT_RAID1:
- case RT_RAID10:
- if (count % 2 != 0) {
- warnx("RAID1 and RAID10 require an even number of "
- "drives in each array");
- return (EINVAL);
- }
- break;
- case RT_RAID5:
- case RT_RAID50:
- if (count < 3) {
- warnx("RAID5 and RAID50 require at least 3 drives in "
- "each array");
- return (EINVAL);
- }
- break;
- case RT_RAID6:
- case RT_RAID60:
- if (count < 4) {
- warnx("RAID6 and RAID60 require at least 4 drives in "
- "each array");
- return (EINVAL);
- }
- break;
- }
-
- /* Validate each drive. */
- info->drives = calloc(count, sizeof(struct mfi_pd_info));
- info->drive_count = count;
- for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
- pinfo++) {
- error = mfi_lookup_drive(fd, cp, &device_id);
- if (error)
- return (error);
-
- if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
- warn("Failed to fetch drive info for drive %s", cp);
- return (errno);
- }
-
- if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
- warnx("Drive %u is not available", device_id);
- return (EINVAL);
- }
- }
-
- return (0);
-}
-
-/*
- * Find the next free array ref assuming that 'array_ref' is the last
- * one used. 'array_ref' should be 0xffff for the initial test.
- */
-static uint16_t
-find_next_array(struct config_id_state *state)
-{
- int i;
-
- /* Assume the current one is used. */
- state->array_ref++;
-
- /* Find the next free one. */
- for (i = 0; i < state->array_count; i++)
- if (state->arrays[i] == state->array_ref)
- state->array_ref++;
- return (state->array_ref);
-}
-
-/*
- * Find the next free volume ID assuming that 'target_id' is the last
- * one used. 'target_id' should be 0xff for the initial test.
- */
-static uint8_t
-find_next_volume(struct config_id_state *state)
-{
- int i;
-
- /* Assume the current one is used. */
- state->target_id++;
-
- /* Find the next free one. */
- for (i = 0; i < state->log_drv_count; i++)
- if (state->volumes[i] == state->target_id)
- state->target_id++;
- return (state->target_id);
-}
-
-/* Populate an array with drives. */
-static void
-build_array(int fd, char *arrayp, struct array_info *array_info,
- struct config_id_state *state, int verbose)
-{
- struct mfi_array *ar = (struct mfi_array *)arrayp;
- int i;
-
- ar->size = array_info->drives[0].coerced_size;
- ar->num_drives = array_info->drive_count;
- ar->array_ref = find_next_array(state);
- for (i = 0; i < array_info->drive_count; i++) {
- if (verbose)
- printf("Adding drive %u to array %u\n",
- array_info->drives[i].ref.v.device_id,
- ar->array_ref);
- if (ar->size > array_info->drives[i].coerced_size)
- ar->size = array_info->drives[i].coerced_size;
- ar->pd[i].ref = array_info->drives[i].ref;
- ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
- }
- array_info->array = ar;
-}
-
-/*
- * Create a volume that spans one or more arrays.
- */
-static void
-build_volume(char *volumep, int narrays, struct array_info *arrays,
- int raid_type, long stripe_size, struct config_id_state *state, int verbose)
-{
- struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
- struct mfi_array *ar;
- int i;
-
- /* properties */
- ld->properties.ld.v.target_id = find_next_volume(state);
- ld->properties.ld.v.seq = 0;
- ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
- MR_LD_CACHE_WRITE_BACK;
- ld->properties.access_policy = MFI_LD_ACCESS_RW;
- ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
- ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
- MR_LD_CACHE_WRITE_BACK;
- ld->properties.no_bgi = 0;
-
- /* params */
- switch (raid_type) {
- case RT_RAID0:
- case RT_JBOD:
- ld->params.primary_raid_level = DDF_RAID0;
- ld->params.raid_level_qualifier = 0;
- ld->params.secondary_raid_level = 0;
- break;
- case RT_RAID1:
- ld->params.primary_raid_level = DDF_RAID1;
- ld->params.raid_level_qualifier = 0;
- ld->params.secondary_raid_level = 0;
- break;
- case RT_RAID5:
- ld->params.primary_raid_level = DDF_RAID5;
- ld->params.raid_level_qualifier = 3;
- ld->params.secondary_raid_level = 0;
- break;
- case RT_RAID6:
- ld->params.primary_raid_level = DDF_RAID6;
- ld->params.raid_level_qualifier = 3;
- ld->params.secondary_raid_level = 0;
- break;
- case RT_CONCAT:
- ld->params.primary_raid_level = DDF_CONCAT;
- ld->params.raid_level_qualifier = 0;
- ld->params.secondary_raid_level = 0;
- break;
- case RT_RAID10:
- ld->params.primary_raid_level = DDF_RAID1;
- ld->params.raid_level_qualifier = 0;
- ld->params.secondary_raid_level = 3; /* XXX? */
- break;
- case RT_RAID50:
- /*
- * XXX: This appears to work though the card's BIOS
- * complains that the configuration is foreign. The
- * BIOS setup does not allow for creation of RAID-50
- * or RAID-60 arrays. The only nested array
- * configuration it allows for is RAID-10.
- */
- ld->params.primary_raid_level = DDF_RAID5;
- ld->params.raid_level_qualifier = 3;
- ld->params.secondary_raid_level = 3; /* XXX? */
- break;
- case RT_RAID60:
- ld->params.primary_raid_level = DDF_RAID6;
- ld->params.raid_level_qualifier = 3;
- ld->params.secondary_raid_level = 3; /* XXX? */
- break;
- }
-
- /*
- * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
- * ffs() to simulate log2(stripe_size).
- */
- ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
- ld->params.num_drives = arrays[0].array->num_drives;
- ld->params.span_depth = narrays;
- ld->params.state = MFI_LD_STATE_OPTIMAL;
- ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
- ld->params.is_consistent = 0;
-
- /* spans */
- for (i = 0; i < narrays; i++) {
- ar = arrays[i].array;
- if (verbose)
- printf("Adding array %u to volume %u\n", ar->array_ref,
- ld->properties.ld.v.target_id);
- ld->span[i].start_block = 0;
- ld->span[i].num_blocks = ar->size;
- ld->span[i].array_ref = ar->array_ref;
- }
-}
-
-static int
-create_volume(int ac, char **av)
-{
- struct mfi_config_data *config;
- struct mfi_array *ar;
- struct mfi_ld_config *ld;
- struct config_id_state state;
- size_t config_size;
- char *p, *cfg_arrays, *cfg_volumes;
- int error, fd, i, raid_type;
- int narrays, nvolumes, arrays_per_volume;
- struct array_info *arrays;
- long stripe_size;
-#ifdef DEBUG
- int dump;
-#endif
- int ch, verbose;
-
- /*
- * Backwards compat. Map 'create volume' to 'create' and
- * 'create spare' to 'add'.
- */
- if (ac > 1) {
- if (strcmp(av[1], "volume") == 0) {
- av++;
- ac--;
- } else if (strcmp(av[1], "spare") == 0) {
- av++;
- ac--;
- return (add_spare(ac, av));
- }
- }
-
- if (ac < 2) {
- warnx("create volume: volume type required");
- return (EINVAL);
- }
-
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- if (!mfi_reconfig_supported()) {
- warnx("The current mfi(4) driver does not support "
- "configuration changes.");
- return (EOPNOTSUPP);
- }
-
- /* Lookup the RAID type first. */
- raid_type = -1;
- for (i = 0; raid_type_table[i].name != NULL; i++)
- if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
- raid_type = raid_type_table[i].raid_type;
- break;
- }
-
- if (raid_type == -1) {
- warnx("Unknown or unsupported volume type %s", av[1]);
- return (EINVAL);
- }
-
- /* Parse any options. */
- optind = 2;
-#ifdef DEBUG
- dump = 0;
-#endif
- verbose = 0;
- stripe_size = 64 * 1024;
-
- while ((ch = getopt(ac, av, "ds:v")) != -1) {
- switch (ch) {
-#ifdef DEBUG
- case 'd':
- dump = 1;
- break;
-#endif
- case 's':
- stripe_size = dehumanize(optarg);
- if ((stripe_size < 512) || (!powerof2(stripe_size)))
- stripe_size = 64 * 1024;
- break;
- case 'v':
- verbose = 1;
- break;
- case '?':
- default:
- return (EINVAL);
- }
- }
- ac -= optind;
- av += optind;
-
- /* Parse all the arrays. */
- narrays = ac;
- if (narrays == 0) {
- warnx("At least one drive list is required");
- return (EINVAL);
- }
- switch (raid_type) {
- case RT_RAID0:
- case RT_RAID1:
- case RT_RAID5:
- case RT_RAID6:
- case RT_CONCAT:
- if (narrays != 1) {
- warnx("Only one drive list can be specified");
- return (EINVAL);
- }
- break;
- case RT_RAID10:
- case RT_RAID50:
- case RT_RAID60:
- if (narrays < 1) {
- warnx("RAID10, RAID50, and RAID60 require at least "
- "two drive lists");
- return (EINVAL);
- }
- if (narrays > MFI_MAX_SPAN_DEPTH) {
- warnx("Volume spans more than %d arrays",
- MFI_MAX_SPAN_DEPTH);
- return (EINVAL);
- }
- break;
- }
- arrays = calloc(narrays, sizeof(*arrays));
- for (i = 0; i < narrays; i++) {
- error = parse_array(fd, raid_type, av[i], &arrays[i]);
- if (error)
- return (error);
- }
-
- switch (raid_type) {
- case RT_RAID10:
- case RT_RAID50:
- case RT_RAID60:
- for (i = 1; i < narrays; i++) {
- if (arrays[i].drive_count != arrays[0].drive_count) {
- warnx("All arrays must contain the same "
- "number of drives");
- return (EINVAL);
- }
- }
- break;
- }
-
- /*
- * Fetch the current config and build sorted lists of existing
- * array and volume identifiers.
- */
- if (mfi_config_read(fd, &config) < 0) {
- warn("Failed to read configuration");
- return (errno);
- }
- p = (char *)config->array;
- state.array_ref = 0xffff;
- state.target_id = 0xff;
- state.array_count = config->array_count;
- if (config->array_count > 0) {
- state.arrays = calloc(config->array_count, sizeof(int));
- for (i = 0; i < config->array_count; i++) {
- ar = (struct mfi_array *)p;
- state.arrays[i] = ar->array_ref;
- p += config->array_size;
- }
- qsort(state.arrays, config->array_count, sizeof(int),
- compare_int);
- } else
- state.arrays = NULL;
- state.log_drv_count = config->log_drv_count;
- if (config->log_drv_count) {
- state.volumes = calloc(config->log_drv_count, sizeof(int));
- for (i = 0; i < config->log_drv_count; i++) {
- ld = (struct mfi_ld_config *)p;
- state.volumes[i] = ld->properties.ld.v.target_id;
- p += config->log_drv_size;
- }
- qsort(state.volumes, config->log_drv_count, sizeof(int),
- compare_int);
- } else
- state.volumes = NULL;
- free(config);
-
- /* Determine the size of the configuration we will build. */
- switch (raid_type) {
- case RT_RAID0:
- case RT_RAID1:
- case RT_RAID5:
- case RT_RAID6:
- case RT_CONCAT:
- case RT_JBOD:
- /* Each volume spans a single array. */
- nvolumes = narrays;
- break;
- case RT_RAID10:
- case RT_RAID50:
- case RT_RAID60:
- /* A single volume spans multiple arrays. */
- nvolumes = 1;
- break;
- default:
- /* Pacify gcc. */
- abort();
- }
-
- config_size = sizeof(struct mfi_config_data) +
- sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
- config = calloc(1, config_size);
- config->size = config_size;
- config->array_count = narrays;
- config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
- config->log_drv_count = nvolumes;
- config->log_drv_size = sizeof(struct mfi_ld_config);
- config->spares_count = 0;
- config->spares_size = 40; /* XXX: Firmware hardcode */
- cfg_arrays = (char *)config->array;
- cfg_volumes = cfg_arrays + config->array_size * narrays;
-
- /* Build the arrays. */
- for (i = 0; i < narrays; i++) {
- build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
- cfg_arrays += config->array_size;
- }
-
- /* Now build the volume(s). */
- arrays_per_volume = narrays / nvolumes;
- for (i = 0; i < nvolumes; i++) {
- build_volume(cfg_volumes, arrays_per_volume,
- &arrays[i * arrays_per_volume], raid_type, stripe_size,
- &state, verbose);
- cfg_volumes += config->log_drv_size;
- }
-
-#ifdef DEBUG
- if (dump)
- dump_config(fd, config);
- else
-#endif
-
- /* Send the new config to the controller. */
- if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
- NULL, 0, NULL) < 0) {
- warn("Failed to add volume");
- return (errno);
- }
-
- /* Clean up. */
- free(config);
- if (state.log_drv_count > 0)
- free(state.volumes);
- if (state.array_count > 0)
- free(state.arrays);
- for (i = 0; i < narrays; i++)
- free(arrays[i].drives);
- free(arrays);
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, create, create_volume);
-
-static int
-delete_volume(int ac, char **av)
-{
- struct mfi_ld_info info;
- int fd;
- uint8_t target_id, mbox[4];
-
- /*
- * Backwards compat. Map 'delete volume' to 'delete' and
- * 'delete spare' to 'remove'.
- */
- if (ac > 1) {
- if (strcmp(av[1], "volume") == 0) {
- av++;
- ac--;
- } else if (strcmp(av[1], "spare") == 0) {
- av++;
- ac--;
- return (remove_spare(ac, av));
- }
- }
-
- if (ac != 2) {
- warnx("delete volume: volume required");
- return (EINVAL);
- }
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- if (!mfi_reconfig_supported()) {
- warnx("The current mfi(4) driver does not support "
- "configuration changes.");
- return (EOPNOTSUPP);
- }
-
- if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
- warn("Invalid volume %s", av[1]);
- return (errno);
- }
-
- if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
- warn("Failed to get info for volume %d", target_id);
- return (errno);
- }
-
- if (mfi_volume_busy(fd, target_id)) {
- warnx("Volume %s is busy and cannot be deleted",
- mfi_volume_name(fd, target_id));
- return (EBUSY);
- }
-
- mbox_store_ldref(mbox, &info.ld_config.properties.ld);
- if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
- sizeof(mbox), NULL) < 0) {
- warn("Failed to delete volume");
- return (errno);
- }
-
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, delete, delete_volume);
-
-static int
-add_spare(int ac, char **av)
-{
- struct mfi_pd_info info;
- struct mfi_config_data *config;
- struct mfi_array *ar;
- struct mfi_ld_config *ld;
- struct mfi_spare *spare;
- uint16_t device_id;
- uint8_t target_id;
- char *p;
- int error, fd, i;
-
- if (ac < 2) {
- warnx("add spare: drive required");
- return (EINVAL);
- }
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- error = mfi_lookup_drive(fd, av[1], &device_id);
- if (error)
- return (error);
-
- if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
- warn("Failed to fetch drive info");
- return (errno);
- }
-
- if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
- warnx("Drive %u is not available", device_id);
- return (EINVAL);
- }
-
- if (ac > 2) {
- if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
- warn("Invalid volume %s", av[2]);
- return (errno);
- }
- }
-
- if (mfi_config_read(fd, &config) < 0) {
- warn("Failed to read configuration");
- return (errno);
- }
-
- spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
- config->array_count);
- bzero(spare, sizeof(struct mfi_spare));
- spare->ref = info.ref;
-
- if (ac == 2) {
- /* Global spare backs all arrays. */
- p = (char *)config->array;
- for (i = 0; i < config->array_count; i++) {
- ar = (struct mfi_array *)p;
- if (ar->size > info.coerced_size) {
- warnx("Spare isn't large enough for array %u",
- ar->array_ref);
- return (EINVAL);
- }
- p += config->array_size;
- }
- spare->array_count = 0;
- } else {
- /*
- * Dedicated spares only back the arrays for a
- * specific volume.
- */
- ld = mfi_config_lookup_volume(config, target_id);
- if (ld == NULL) {
- warnx("Did not find volume %d", target_id);
- return (EINVAL);
- }
-
- spare->spare_type |= MFI_SPARE_DEDICATED;
- spare->array_count = ld->params.span_depth;
- for (i = 0; i < ld->params.span_depth; i++) {
- ar = mfi_config_lookup_array(config,
- ld->span[i].array_ref);
- if (ar == NULL) {
- warnx("Missing array; inconsistent config?");
- return (ENXIO);
- }
- if (ar->size > info.coerced_size) {
- warnx("Spare isn't large enough for array %u",
- ar->array_ref);
- return (EINVAL);
- }
- spare->array_ref[i] = ar->array_ref;
- }
- }
- free(config);
-
- if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
- sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
- NULL, 0, NULL) < 0) {
- warn("Failed to assign spare");
- return (errno);
- }
-
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, add, add_spare);
-
-static int
-remove_spare(int ac, char **av)
-{
- struct mfi_pd_info info;
- int error, fd;
- uint16_t device_id;
- uint8_t mbox[4];
-
- if (ac != 2) {
- warnx("remove spare: drive required");
- return (EINVAL);
- }
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- error = mfi_lookup_drive(fd, av[1], &device_id);
- if (error)
- return (error);
-
- /* Get the info for this drive. */
- if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
- warn("Failed to fetch info for drive %u", device_id);
- return (errno);
- }
-
- if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
- warnx("Drive %u is not a hot spare", device_id);
- return (EINVAL);
- }
-
- mbox_store_pdref(mbox, &info.ref);
- if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
- sizeof(mbox), NULL) < 0) {
- warn("Failed to delete spare");
- return (errno);
- }
-
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, remove, remove_spare);
-
-#ifdef DEBUG
-/* Display raw data about a config. */
-static void
-dump_config(int fd, struct mfi_config_data *config)
-{
- struct mfi_array *ar;
- struct mfi_ld_config *ld;
- struct mfi_spare *sp;
- struct mfi_pd_info pinfo;
- uint16_t device_id;
- char *p;
- int i, j;
-
- printf(
- "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
- mfi_unit, config->array_count, config->log_drv_count,
- config->spares_count);
- printf(" array size: %u\n", config->array_size);
- printf(" volume size: %u\n", config->log_drv_size);
- printf(" spare size: %u\n", config->spares_size);
- p = (char *)config->array;
-
- for (i = 0; i < config->array_count; i++) {
- ar = (struct mfi_array *)p;
- printf(" array %u of %u drives:\n", ar->array_ref,
- ar->num_drives);
- printf(" size = %ju\n", (uintmax_t)ar->size);
- for (j = 0; j < ar->num_drives; j++) {
- device_id = ar->pd[j].ref.v.device_id;
- if (device_id == 0xffff)
- printf(" drive MISSING\n");
- else {
- printf(" drive %u %s\n", device_id,
- mfi_pdstate(ar->pd[j].fw_state));
- if (mfi_pd_get_info(fd, device_id, &pinfo,
- NULL) >= 0) {
- printf(" raw size: %ju\n",
- (uintmax_t)pinfo.raw_size);
- printf(" non-coerced size: %ju\n",
- (uintmax_t)pinfo.non_coerced_size);
- printf(" coerced size: %ju\n",
- (uintmax_t)pinfo.coerced_size);
- }
- }
- }
- p += config->array_size;
- }
-
- for (i = 0; i < config->log_drv_count; i++) {
- ld = (struct mfi_ld_config *)p;
- printf(" volume %s ",
- mfi_volume_name(fd, ld->properties.ld.v.target_id));
- printf("%s %s",
- mfi_raid_level(ld->params.primary_raid_level,
- ld->params.secondary_raid_level),
- mfi_ldstate(ld->params.state));
- if (ld->properties.name[0] != '\0')
- printf(" <%s>", ld->properties.name);
- printf("\n");
- printf(" primary raid level: %u\n",
- ld->params.primary_raid_level);
- printf(" raid level qualifier: %u\n",
- ld->params.raid_level_qualifier);
- printf(" secondary raid level: %u\n",
- ld->params.secondary_raid_level);
- printf(" stripe size: %u\n", ld->params.stripe_size);
- printf(" num drives: %u\n", ld->params.num_drives);
- printf(" init state: %u\n", ld->params.init_state);
- printf(" consistent: %u\n", ld->params.is_consistent);
- printf(" no bgi: %u\n", ld->properties.no_bgi);
- printf(" spans:\n");
- for (j = 0; j < ld->params.span_depth; j++) {
- printf(" array %u @ ", ld->span[j].array_ref);
- printf("%ju : %ju\n",
- (uintmax_t)ld->span[j].start_block,
- (uintmax_t)ld->span[j].num_blocks);
- }
- p += config->log_drv_size;
- }
-
- for (i = 0; i < config->spares_count; i++) {
- sp = (struct mfi_spare *)p;
- printf(" %s spare %u ",
- sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
- "global", sp->ref.v.device_id);
- printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
- printf(" backs:\n");
- for (j = 0; j < sp->array_count; j++)
- printf(" array %u\n", sp->array_ref[j]);
- p += config->spares_size;
- }
-}
-
-static int
-debug_config(int ac, char **av)
-{
- struct mfi_config_data *config;
- int fd;
-
- if (ac != 1) {
- warnx("debug: extra arguments");
- return (EINVAL);
- }
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- /* Get the config from the controller. */
- if (mfi_config_read(fd, &config) < 0) {
- warn("Failed to get config");
- return (errno);
- }
-
- /* Dump out the configuration. */
- dump_config(fd, config);
- free(config);
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, debug, debug_config);
-
-static int
-dump(int ac, char **av)
-{
- struct mfi_config_data *config;
- char buf[64];
- size_t len;
- int fd;
-
- if (ac != 1) {
- warnx("dump: extra arguments");
- return (EINVAL);
- }
-
- fd = mfi_open(mfi_unit);
- if (fd < 0) {
- warn("mfi_open");
- return (errno);
- }
-
- /* Get the stashed copy of the last dcmd from the driver. */
- snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
- if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
- warn("Failed to read debug command");
- if (errno == ENOENT)
- errno = EOPNOTSUPP;
- return (errno);
- }
-
- config = malloc(len);
- if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
- warn("Failed to read debug command");
- return (errno);
- }
- dump_config(fd, config);
- free(config);
- close(fd);
-
- return (0);
-}
-MFI_COMMAND(top, dump, dump);
-#endif
OpenPOWER on IntegriCloud