1763fae79SScott Long /*- 2763fae79SScott Long * Copyright (c) 2008, 2009 Yahoo!, Inc. 3763fae79SScott Long * All rights reserved. 4763fae79SScott Long * 5763fae79SScott Long * Redistribution and use in source and binary forms, with or without 6763fae79SScott Long * modification, are permitted provided that the following conditions 7763fae79SScott Long * are met: 8763fae79SScott Long * 1. Redistributions of source code must retain the above copyright 9763fae79SScott Long * notice, this list of conditions and the following disclaimer. 10763fae79SScott Long * 2. Redistributions in binary form must reproduce the above copyright 11763fae79SScott Long * notice, this list of conditions and the following disclaimer in the 12763fae79SScott Long * documentation and/or other materials provided with the distribution. 13763fae79SScott Long * 3. The names of the authors may not be used to endorse or promote 14763fae79SScott Long * products derived from this software without specific prior written 15763fae79SScott Long * permission. 16763fae79SScott Long * 17763fae79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18763fae79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19763fae79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20763fae79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21763fae79SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22763fae79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23763fae79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24763fae79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25763fae79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26763fae79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27763fae79SScott Long * SUCH DAMAGE. 28763fae79SScott Long * 29763fae79SScott Long * $FreeBSD$ 30763fae79SScott Long */ 31763fae79SScott Long 32c02999d9SJohn Baldwin #include <sys/param.h> 33763fae79SScott Long #ifdef DEBUG 34763fae79SScott Long #include <sys/sysctl.h> 35763fae79SScott Long #endif 36763fae79SScott Long #include <err.h> 37c02999d9SJohn Baldwin #include <errno.h> 38763fae79SScott Long #include <libutil.h> 39763fae79SScott Long #ifdef DEBUG 40763fae79SScott Long #include <stdint.h> 41763fae79SScott Long #endif 42763fae79SScott Long #include <stdio.h> 43763fae79SScott Long #include <stdlib.h> 44763fae79SScott Long #include <string.h> 45763fae79SScott Long #include <unistd.h> 46763fae79SScott Long #include "mfiutil.h" 47763fae79SScott Long 48763fae79SScott Long #ifdef DEBUG 49763fae79SScott Long static void dump_config(int fd, struct mfi_config_data *config); 50763fae79SScott Long #endif 51763fae79SScott Long 52763fae79SScott Long static int add_spare(int ac, char **av); 53763fae79SScott Long static int remove_spare(int ac, char **av); 54763fae79SScott Long 55763fae79SScott Long static long 56763fae79SScott Long dehumanize(const char *value) 57763fae79SScott Long { 58763fae79SScott Long char *vtp; 59763fae79SScott Long long iv; 60763fae79SScott Long 61763fae79SScott Long if (value == NULL) 62763fae79SScott Long return (0); 63763fae79SScott Long iv = strtoq(value, &vtp, 0); 64763fae79SScott Long if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 65763fae79SScott Long return (0); 66763fae79SScott Long } 67763fae79SScott Long switch (vtp[0]) { 68763fae79SScott Long case 't': case 'T': 69763fae79SScott Long iv *= 1024; 70763fae79SScott Long case 'g': case 'G': 71763fae79SScott Long iv *= 1024; 72763fae79SScott Long case 'm': case 'M': 73763fae79SScott Long iv *= 1024; 74763fae79SScott Long case 'k': case 'K': 75763fae79SScott Long iv *= 1024; 76763fae79SScott Long case '\0': 77763fae79SScott Long break; 78763fae79SScott Long default: 79763fae79SScott Long return (0); 80763fae79SScott Long } 81763fae79SScott Long return (iv); 82763fae79SScott Long } 83763fae79SScott Long int 84763fae79SScott Long mfi_config_read(int fd, struct mfi_config_data **configp) 85763fae79SScott Long { 86763fae79SScott Long struct mfi_config_data *config; 87763fae79SScott Long uint32_t config_size; 88375c4656SBjoern A. Zeeb int error; 89763fae79SScott Long 90763fae79SScott Long /* 91763fae79SScott Long * Keep fetching the config in a loop until we have a large enough 92763fae79SScott Long * buffer to hold the entire configuration. 93763fae79SScott Long */ 94763fae79SScott Long config = NULL; 95763fae79SScott Long config_size = 1024; 96763fae79SScott Long fetch: 97763fae79SScott Long config = reallocf(config, config_size); 98763fae79SScott Long if (config == NULL) 99763fae79SScott Long return (-1); 100763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config, 101375c4656SBjoern A. Zeeb config_size, NULL, 0, NULL) < 0) { 102375c4656SBjoern A. Zeeb error = errno; 103375c4656SBjoern A. Zeeb free(config); 104375c4656SBjoern A. Zeeb errno = error; 105763fae79SScott Long return (-1); 106375c4656SBjoern A. Zeeb } 107763fae79SScott Long 108763fae79SScott Long if (config->size > config_size) { 109763fae79SScott Long config_size = config->size; 110763fae79SScott Long goto fetch; 111763fae79SScott Long } 112763fae79SScott Long 113763fae79SScott Long *configp = config; 114763fae79SScott Long return (0); 115763fae79SScott Long } 116763fae79SScott Long 117763fae79SScott Long static struct mfi_array * 118763fae79SScott Long mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 119763fae79SScott Long { 120763fae79SScott Long struct mfi_array *ar; 121763fae79SScott Long char *p; 122763fae79SScott Long int i; 123763fae79SScott Long 124763fae79SScott Long p = (char *)config->array; 125763fae79SScott Long for (i = 0; i < config->array_count; i++) { 126763fae79SScott Long ar = (struct mfi_array *)p; 127763fae79SScott Long if (ar->array_ref == array_ref) 128763fae79SScott Long return (ar); 129763fae79SScott Long p += config->array_size; 130763fae79SScott Long } 131763fae79SScott Long 132763fae79SScott Long return (NULL); 133763fae79SScott Long } 134763fae79SScott Long 135763fae79SScott Long static struct mfi_ld_config * 136763fae79SScott Long mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 137763fae79SScott Long { 138763fae79SScott Long struct mfi_ld_config *ld; 139763fae79SScott Long char *p; 140763fae79SScott Long int i; 141763fae79SScott Long 142763fae79SScott Long p = (char *)config->array + config->array_count * config->array_size; 143763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 144763fae79SScott Long ld = (struct mfi_ld_config *)p; 145763fae79SScott Long if (ld->properties.ld.v.target_id == target_id) 146763fae79SScott Long return (ld); 147763fae79SScott Long p += config->log_drv_size; 148763fae79SScott Long } 149763fae79SScott Long 150763fae79SScott Long return (NULL); 151763fae79SScott Long } 152763fae79SScott Long 153763fae79SScott Long static int 154763fae79SScott Long clear_config(int ac, char **av) 155763fae79SScott Long { 156763fae79SScott Long struct mfi_ld_list list; 157c02999d9SJohn Baldwin int ch, error, fd; 158763fae79SScott Long u_int i; 159763fae79SScott Long 160763fae79SScott Long fd = mfi_open(mfi_unit); 161763fae79SScott Long if (fd < 0) { 162c02999d9SJohn Baldwin error = errno; 163763fae79SScott Long warn("mfi_open"); 164c02999d9SJohn Baldwin return (error); 165763fae79SScott Long } 166763fae79SScott Long 167763fae79SScott Long if (!mfi_reconfig_supported()) { 168763fae79SScott Long warnx("The current mfi(4) driver does not support " 169763fae79SScott Long "configuration changes."); 170375c4656SBjoern A. Zeeb close(fd); 171763fae79SScott Long return (EOPNOTSUPP); 172763fae79SScott Long } 173763fae79SScott Long 174763fae79SScott Long if (mfi_ld_get_list(fd, &list, NULL) < 0) { 175c02999d9SJohn Baldwin error = errno; 176763fae79SScott Long warn("Failed to get volume list"); 177375c4656SBjoern A. Zeeb close(fd); 178c02999d9SJohn Baldwin return (error); 179763fae79SScott Long } 180763fae79SScott Long 181763fae79SScott Long for (i = 0; i < list.ld_count; i++) { 182763fae79SScott Long if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 183763fae79SScott Long warnx("Volume %s is busy and cannot be deleted", 184763fae79SScott Long mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 185375c4656SBjoern A. Zeeb close(fd); 186763fae79SScott Long return (EBUSY); 187763fae79SScott Long } 188763fae79SScott Long } 189763fae79SScott Long 190763fae79SScott Long printf( 191763fae79SScott Long "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 192763fae79SScott Long mfi_unit); 193763fae79SScott Long ch = getchar(); 194763fae79SScott Long if (ch != 'y' && ch != 'Y') { 195763fae79SScott Long printf("\nAborting\n"); 196375c4656SBjoern A. Zeeb close(fd); 197763fae79SScott Long return (0); 198763fae79SScott Long } 199763fae79SScott Long 200763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 201c02999d9SJohn Baldwin error = errno; 202763fae79SScott Long warn("Failed to clear configuration"); 203375c4656SBjoern A. Zeeb close(fd); 204c02999d9SJohn Baldwin return (error); 205763fae79SScott Long } 206763fae79SScott Long 207763fae79SScott Long printf("mfi%d: Configuration cleared\n", mfi_unit); 208763fae79SScott Long close(fd); 209763fae79SScott Long 210763fae79SScott Long return (0); 211763fae79SScott Long } 212763fae79SScott Long MFI_COMMAND(top, clear, clear_config); 213763fae79SScott Long 214763fae79SScott Long #define MFI_ARRAY_SIZE 288 215763fae79SScott Long #define MAX_DRIVES_PER_ARRAY \ 216763fae79SScott Long ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8) 217763fae79SScott Long 218763fae79SScott Long #define RT_RAID0 0 219763fae79SScott Long #define RT_RAID1 1 220763fae79SScott Long #define RT_RAID5 2 221763fae79SScott Long #define RT_RAID6 3 222763fae79SScott Long #define RT_JBOD 4 223763fae79SScott Long #define RT_CONCAT 5 224763fae79SScott Long #define RT_RAID10 6 225763fae79SScott Long #define RT_RAID50 7 226763fae79SScott Long #define RT_RAID60 8 227763fae79SScott Long 228763fae79SScott Long static int 229763fae79SScott Long compare_int(const void *one, const void *two) 230763fae79SScott Long { 231763fae79SScott Long int first, second; 232763fae79SScott Long 233763fae79SScott Long first = *(const int *)one; 234763fae79SScott Long second = *(const int *)two; 235763fae79SScott Long 236763fae79SScott Long return (first - second); 237763fae79SScott Long } 238763fae79SScott Long 239763fae79SScott Long static struct raid_type_entry { 240763fae79SScott Long const char *name; 241763fae79SScott Long int raid_type; 242763fae79SScott Long } raid_type_table[] = { 243763fae79SScott Long { "raid0", RT_RAID0 }, 244763fae79SScott Long { "raid-0", RT_RAID0 }, 245763fae79SScott Long { "raid1", RT_RAID1 }, 246763fae79SScott Long { "raid-1", RT_RAID1 }, 247763fae79SScott Long { "mirror", RT_RAID1 }, 248763fae79SScott Long { "raid5", RT_RAID5 }, 249763fae79SScott Long { "raid-5", RT_RAID5 }, 250763fae79SScott Long { "raid6", RT_RAID6 }, 251763fae79SScott Long { "raid-6", RT_RAID6 }, 252763fae79SScott Long { "jbod", RT_JBOD }, 253763fae79SScott Long { "concat", RT_CONCAT }, 254763fae79SScott Long { "raid10", RT_RAID10 }, 255763fae79SScott Long { "raid1+0", RT_RAID10 }, 256763fae79SScott Long { "raid-10", RT_RAID10 }, 257763fae79SScott Long { "raid-1+0", RT_RAID10 }, 258763fae79SScott Long { "raid50", RT_RAID50 }, 259763fae79SScott Long { "raid5+0", RT_RAID50 }, 260763fae79SScott Long { "raid-50", RT_RAID50 }, 261763fae79SScott Long { "raid-5+0", RT_RAID50 }, 262763fae79SScott Long { "raid60", RT_RAID60 }, 263763fae79SScott Long { "raid6+0", RT_RAID60 }, 264763fae79SScott Long { "raid-60", RT_RAID60 }, 265763fae79SScott Long { "raid-6+0", RT_RAID60 }, 266763fae79SScott Long { NULL, 0 }, 267763fae79SScott Long }; 268763fae79SScott Long 269763fae79SScott Long struct config_id_state { 270763fae79SScott Long int array_count; 271763fae79SScott Long int log_drv_count; 272763fae79SScott Long int *arrays; 273763fae79SScott Long int *volumes; 274763fae79SScott Long uint16_t array_ref; 275763fae79SScott Long uint8_t target_id; 276763fae79SScott Long }; 277763fae79SScott Long 278763fae79SScott Long struct array_info { 279763fae79SScott Long int drive_count; 280763fae79SScott Long struct mfi_pd_info *drives; 281763fae79SScott Long struct mfi_array *array; 282763fae79SScott Long }; 283763fae79SScott Long 284763fae79SScott Long /* Parse a comma-separated list of drives for an array. */ 285763fae79SScott Long static int 286763fae79SScott Long parse_array(int fd, int raid_type, char *array_str, struct array_info *info) 287763fae79SScott Long { 288763fae79SScott Long struct mfi_pd_info *pinfo; 289763fae79SScott Long uint16_t device_id; 290763fae79SScott Long char *cp; 291763fae79SScott Long u_int count; 292763fae79SScott Long int error; 293763fae79SScott Long 294763fae79SScott Long cp = array_str; 295763fae79SScott Long for (count = 0; cp != NULL; count++) { 296763fae79SScott Long cp = strchr(cp, ','); 297763fae79SScott Long if (cp != NULL) { 298763fae79SScott Long cp++; 299763fae79SScott Long if (*cp == ',') { 300763fae79SScott Long warnx("Invalid drive list '%s'", array_str); 301763fae79SScott Long return (EINVAL); 302763fae79SScott Long } 303763fae79SScott Long } 304763fae79SScott Long } 305763fae79SScott Long 306763fae79SScott Long /* Validate the number of drives for this array. */ 307763fae79SScott Long if (count >= MAX_DRIVES_PER_ARRAY) { 308763fae79SScott Long warnx("Too many drives for a single array: max is %zu", 309763fae79SScott Long MAX_DRIVES_PER_ARRAY); 310763fae79SScott Long return (EINVAL); 311763fae79SScott Long } 312763fae79SScott Long switch (raid_type) { 313763fae79SScott Long case RT_RAID1: 314763fae79SScott Long case RT_RAID10: 315763fae79SScott Long if (count % 2 != 0) { 316763fae79SScott Long warnx("RAID1 and RAID10 require an even number of " 317763fae79SScott Long "drives in each array"); 318763fae79SScott Long return (EINVAL); 319763fae79SScott Long } 320763fae79SScott Long break; 321763fae79SScott Long case RT_RAID5: 322763fae79SScott Long case RT_RAID50: 323763fae79SScott Long if (count < 3) { 324763fae79SScott Long warnx("RAID5 and RAID50 require at least 3 drives in " 325763fae79SScott Long "each array"); 326763fae79SScott Long return (EINVAL); 327763fae79SScott Long } 328763fae79SScott Long break; 329763fae79SScott Long case RT_RAID6: 330763fae79SScott Long case RT_RAID60: 331763fae79SScott Long if (count < 4) { 332763fae79SScott Long warnx("RAID6 and RAID60 require at least 4 drives in " 333763fae79SScott Long "each array"); 334763fae79SScott Long return (EINVAL); 335763fae79SScott Long } 336763fae79SScott Long break; 337763fae79SScott Long } 338763fae79SScott Long 339763fae79SScott Long /* Validate each drive. */ 340763fae79SScott Long info->drives = calloc(count, sizeof(struct mfi_pd_info)); 3413c22a809SJohn Baldwin if (info->drives == NULL) { 3423c22a809SJohn Baldwin warnx("malloc failed"); 3433c22a809SJohn Baldwin return (ENOMEM); 3443c22a809SJohn Baldwin } 345763fae79SScott Long info->drive_count = count; 346763fae79SScott Long for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 347763fae79SScott Long pinfo++) { 348763fae79SScott Long error = mfi_lookup_drive(fd, cp, &device_id); 349375c4656SBjoern A. Zeeb if (error) { 350375c4656SBjoern A. Zeeb free(info->drives); 351*f1e7af36SEd Maste info->drives = NULL; 352763fae79SScott Long return (error); 353375c4656SBjoern A. Zeeb } 354763fae79SScott Long 355763fae79SScott Long if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 356c02999d9SJohn Baldwin error = errno; 357763fae79SScott Long warn("Failed to fetch drive info for drive %s", cp); 358375c4656SBjoern A. Zeeb free(info->drives); 359*f1e7af36SEd Maste info->drives = NULL; 360c02999d9SJohn Baldwin return (error); 361763fae79SScott Long } 362763fae79SScott Long 363763fae79SScott Long if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 364763fae79SScott Long warnx("Drive %u is not available", device_id); 365375c4656SBjoern A. Zeeb free(info->drives); 366*f1e7af36SEd Maste info->drives = NULL; 367763fae79SScott Long return (EINVAL); 368763fae79SScott Long } 369763fae79SScott Long } 370763fae79SScott Long 371763fae79SScott Long return (0); 372763fae79SScott Long } 373763fae79SScott Long 374763fae79SScott Long /* 375763fae79SScott Long * Find the next free array ref assuming that 'array_ref' is the last 376763fae79SScott Long * one used. 'array_ref' should be 0xffff for the initial test. 377763fae79SScott Long */ 378763fae79SScott Long static uint16_t 379763fae79SScott Long find_next_array(struct config_id_state *state) 380763fae79SScott Long { 381763fae79SScott Long int i; 382763fae79SScott Long 383763fae79SScott Long /* Assume the current one is used. */ 384763fae79SScott Long state->array_ref++; 385763fae79SScott Long 386763fae79SScott Long /* Find the next free one. */ 387763fae79SScott Long for (i = 0; i < state->array_count; i++) 388763fae79SScott Long if (state->arrays[i] == state->array_ref) 389763fae79SScott Long state->array_ref++; 390763fae79SScott Long return (state->array_ref); 391763fae79SScott Long } 392763fae79SScott Long 393763fae79SScott Long /* 394763fae79SScott Long * Find the next free volume ID assuming that 'target_id' is the last 395763fae79SScott Long * one used. 'target_id' should be 0xff for the initial test. 396763fae79SScott Long */ 397763fae79SScott Long static uint8_t 398763fae79SScott Long find_next_volume(struct config_id_state *state) 399763fae79SScott Long { 400763fae79SScott Long int i; 401763fae79SScott Long 402763fae79SScott Long /* Assume the current one is used. */ 403763fae79SScott Long state->target_id++; 404763fae79SScott Long 405763fae79SScott Long /* Find the next free one. */ 406763fae79SScott Long for (i = 0; i < state->log_drv_count; i++) 407763fae79SScott Long if (state->volumes[i] == state->target_id) 408763fae79SScott Long state->target_id++; 409763fae79SScott Long return (state->target_id); 410763fae79SScott Long } 411763fae79SScott Long 412763fae79SScott Long /* Populate an array with drives. */ 413763fae79SScott Long static void 414763fae79SScott Long build_array(int fd, char *arrayp, struct array_info *array_info, 415763fae79SScott Long struct config_id_state *state, int verbose) 416763fae79SScott Long { 417763fae79SScott Long struct mfi_array *ar = (struct mfi_array *)arrayp; 418763fae79SScott Long int i; 419763fae79SScott Long 420763fae79SScott Long ar->size = array_info->drives[0].coerced_size; 421763fae79SScott Long ar->num_drives = array_info->drive_count; 422763fae79SScott Long ar->array_ref = find_next_array(state); 423763fae79SScott Long for (i = 0; i < array_info->drive_count; i++) { 424763fae79SScott Long if (verbose) 4257bbae305SBjoern A. Zeeb printf("Adding drive %s to array %u\n", 4267bbae305SBjoern A. Zeeb mfi_drive_name(NULL, 427763fae79SScott Long array_info->drives[i].ref.v.device_id, 4287bbae305SBjoern A. Zeeb MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS), 429763fae79SScott Long ar->array_ref); 430763fae79SScott Long if (ar->size > array_info->drives[i].coerced_size) 431763fae79SScott Long ar->size = array_info->drives[i].coerced_size; 432763fae79SScott Long ar->pd[i].ref = array_info->drives[i].ref; 433763fae79SScott Long ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 434763fae79SScott Long } 435763fae79SScott Long array_info->array = ar; 436763fae79SScott Long } 437763fae79SScott Long 438763fae79SScott Long /* 439763fae79SScott Long * Create a volume that spans one or more arrays. 440763fae79SScott Long */ 441763fae79SScott Long static void 442763fae79SScott Long build_volume(char *volumep, int narrays, struct array_info *arrays, 443763fae79SScott Long int raid_type, long stripe_size, struct config_id_state *state, int verbose) 444763fae79SScott Long { 445763fae79SScott Long struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 446763fae79SScott Long struct mfi_array *ar; 447763fae79SScott Long int i; 448763fae79SScott Long 449763fae79SScott Long /* properties */ 450763fae79SScott Long ld->properties.ld.v.target_id = find_next_volume(state); 451763fae79SScott Long ld->properties.ld.v.seq = 0; 452763fae79SScott Long ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 453763fae79SScott Long MR_LD_CACHE_WRITE_BACK; 454763fae79SScott Long ld->properties.access_policy = MFI_LD_ACCESS_RW; 455763fae79SScott Long ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 456763fae79SScott Long ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 457763fae79SScott Long MR_LD_CACHE_WRITE_BACK; 458763fae79SScott Long ld->properties.no_bgi = 0; 459763fae79SScott Long 460763fae79SScott Long /* params */ 461763fae79SScott Long switch (raid_type) { 462763fae79SScott Long case RT_RAID0: 463763fae79SScott Long case RT_JBOD: 464763fae79SScott Long ld->params.primary_raid_level = DDF_RAID0; 465763fae79SScott Long ld->params.raid_level_qualifier = 0; 466763fae79SScott Long ld->params.secondary_raid_level = 0; 467763fae79SScott Long break; 468763fae79SScott Long case RT_RAID1: 469763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1; 470763fae79SScott Long ld->params.raid_level_qualifier = 0; 471763fae79SScott Long ld->params.secondary_raid_level = 0; 472763fae79SScott Long break; 473763fae79SScott Long case RT_RAID5: 474763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5; 475763fae79SScott Long ld->params.raid_level_qualifier = 3; 476763fae79SScott Long ld->params.secondary_raid_level = 0; 477763fae79SScott Long break; 478763fae79SScott Long case RT_RAID6: 479763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6; 480763fae79SScott Long ld->params.raid_level_qualifier = 3; 481763fae79SScott Long ld->params.secondary_raid_level = 0; 482763fae79SScott Long break; 483763fae79SScott Long case RT_CONCAT: 484763fae79SScott Long ld->params.primary_raid_level = DDF_CONCAT; 485763fae79SScott Long ld->params.raid_level_qualifier = 0; 486763fae79SScott Long ld->params.secondary_raid_level = 0; 487763fae79SScott Long break; 488763fae79SScott Long case RT_RAID10: 489763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1; 490763fae79SScott Long ld->params.raid_level_qualifier = 0; 491763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 492763fae79SScott Long break; 493763fae79SScott Long case RT_RAID50: 494763fae79SScott Long /* 495763fae79SScott Long * XXX: This appears to work though the card's BIOS 496763fae79SScott Long * complains that the configuration is foreign. The 497763fae79SScott Long * BIOS setup does not allow for creation of RAID-50 498763fae79SScott Long * or RAID-60 arrays. The only nested array 499763fae79SScott Long * configuration it allows for is RAID-10. 500763fae79SScott Long */ 501763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5; 502763fae79SScott Long ld->params.raid_level_qualifier = 3; 503763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 504763fae79SScott Long break; 505763fae79SScott Long case RT_RAID60: 506763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6; 507763fae79SScott Long ld->params.raid_level_qualifier = 3; 508763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 509763fae79SScott Long break; 510763fae79SScott Long } 511763fae79SScott Long 512763fae79SScott Long /* 513763fae79SScott Long * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 514763fae79SScott Long * ffs() to simulate log2(stripe_size). 515763fae79SScott Long */ 516763fae79SScott Long ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 517763fae79SScott Long ld->params.num_drives = arrays[0].array->num_drives; 518763fae79SScott Long ld->params.span_depth = narrays; 519763fae79SScott Long ld->params.state = MFI_LD_STATE_OPTIMAL; 520763fae79SScott Long ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 521763fae79SScott Long ld->params.is_consistent = 0; 522763fae79SScott Long 523763fae79SScott Long /* spans */ 524763fae79SScott Long for (i = 0; i < narrays; i++) { 525763fae79SScott Long ar = arrays[i].array; 526763fae79SScott Long if (verbose) 527763fae79SScott Long printf("Adding array %u to volume %u\n", ar->array_ref, 528763fae79SScott Long ld->properties.ld.v.target_id); 529763fae79SScott Long ld->span[i].start_block = 0; 530763fae79SScott Long ld->span[i].num_blocks = ar->size; 531763fae79SScott Long ld->span[i].array_ref = ar->array_ref; 532763fae79SScott Long } 533763fae79SScott Long } 534763fae79SScott Long 535763fae79SScott Long static int 536763fae79SScott Long create_volume(int ac, char **av) 537763fae79SScott Long { 538763fae79SScott Long struct mfi_config_data *config; 539763fae79SScott Long struct mfi_array *ar; 540763fae79SScott Long struct mfi_ld_config *ld; 541763fae79SScott Long struct config_id_state state; 542763fae79SScott Long size_t config_size; 543763fae79SScott Long char *p, *cfg_arrays, *cfg_volumes; 544763fae79SScott Long int error, fd, i, raid_type; 545763fae79SScott Long int narrays, nvolumes, arrays_per_volume; 546763fae79SScott Long struct array_info *arrays; 547763fae79SScott Long long stripe_size; 548763fae79SScott Long #ifdef DEBUG 549763fae79SScott Long int dump; 550763fae79SScott Long #endif 551763fae79SScott Long int ch, verbose; 552763fae79SScott Long 553763fae79SScott Long /* 554763fae79SScott Long * Backwards compat. Map 'create volume' to 'create' and 555763fae79SScott Long * 'create spare' to 'add'. 556763fae79SScott Long */ 557763fae79SScott Long if (ac > 1) { 558763fae79SScott Long if (strcmp(av[1], "volume") == 0) { 559763fae79SScott Long av++; 560763fae79SScott Long ac--; 561763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) { 562763fae79SScott Long av++; 563763fae79SScott Long ac--; 564763fae79SScott Long return (add_spare(ac, av)); 565763fae79SScott Long } 566763fae79SScott Long } 567763fae79SScott Long 568763fae79SScott Long if (ac < 2) { 569763fae79SScott Long warnx("create volume: volume type required"); 570763fae79SScott Long return (EINVAL); 571763fae79SScott Long } 572763fae79SScott Long 573375c4656SBjoern A. Zeeb bzero(&state, sizeof(state)); 574375c4656SBjoern A. Zeeb config = NULL; 575375c4656SBjoern A. Zeeb arrays = NULL; 576375c4656SBjoern A. Zeeb narrays = 0; 577375c4656SBjoern A. Zeeb error = 0; 578763fae79SScott Long 579763fae79SScott Long fd = mfi_open(mfi_unit); 580763fae79SScott Long if (fd < 0) { 581c02999d9SJohn Baldwin error = errno; 582763fae79SScott Long warn("mfi_open"); 583c02999d9SJohn Baldwin return (error); 584763fae79SScott Long } 585763fae79SScott Long 586763fae79SScott Long if (!mfi_reconfig_supported()) { 587763fae79SScott Long warnx("The current mfi(4) driver does not support " 588763fae79SScott Long "configuration changes."); 589375c4656SBjoern A. Zeeb error = EOPNOTSUPP; 590375c4656SBjoern A. Zeeb goto error; 591763fae79SScott Long } 592763fae79SScott Long 593763fae79SScott Long /* Lookup the RAID type first. */ 594763fae79SScott Long raid_type = -1; 595763fae79SScott Long for (i = 0; raid_type_table[i].name != NULL; i++) 596763fae79SScott Long if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 597763fae79SScott Long raid_type = raid_type_table[i].raid_type; 598763fae79SScott Long break; 599763fae79SScott Long } 600763fae79SScott Long 601763fae79SScott Long if (raid_type == -1) { 602763fae79SScott Long warnx("Unknown or unsupported volume type %s", av[1]); 603375c4656SBjoern A. Zeeb error = EINVAL; 604375c4656SBjoern A. Zeeb goto error; 605763fae79SScott Long } 606763fae79SScott Long 607763fae79SScott Long /* Parse any options. */ 608763fae79SScott Long optind = 2; 609763fae79SScott Long #ifdef DEBUG 610763fae79SScott Long dump = 0; 611763fae79SScott Long #endif 612763fae79SScott Long verbose = 0; 613763fae79SScott Long stripe_size = 64 * 1024; 614763fae79SScott Long 615763fae79SScott Long while ((ch = getopt(ac, av, "ds:v")) != -1) { 616763fae79SScott Long switch (ch) { 617763fae79SScott Long #ifdef DEBUG 618763fae79SScott Long case 'd': 619763fae79SScott Long dump = 1; 620763fae79SScott Long break; 621763fae79SScott Long #endif 622763fae79SScott Long case 's': 623763fae79SScott Long stripe_size = dehumanize(optarg); 624763fae79SScott Long if ((stripe_size < 512) || (!powerof2(stripe_size))) 625763fae79SScott Long stripe_size = 64 * 1024; 626763fae79SScott Long break; 627763fae79SScott Long case 'v': 628763fae79SScott Long verbose = 1; 629763fae79SScott Long break; 630763fae79SScott Long case '?': 631763fae79SScott Long default: 632375c4656SBjoern A. Zeeb error = EINVAL; 633375c4656SBjoern A. Zeeb goto error; 634763fae79SScott Long } 635763fae79SScott Long } 636763fae79SScott Long ac -= optind; 637763fae79SScott Long av += optind; 638763fae79SScott Long 639763fae79SScott Long /* Parse all the arrays. */ 640763fae79SScott Long narrays = ac; 641763fae79SScott Long if (narrays == 0) { 642763fae79SScott Long warnx("At least one drive list is required"); 643375c4656SBjoern A. Zeeb error = EINVAL; 644375c4656SBjoern A. Zeeb goto error; 645763fae79SScott Long } 646763fae79SScott Long switch (raid_type) { 647763fae79SScott Long case RT_RAID0: 648763fae79SScott Long case RT_RAID1: 649763fae79SScott Long case RT_RAID5: 650763fae79SScott Long case RT_RAID6: 651763fae79SScott Long case RT_CONCAT: 652763fae79SScott Long if (narrays != 1) { 653763fae79SScott Long warnx("Only one drive list can be specified"); 654375c4656SBjoern A. Zeeb error = EINVAL; 655375c4656SBjoern A. Zeeb goto error; 656763fae79SScott Long } 657763fae79SScott Long break; 658763fae79SScott Long case RT_RAID10: 659763fae79SScott Long case RT_RAID50: 660763fae79SScott Long case RT_RAID60: 661763fae79SScott Long if (narrays < 1) { 662763fae79SScott Long warnx("RAID10, RAID50, and RAID60 require at least " 663763fae79SScott Long "two drive lists"); 664375c4656SBjoern A. Zeeb error = EINVAL; 665375c4656SBjoern A. Zeeb goto error; 666763fae79SScott Long } 667763fae79SScott Long if (narrays > MFI_MAX_SPAN_DEPTH) { 668763fae79SScott Long warnx("Volume spans more than %d arrays", 669763fae79SScott Long MFI_MAX_SPAN_DEPTH); 670375c4656SBjoern A. Zeeb error = EINVAL; 671375c4656SBjoern A. Zeeb goto error; 672763fae79SScott Long } 673763fae79SScott Long break; 674763fae79SScott Long } 675763fae79SScott Long arrays = calloc(narrays, sizeof(*arrays)); 6763c22a809SJohn Baldwin if (arrays == NULL) { 6773c22a809SJohn Baldwin warnx("malloc failed"); 678375c4656SBjoern A. Zeeb error = ENOMEM; 679375c4656SBjoern A. Zeeb goto error; 6803c22a809SJohn Baldwin } 681763fae79SScott Long for (i = 0; i < narrays; i++) { 682763fae79SScott Long error = parse_array(fd, raid_type, av[i], &arrays[i]); 683763fae79SScott Long if (error) 684375c4656SBjoern A. Zeeb goto error; 685763fae79SScott Long } 686763fae79SScott Long 687763fae79SScott Long switch (raid_type) { 688763fae79SScott Long case RT_RAID10: 689763fae79SScott Long case RT_RAID50: 690763fae79SScott Long case RT_RAID60: 691763fae79SScott Long for (i = 1; i < narrays; i++) { 692763fae79SScott Long if (arrays[i].drive_count != arrays[0].drive_count) { 693763fae79SScott Long warnx("All arrays must contain the same " 694763fae79SScott Long "number of drives"); 695375c4656SBjoern A. Zeeb error = EINVAL; 696375c4656SBjoern A. Zeeb goto error; 697763fae79SScott Long } 698763fae79SScott Long } 699763fae79SScott Long break; 700763fae79SScott Long } 701763fae79SScott Long 702763fae79SScott Long /* 703763fae79SScott Long * Fetch the current config and build sorted lists of existing 704763fae79SScott Long * array and volume identifiers. 705763fae79SScott Long */ 706763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 707c02999d9SJohn Baldwin error = errno; 708763fae79SScott Long warn("Failed to read configuration"); 709375c4656SBjoern A. Zeeb goto error; 710763fae79SScott Long } 711763fae79SScott Long p = (char *)config->array; 712763fae79SScott Long state.array_ref = 0xffff; 713763fae79SScott Long state.target_id = 0xff; 714763fae79SScott Long state.array_count = config->array_count; 715763fae79SScott Long if (config->array_count > 0) { 716763fae79SScott Long state.arrays = calloc(config->array_count, sizeof(int)); 7173c22a809SJohn Baldwin if (state.arrays == NULL) { 7183c22a809SJohn Baldwin warnx("malloc failed"); 719375c4656SBjoern A. Zeeb error = ENOMEM; 720375c4656SBjoern A. Zeeb goto error; 7213c22a809SJohn Baldwin } 722763fae79SScott Long for (i = 0; i < config->array_count; i++) { 723763fae79SScott Long ar = (struct mfi_array *)p; 724763fae79SScott Long state.arrays[i] = ar->array_ref; 725763fae79SScott Long p += config->array_size; 726763fae79SScott Long } 727763fae79SScott Long qsort(state.arrays, config->array_count, sizeof(int), 728763fae79SScott Long compare_int); 729763fae79SScott Long } else 730763fae79SScott Long state.arrays = NULL; 731763fae79SScott Long state.log_drv_count = config->log_drv_count; 732763fae79SScott Long if (config->log_drv_count) { 733763fae79SScott Long state.volumes = calloc(config->log_drv_count, sizeof(int)); 7343c22a809SJohn Baldwin if (state.volumes == NULL) { 7353c22a809SJohn Baldwin warnx("malloc failed"); 736375c4656SBjoern A. Zeeb error = ENOMEM; 737375c4656SBjoern A. Zeeb goto error; 7383c22a809SJohn Baldwin } 739763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 740763fae79SScott Long ld = (struct mfi_ld_config *)p; 741763fae79SScott Long state.volumes[i] = ld->properties.ld.v.target_id; 742763fae79SScott Long p += config->log_drv_size; 743763fae79SScott Long } 744763fae79SScott Long qsort(state.volumes, config->log_drv_count, sizeof(int), 745763fae79SScott Long compare_int); 746763fae79SScott Long } else 747763fae79SScott Long state.volumes = NULL; 748763fae79SScott Long free(config); 749763fae79SScott Long 750763fae79SScott Long /* Determine the size of the configuration we will build. */ 751763fae79SScott Long switch (raid_type) { 752763fae79SScott Long case RT_RAID0: 753763fae79SScott Long case RT_RAID1: 754763fae79SScott Long case RT_RAID5: 755763fae79SScott Long case RT_RAID6: 756763fae79SScott Long case RT_CONCAT: 757763fae79SScott Long case RT_JBOD: 758763fae79SScott Long /* Each volume spans a single array. */ 759763fae79SScott Long nvolumes = narrays; 760763fae79SScott Long break; 761763fae79SScott Long case RT_RAID10: 762763fae79SScott Long case RT_RAID50: 763763fae79SScott Long case RT_RAID60: 764763fae79SScott Long /* A single volume spans multiple arrays. */ 765763fae79SScott Long nvolumes = 1; 766763fae79SScott Long break; 767763fae79SScott Long default: 768763fae79SScott Long /* Pacify gcc. */ 769763fae79SScott Long abort(); 770763fae79SScott Long } 771763fae79SScott Long 772763fae79SScott Long config_size = sizeof(struct mfi_config_data) + 773763fae79SScott Long sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 774763fae79SScott Long config = calloc(1, config_size); 7753c22a809SJohn Baldwin if (config == NULL) { 7763c22a809SJohn Baldwin warnx("malloc failed"); 777375c4656SBjoern A. Zeeb error = ENOMEM; 778375c4656SBjoern A. Zeeb goto error; 7793c22a809SJohn Baldwin } 780763fae79SScott Long config->size = config_size; 781763fae79SScott Long config->array_count = narrays; 782763fae79SScott Long config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 783763fae79SScott Long config->log_drv_count = nvolumes; 784763fae79SScott Long config->log_drv_size = sizeof(struct mfi_ld_config); 785763fae79SScott Long config->spares_count = 0; 786763fae79SScott Long config->spares_size = 40; /* XXX: Firmware hardcode */ 787763fae79SScott Long cfg_arrays = (char *)config->array; 788763fae79SScott Long cfg_volumes = cfg_arrays + config->array_size * narrays; 789763fae79SScott Long 790763fae79SScott Long /* Build the arrays. */ 791763fae79SScott Long for (i = 0; i < narrays; i++) { 792763fae79SScott Long build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 793763fae79SScott Long cfg_arrays += config->array_size; 794763fae79SScott Long } 795763fae79SScott Long 796763fae79SScott Long /* Now build the volume(s). */ 797763fae79SScott Long arrays_per_volume = narrays / nvolumes; 798763fae79SScott Long for (i = 0; i < nvolumes; i++) { 799763fae79SScott Long build_volume(cfg_volumes, arrays_per_volume, 800763fae79SScott Long &arrays[i * arrays_per_volume], raid_type, stripe_size, 801763fae79SScott Long &state, verbose); 802763fae79SScott Long cfg_volumes += config->log_drv_size; 803763fae79SScott Long } 804763fae79SScott Long 805763fae79SScott Long #ifdef DEBUG 806763fae79SScott Long if (dump) 807763fae79SScott Long dump_config(fd, config); 808763fae79SScott Long #endif 809763fae79SScott Long 810763fae79SScott Long /* Send the new config to the controller. */ 811763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 812763fae79SScott Long NULL, 0, NULL) < 0) { 813c02999d9SJohn Baldwin error = errno; 814763fae79SScott Long warn("Failed to add volume"); 815375c4656SBjoern A. Zeeb /* FALLTHROUGH */ 816763fae79SScott Long } 817763fae79SScott Long 818375c4656SBjoern A. Zeeb error: 819763fae79SScott Long /* Clean up. */ 820763fae79SScott Long free(config); 821763fae79SScott Long free(state.volumes); 822763fae79SScott Long free(state.arrays); 823763fae79SScott Long for (i = 0; i < narrays; i++) 824763fae79SScott Long free(arrays[i].drives); 825763fae79SScott Long free(arrays); 826763fae79SScott Long close(fd); 827763fae79SScott Long 828375c4656SBjoern A. Zeeb return (error); 829763fae79SScott Long } 830763fae79SScott Long MFI_COMMAND(top, create, create_volume); 831763fae79SScott Long 832763fae79SScott Long static int 833763fae79SScott Long delete_volume(int ac, char **av) 834763fae79SScott Long { 835763fae79SScott Long struct mfi_ld_info info; 836c02999d9SJohn Baldwin int error, fd; 837763fae79SScott Long uint8_t target_id, mbox[4]; 838763fae79SScott Long 839763fae79SScott Long /* 840763fae79SScott Long * Backwards compat. Map 'delete volume' to 'delete' and 841763fae79SScott Long * 'delete spare' to 'remove'. 842763fae79SScott Long */ 843763fae79SScott Long if (ac > 1) { 844763fae79SScott Long if (strcmp(av[1], "volume") == 0) { 845763fae79SScott Long av++; 846763fae79SScott Long ac--; 847763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) { 848763fae79SScott Long av++; 849763fae79SScott Long ac--; 850763fae79SScott Long return (remove_spare(ac, av)); 851763fae79SScott Long } 852763fae79SScott Long } 853763fae79SScott Long 854763fae79SScott Long if (ac != 2) { 855763fae79SScott Long warnx("delete volume: volume required"); 856763fae79SScott Long return (EINVAL); 857763fae79SScott Long } 858763fae79SScott Long 859763fae79SScott Long fd = mfi_open(mfi_unit); 860763fae79SScott Long if (fd < 0) { 861c02999d9SJohn Baldwin error = errno; 862763fae79SScott Long warn("mfi_open"); 863c02999d9SJohn Baldwin return (error); 864763fae79SScott Long } 865763fae79SScott Long 866763fae79SScott Long if (!mfi_reconfig_supported()) { 867763fae79SScott Long warnx("The current mfi(4) driver does not support " 868763fae79SScott Long "configuration changes."); 869375c4656SBjoern A. Zeeb close(fd); 870763fae79SScott Long return (EOPNOTSUPP); 871763fae79SScott Long } 872763fae79SScott Long 873763fae79SScott Long if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 874c02999d9SJohn Baldwin error = errno; 875763fae79SScott Long warn("Invalid volume %s", av[1]); 876375c4656SBjoern A. Zeeb close(fd); 877c02999d9SJohn Baldwin return (error); 878763fae79SScott Long } 879763fae79SScott Long 880763fae79SScott Long if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 881c02999d9SJohn Baldwin error = errno; 882763fae79SScott Long warn("Failed to get info for volume %d", target_id); 883375c4656SBjoern A. Zeeb close(fd); 884c02999d9SJohn Baldwin return (error); 885763fae79SScott Long } 886763fae79SScott Long 887763fae79SScott Long if (mfi_volume_busy(fd, target_id)) { 888763fae79SScott Long warnx("Volume %s is busy and cannot be deleted", 889763fae79SScott Long mfi_volume_name(fd, target_id)); 890375c4656SBjoern A. Zeeb close(fd); 891763fae79SScott Long return (EBUSY); 892763fae79SScott Long } 893763fae79SScott Long 894763fae79SScott Long mbox_store_ldref(mbox, &info.ld_config.properties.ld); 895763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 896763fae79SScott Long sizeof(mbox), NULL) < 0) { 897c02999d9SJohn Baldwin error = errno; 898763fae79SScott Long warn("Failed to delete volume"); 899375c4656SBjoern A. Zeeb close(fd); 900c02999d9SJohn Baldwin return (error); 901763fae79SScott Long } 902763fae79SScott Long 903763fae79SScott Long close(fd); 904763fae79SScott Long 905763fae79SScott Long return (0); 906763fae79SScott Long } 907763fae79SScott Long MFI_COMMAND(top, delete, delete_volume); 908763fae79SScott Long 909763fae79SScott Long static int 910763fae79SScott Long add_spare(int ac, char **av) 911763fae79SScott Long { 912763fae79SScott Long struct mfi_pd_info info; 913763fae79SScott Long struct mfi_config_data *config; 914763fae79SScott Long struct mfi_array *ar; 915763fae79SScott Long struct mfi_ld_config *ld; 916763fae79SScott Long struct mfi_spare *spare; 917763fae79SScott Long uint16_t device_id; 918763fae79SScott Long uint8_t target_id; 919763fae79SScott Long char *p; 920763fae79SScott Long int error, fd, i; 921763fae79SScott Long 922763fae79SScott Long if (ac < 2) { 923763fae79SScott Long warnx("add spare: drive required"); 924763fae79SScott Long return (EINVAL); 925763fae79SScott Long } 926763fae79SScott Long 927763fae79SScott Long fd = mfi_open(mfi_unit); 928763fae79SScott Long if (fd < 0) { 929c02999d9SJohn Baldwin error = errno; 930763fae79SScott Long warn("mfi_open"); 931c02999d9SJohn Baldwin return (error); 932763fae79SScott Long } 933763fae79SScott Long 934375c4656SBjoern A. Zeeb config = NULL; 935375c4656SBjoern A. Zeeb spare = NULL; 936763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id); 937763fae79SScott Long if (error) 938375c4656SBjoern A. Zeeb goto error; 939763fae79SScott Long 940763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 941c02999d9SJohn Baldwin error = errno; 942763fae79SScott Long warn("Failed to fetch drive info"); 943375c4656SBjoern A. Zeeb goto error; 944763fae79SScott Long } 945763fae79SScott Long 946763fae79SScott Long if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 947763fae79SScott Long warnx("Drive %u is not available", device_id); 948375c4656SBjoern A. Zeeb error = EINVAL; 949375c4656SBjoern A. Zeeb goto error; 950763fae79SScott Long } 951763fae79SScott Long 952763fae79SScott Long if (ac > 2) { 953763fae79SScott Long if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 954c02999d9SJohn Baldwin error = errno; 955763fae79SScott Long warn("Invalid volume %s", av[2]); 956375c4656SBjoern A. Zeeb goto error; 957763fae79SScott Long } 958763fae79SScott Long } 959763fae79SScott Long 960763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 961c02999d9SJohn Baldwin error = errno; 962763fae79SScott Long warn("Failed to read configuration"); 963375c4656SBjoern A. Zeeb goto error; 964763fae79SScott Long } 965763fae79SScott Long 966763fae79SScott Long spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 967763fae79SScott Long config->array_count); 9683c22a809SJohn Baldwin if (spare == NULL) { 9693c22a809SJohn Baldwin warnx("malloc failed"); 970375c4656SBjoern A. Zeeb error = ENOMEM; 971375c4656SBjoern A. Zeeb goto error; 9723c22a809SJohn Baldwin } 973763fae79SScott Long bzero(spare, sizeof(struct mfi_spare)); 974763fae79SScott Long spare->ref = info.ref; 975763fae79SScott Long 976763fae79SScott Long if (ac == 2) { 977763fae79SScott Long /* Global spare backs all arrays. */ 978763fae79SScott Long p = (char *)config->array; 979763fae79SScott Long for (i = 0; i < config->array_count; i++) { 980763fae79SScott Long ar = (struct mfi_array *)p; 981763fae79SScott Long if (ar->size > info.coerced_size) { 982763fae79SScott Long warnx("Spare isn't large enough for array %u", 983763fae79SScott Long ar->array_ref); 984375c4656SBjoern A. Zeeb error = EINVAL; 985375c4656SBjoern A. Zeeb goto error; 986763fae79SScott Long } 987763fae79SScott Long p += config->array_size; 988763fae79SScott Long } 989763fae79SScott Long spare->array_count = 0; 990763fae79SScott Long } else { 991763fae79SScott Long /* 992763fae79SScott Long * Dedicated spares only back the arrays for a 993763fae79SScott Long * specific volume. 994763fae79SScott Long */ 995763fae79SScott Long ld = mfi_config_lookup_volume(config, target_id); 996763fae79SScott Long if (ld == NULL) { 997763fae79SScott Long warnx("Did not find volume %d", target_id); 998375c4656SBjoern A. Zeeb error = EINVAL; 999375c4656SBjoern A. Zeeb goto error; 1000763fae79SScott Long } 1001763fae79SScott Long 1002763fae79SScott Long spare->spare_type |= MFI_SPARE_DEDICATED; 1003763fae79SScott Long spare->array_count = ld->params.span_depth; 1004763fae79SScott Long for (i = 0; i < ld->params.span_depth; i++) { 1005763fae79SScott Long ar = mfi_config_lookup_array(config, 1006763fae79SScott Long ld->span[i].array_ref); 1007763fae79SScott Long if (ar == NULL) { 1008763fae79SScott Long warnx("Missing array; inconsistent config?"); 1009375c4656SBjoern A. Zeeb error = ENXIO; 1010375c4656SBjoern A. Zeeb goto error; 1011763fae79SScott Long } 1012763fae79SScott Long if (ar->size > info.coerced_size) { 1013763fae79SScott Long warnx("Spare isn't large enough for array %u", 1014763fae79SScott Long ar->array_ref); 1015375c4656SBjoern A. Zeeb error = EINVAL; 1016375c4656SBjoern A. Zeeb goto error; 1017763fae79SScott Long } 1018763fae79SScott Long spare->array_ref[i] = ar->array_ref; 1019763fae79SScott Long } 1020763fae79SScott Long } 1021763fae79SScott Long 1022763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 1023763fae79SScott Long sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 1024763fae79SScott Long NULL, 0, NULL) < 0) { 1025c02999d9SJohn Baldwin error = errno; 1026763fae79SScott Long warn("Failed to assign spare"); 1027375c4656SBjoern A. Zeeb /* FALLTHROUGH. */ 1028763fae79SScott Long } 1029763fae79SScott Long 1030375c4656SBjoern A. Zeeb error: 1031375c4656SBjoern A. Zeeb free(spare); 1032375c4656SBjoern A. Zeeb free(config); 1033763fae79SScott Long close(fd); 1034763fae79SScott Long 1035375c4656SBjoern A. Zeeb return (error); 1036763fae79SScott Long } 1037763fae79SScott Long MFI_COMMAND(top, add, add_spare); 1038763fae79SScott Long 1039763fae79SScott Long static int 1040763fae79SScott Long remove_spare(int ac, char **av) 1041763fae79SScott Long { 1042763fae79SScott Long struct mfi_pd_info info; 1043763fae79SScott Long int error, fd; 1044763fae79SScott Long uint16_t device_id; 1045763fae79SScott Long uint8_t mbox[4]; 1046763fae79SScott Long 1047763fae79SScott Long if (ac != 2) { 1048763fae79SScott Long warnx("remove spare: drive required"); 1049763fae79SScott Long return (EINVAL); 1050763fae79SScott Long } 1051763fae79SScott Long 1052763fae79SScott Long fd = mfi_open(mfi_unit); 1053763fae79SScott Long if (fd < 0) { 1054c02999d9SJohn Baldwin error = errno; 1055763fae79SScott Long warn("mfi_open"); 1056c02999d9SJohn Baldwin return (error); 1057763fae79SScott Long } 1058763fae79SScott Long 1059763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id); 1060375c4656SBjoern A. Zeeb if (error) { 1061375c4656SBjoern A. Zeeb close(fd); 1062763fae79SScott Long return (error); 1063375c4656SBjoern A. Zeeb } 1064763fae79SScott Long 1065763fae79SScott Long /* Get the info for this drive. */ 1066763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 1067c02999d9SJohn Baldwin error = errno; 1068763fae79SScott Long warn("Failed to fetch info for drive %u", device_id); 1069375c4656SBjoern A. Zeeb close(fd); 1070c02999d9SJohn Baldwin return (error); 1071763fae79SScott Long } 1072763fae79SScott Long 1073763fae79SScott Long if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 1074763fae79SScott Long warnx("Drive %u is not a hot spare", device_id); 1075375c4656SBjoern A. Zeeb close(fd); 1076763fae79SScott Long return (EINVAL); 1077763fae79SScott Long } 1078763fae79SScott Long 1079763fae79SScott Long mbox_store_pdref(mbox, &info.ref); 1080763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 1081763fae79SScott Long sizeof(mbox), NULL) < 0) { 1082c02999d9SJohn Baldwin error = errno; 1083763fae79SScott Long warn("Failed to delete spare"); 1084375c4656SBjoern A. Zeeb close(fd); 1085c02999d9SJohn Baldwin return (error); 1086763fae79SScott Long } 1087763fae79SScott Long 1088763fae79SScott Long close(fd); 1089763fae79SScott Long 1090763fae79SScott Long return (0); 1091763fae79SScott Long } 1092763fae79SScott Long MFI_COMMAND(top, remove, remove_spare); 1093763fae79SScott Long 1094763fae79SScott Long #ifdef DEBUG 1095763fae79SScott Long /* Display raw data about a config. */ 1096763fae79SScott Long static void 1097763fae79SScott Long dump_config(int fd, struct mfi_config_data *config) 1098763fae79SScott Long { 1099763fae79SScott Long struct mfi_array *ar; 1100763fae79SScott Long struct mfi_ld_config *ld; 1101763fae79SScott Long struct mfi_spare *sp; 1102763fae79SScott Long struct mfi_pd_info pinfo; 1103763fae79SScott Long uint16_t device_id; 1104763fae79SScott Long char *p; 1105763fae79SScott Long int i, j; 1106763fae79SScott Long 1107763fae79SScott Long printf( 1108763fae79SScott Long "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n", 1109763fae79SScott Long mfi_unit, config->array_count, config->log_drv_count, 1110763fae79SScott Long config->spares_count); 1111763fae79SScott Long printf(" array size: %u\n", config->array_size); 1112763fae79SScott Long printf(" volume size: %u\n", config->log_drv_size); 1113763fae79SScott Long printf(" spare size: %u\n", config->spares_size); 1114763fae79SScott Long p = (char *)config->array; 1115763fae79SScott Long 1116763fae79SScott Long for (i = 0; i < config->array_count; i++) { 1117763fae79SScott Long ar = (struct mfi_array *)p; 1118763fae79SScott Long printf(" array %u of %u drives:\n", ar->array_ref, 1119763fae79SScott Long ar->num_drives); 1120763fae79SScott Long printf(" size = %ju\n", (uintmax_t)ar->size); 1121763fae79SScott Long for (j = 0; j < ar->num_drives; j++) { 11220c019d8cSRandi Harper device_id = ar->pd[j].ref.v.device_id; 1123763fae79SScott Long if (device_id == 0xffff) 1124763fae79SScott Long printf(" drive MISSING\n"); 1125763fae79SScott Long else { 1126763fae79SScott Long printf(" drive %u %s\n", device_id, 1127763fae79SScott Long mfi_pdstate(ar->pd[j].fw_state)); 1128763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &pinfo, 1129763fae79SScott Long NULL) >= 0) { 1130763fae79SScott Long printf(" raw size: %ju\n", 1131763fae79SScott Long (uintmax_t)pinfo.raw_size); 1132763fae79SScott Long printf(" non-coerced size: %ju\n", 1133763fae79SScott Long (uintmax_t)pinfo.non_coerced_size); 1134763fae79SScott Long printf(" coerced size: %ju\n", 1135763fae79SScott Long (uintmax_t)pinfo.coerced_size); 1136763fae79SScott Long } 1137763fae79SScott Long } 1138763fae79SScott Long } 1139763fae79SScott Long p += config->array_size; 1140763fae79SScott Long } 1141763fae79SScott Long 1142763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 1143763fae79SScott Long ld = (struct mfi_ld_config *)p; 1144763fae79SScott Long printf(" volume %s ", 1145763fae79SScott Long mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1146763fae79SScott Long printf("%s %s", 1147763fae79SScott Long mfi_raid_level(ld->params.primary_raid_level, 1148763fae79SScott Long ld->params.secondary_raid_level), 1149763fae79SScott Long mfi_ldstate(ld->params.state)); 1150763fae79SScott Long if (ld->properties.name[0] != '\0') 1151763fae79SScott Long printf(" <%s>", ld->properties.name); 1152763fae79SScott Long printf("\n"); 1153763fae79SScott Long printf(" primary raid level: %u\n", 1154763fae79SScott Long ld->params.primary_raid_level); 1155763fae79SScott Long printf(" raid level qualifier: %u\n", 1156763fae79SScott Long ld->params.raid_level_qualifier); 1157763fae79SScott Long printf(" secondary raid level: %u\n", 1158763fae79SScott Long ld->params.secondary_raid_level); 1159763fae79SScott Long printf(" stripe size: %u\n", ld->params.stripe_size); 1160763fae79SScott Long printf(" num drives: %u\n", ld->params.num_drives); 1161763fae79SScott Long printf(" init state: %u\n", ld->params.init_state); 1162763fae79SScott Long printf(" consistent: %u\n", ld->params.is_consistent); 1163763fae79SScott Long printf(" no bgi: %u\n", ld->properties.no_bgi); 1164763fae79SScott Long printf(" spans:\n"); 1165763fae79SScott Long for (j = 0; j < ld->params.span_depth; j++) { 1166763fae79SScott Long printf(" array %u @ ", ld->span[j].array_ref); 1167763fae79SScott Long printf("%ju : %ju\n", 1168763fae79SScott Long (uintmax_t)ld->span[j].start_block, 1169763fae79SScott Long (uintmax_t)ld->span[j].num_blocks); 1170763fae79SScott Long } 1171763fae79SScott Long p += config->log_drv_size; 1172763fae79SScott Long } 1173763fae79SScott Long 1174763fae79SScott Long for (i = 0; i < config->spares_count; i++) { 1175763fae79SScott Long sp = (struct mfi_spare *)p; 1176763fae79SScott Long printf(" %s spare %u ", 1177763fae79SScott Long sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 11780c019d8cSRandi Harper "global", sp->ref.v.device_id); 1179763fae79SScott Long printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1180763fae79SScott Long printf(" backs:\n"); 1181763fae79SScott Long for (j = 0; j < sp->array_count; j++) 1182763fae79SScott Long printf(" array %u\n", sp->array_ref[j]); 1183763fae79SScott Long p += config->spares_size; 1184763fae79SScott Long } 1185763fae79SScott Long } 1186763fae79SScott Long 1187763fae79SScott Long static int 1188763fae79SScott Long debug_config(int ac, char **av) 1189763fae79SScott Long { 1190763fae79SScott Long struct mfi_config_data *config; 1191c02999d9SJohn Baldwin int error, fd; 1192763fae79SScott Long 1193763fae79SScott Long if (ac != 1) { 1194763fae79SScott Long warnx("debug: extra arguments"); 1195763fae79SScott Long return (EINVAL); 1196763fae79SScott Long } 1197763fae79SScott Long 1198763fae79SScott Long fd = mfi_open(mfi_unit); 1199763fae79SScott Long if (fd < 0) { 1200c02999d9SJohn Baldwin error = errno; 1201763fae79SScott Long warn("mfi_open"); 1202c02999d9SJohn Baldwin return (error); 1203763fae79SScott Long } 1204763fae79SScott Long 1205763fae79SScott Long /* Get the config from the controller. */ 1206763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 1207c02999d9SJohn Baldwin error = errno; 1208763fae79SScott Long warn("Failed to get config"); 1209375c4656SBjoern A. Zeeb close(fd); 1210c02999d9SJohn Baldwin return (error); 1211763fae79SScott Long } 1212763fae79SScott Long 1213763fae79SScott Long /* Dump out the configuration. */ 1214763fae79SScott Long dump_config(fd, config); 1215763fae79SScott Long free(config); 1216763fae79SScott Long close(fd); 1217763fae79SScott Long 1218763fae79SScott Long return (0); 1219763fae79SScott Long } 1220763fae79SScott Long MFI_COMMAND(top, debug, debug_config); 1221763fae79SScott Long 1222763fae79SScott Long static int 1223763fae79SScott Long dump(int ac, char **av) 1224763fae79SScott Long { 1225763fae79SScott Long struct mfi_config_data *config; 1226763fae79SScott Long char buf[64]; 1227763fae79SScott Long size_t len; 1228c02999d9SJohn Baldwin int error, fd; 1229763fae79SScott Long 1230763fae79SScott Long if (ac != 1) { 1231763fae79SScott Long warnx("dump: extra arguments"); 1232763fae79SScott Long return (EINVAL); 1233763fae79SScott Long } 1234763fae79SScott Long 1235763fae79SScott Long fd = mfi_open(mfi_unit); 1236763fae79SScott Long if (fd < 0) { 1237c02999d9SJohn Baldwin error = errno; 1238763fae79SScott Long warn("mfi_open"); 1239c02999d9SJohn Baldwin return (error); 1240763fae79SScott Long } 1241763fae79SScott Long 1242763fae79SScott Long /* Get the stashed copy of the last dcmd from the driver. */ 1243763fae79SScott Long snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1244763fae79SScott Long if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1245c02999d9SJohn Baldwin error = errno; 1246763fae79SScott Long warn("Failed to read debug command"); 1247c02999d9SJohn Baldwin if (error == ENOENT) 1248c02999d9SJohn Baldwin error = EOPNOTSUPP; 1249375c4656SBjoern A. Zeeb close(fd); 1250c02999d9SJohn Baldwin return (error); 1251763fae79SScott Long } 1252763fae79SScott Long 1253763fae79SScott Long config = malloc(len); 12543c22a809SJohn Baldwin if (config == NULL) { 12553c22a809SJohn Baldwin warnx("malloc failed"); 1256375c4656SBjoern A. Zeeb close(fd); 12573c22a809SJohn Baldwin return (ENOMEM); 12583c22a809SJohn Baldwin } 1259763fae79SScott Long if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1260c02999d9SJohn Baldwin error = errno; 1261763fae79SScott Long warn("Failed to read debug command"); 1262375c4656SBjoern A. Zeeb free(config); 1263375c4656SBjoern A. Zeeb close(fd); 1264c02999d9SJohn Baldwin return (error); 1265763fae79SScott Long } 1266763fae79SScott Long dump_config(fd, config); 1267763fae79SScott Long free(config); 1268763fae79SScott Long close(fd); 1269763fae79SScott Long 1270763fae79SScott Long return (0); 1271763fae79SScott Long } 1272763fae79SScott Long MFI_COMMAND(top, dump, dump); 1273763fae79SScott Long #endif 1274