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 32763fae79SScott Long #include <sys/types.h> 33763fae79SScott Long #ifdef DEBUG 34763fae79SScott Long #include <sys/sysctl.h> 35763fae79SScott Long #endif 36763fae79SScott Long #include <sys/errno.h> 37763fae79SScott Long #include <err.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 #define powerof2(x) ((((x)-1)&(x))==0) 56763fae79SScott Long 57763fae79SScott Long static long 58763fae79SScott Long dehumanize(const char *value) 59763fae79SScott Long { 60763fae79SScott Long char *vtp; 61763fae79SScott Long long iv; 62763fae79SScott Long 63763fae79SScott Long if (value == NULL) 64763fae79SScott Long return (0); 65763fae79SScott Long iv = strtoq(value, &vtp, 0); 66763fae79SScott Long if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 67763fae79SScott Long return (0); 68763fae79SScott Long } 69763fae79SScott Long switch (vtp[0]) { 70763fae79SScott Long case 't': case 'T': 71763fae79SScott Long iv *= 1024; 72763fae79SScott Long case 'g': case 'G': 73763fae79SScott Long iv *= 1024; 74763fae79SScott Long case 'm': case 'M': 75763fae79SScott Long iv *= 1024; 76763fae79SScott Long case 'k': case 'K': 77763fae79SScott Long iv *= 1024; 78763fae79SScott Long case '\0': 79763fae79SScott Long break; 80763fae79SScott Long default: 81763fae79SScott Long return (0); 82763fae79SScott Long } 83763fae79SScott Long return (iv); 84763fae79SScott Long } 85763fae79SScott Long int 86763fae79SScott Long mfi_config_read(int fd, struct mfi_config_data **configp) 87763fae79SScott Long { 88763fae79SScott Long struct mfi_config_data *config; 89763fae79SScott Long uint32_t config_size; 90763fae79SScott Long 91763fae79SScott Long /* 92763fae79SScott Long * Keep fetching the config in a loop until we have a large enough 93763fae79SScott Long * buffer to hold the entire configuration. 94763fae79SScott Long */ 95763fae79SScott Long config = NULL; 96763fae79SScott Long config_size = 1024; 97763fae79SScott Long fetch: 98763fae79SScott Long config = reallocf(config, config_size); 99763fae79SScott Long if (config == NULL) 100763fae79SScott Long return (-1); 101763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config, 102763fae79SScott Long config_size, NULL, 0, NULL) < 0) 103763fae79SScott Long return (-1); 104763fae79SScott Long 105763fae79SScott Long if (config->size > config_size) { 106763fae79SScott Long config_size = config->size; 107763fae79SScott Long goto fetch; 108763fae79SScott Long } 109763fae79SScott Long 110763fae79SScott Long *configp = config; 111763fae79SScott Long return (0); 112763fae79SScott Long } 113763fae79SScott Long 114763fae79SScott Long static struct mfi_array * 115763fae79SScott Long mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 116763fae79SScott Long { 117763fae79SScott Long struct mfi_array *ar; 118763fae79SScott Long char *p; 119763fae79SScott Long int i; 120763fae79SScott Long 121763fae79SScott Long p = (char *)config->array; 122763fae79SScott Long for (i = 0; i < config->array_count; i++) { 123763fae79SScott Long ar = (struct mfi_array *)p; 124763fae79SScott Long if (ar->array_ref == array_ref) 125763fae79SScott Long return (ar); 126763fae79SScott Long p += config->array_size; 127763fae79SScott Long } 128763fae79SScott Long 129763fae79SScott Long return (NULL); 130763fae79SScott Long } 131763fae79SScott Long 132763fae79SScott Long static struct mfi_ld_config * 133763fae79SScott Long mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 134763fae79SScott Long { 135763fae79SScott Long struct mfi_ld_config *ld; 136763fae79SScott Long char *p; 137763fae79SScott Long int i; 138763fae79SScott Long 139763fae79SScott Long p = (char *)config->array + config->array_count * config->array_size; 140763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 141763fae79SScott Long ld = (struct mfi_ld_config *)p; 142763fae79SScott Long if (ld->properties.ld.v.target_id == target_id) 143763fae79SScott Long return (ld); 144763fae79SScott Long p += config->log_drv_size; 145763fae79SScott Long } 146763fae79SScott Long 147763fae79SScott Long return (NULL); 148763fae79SScott Long } 149763fae79SScott Long 150763fae79SScott Long static int 151763fae79SScott Long clear_config(int ac, char **av) 152763fae79SScott Long { 153763fae79SScott Long struct mfi_ld_list list; 154763fae79SScott Long int ch, fd; 155763fae79SScott Long u_int i; 156763fae79SScott Long 157763fae79SScott Long fd = mfi_open(mfi_unit); 158763fae79SScott Long if (fd < 0) { 159763fae79SScott Long warn("mfi_open"); 160763fae79SScott Long return (errno); 161763fae79SScott Long } 162763fae79SScott Long 163763fae79SScott Long if (!mfi_reconfig_supported()) { 164763fae79SScott Long warnx("The current mfi(4) driver does not support " 165763fae79SScott Long "configuration changes."); 166763fae79SScott Long return (EOPNOTSUPP); 167763fae79SScott Long } 168763fae79SScott Long 169763fae79SScott Long if (mfi_ld_get_list(fd, &list, NULL) < 0) { 170763fae79SScott Long warn("Failed to get volume list"); 171763fae79SScott Long return (errno); 172763fae79SScott Long } 173763fae79SScott Long 174763fae79SScott Long for (i = 0; i < list.ld_count; i++) { 175763fae79SScott Long if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 176763fae79SScott Long warnx("Volume %s is busy and cannot be deleted", 177763fae79SScott Long mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 178763fae79SScott Long return (EBUSY); 179763fae79SScott Long } 180763fae79SScott Long } 181763fae79SScott Long 182763fae79SScott Long printf( 183763fae79SScott Long "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 184763fae79SScott Long mfi_unit); 185763fae79SScott Long ch = getchar(); 186763fae79SScott Long if (ch != 'y' && ch != 'Y') { 187763fae79SScott Long printf("\nAborting\n"); 188763fae79SScott Long return (0); 189763fae79SScott Long } 190763fae79SScott Long 191763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 192763fae79SScott Long warn("Failed to clear configuration"); 193763fae79SScott Long return (errno); 194763fae79SScott Long } 195763fae79SScott Long 196763fae79SScott Long printf("mfi%d: Configuration cleared\n", mfi_unit); 197763fae79SScott Long close(fd); 198763fae79SScott Long 199763fae79SScott Long return (0); 200763fae79SScott Long } 201763fae79SScott Long MFI_COMMAND(top, clear, clear_config); 202763fae79SScott Long 203763fae79SScott Long #define MFI_ARRAY_SIZE 288 204763fae79SScott Long #define MAX_DRIVES_PER_ARRAY \ 205763fae79SScott Long ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8) 206763fae79SScott Long 207763fae79SScott Long #define RT_RAID0 0 208763fae79SScott Long #define RT_RAID1 1 209763fae79SScott Long #define RT_RAID5 2 210763fae79SScott Long #define RT_RAID6 3 211763fae79SScott Long #define RT_JBOD 4 212763fae79SScott Long #define RT_CONCAT 5 213763fae79SScott Long #define RT_RAID10 6 214763fae79SScott Long #define RT_RAID50 7 215763fae79SScott Long #define RT_RAID60 8 216763fae79SScott Long 217763fae79SScott Long static int 218763fae79SScott Long compare_int(const void *one, const void *two) 219763fae79SScott Long { 220763fae79SScott Long int first, second; 221763fae79SScott Long 222763fae79SScott Long first = *(const int *)one; 223763fae79SScott Long second = *(const int *)two; 224763fae79SScott Long 225763fae79SScott Long return (first - second); 226763fae79SScott Long } 227763fae79SScott Long 228763fae79SScott Long static struct raid_type_entry { 229763fae79SScott Long const char *name; 230763fae79SScott Long int raid_type; 231763fae79SScott Long } raid_type_table[] = { 232763fae79SScott Long { "raid0", RT_RAID0 }, 233763fae79SScott Long { "raid-0", RT_RAID0 }, 234763fae79SScott Long { "raid1", RT_RAID1 }, 235763fae79SScott Long { "raid-1", RT_RAID1 }, 236763fae79SScott Long { "mirror", RT_RAID1 }, 237763fae79SScott Long { "raid5", RT_RAID5 }, 238763fae79SScott Long { "raid-5", RT_RAID5 }, 239763fae79SScott Long { "raid6", RT_RAID6 }, 240763fae79SScott Long { "raid-6", RT_RAID6 }, 241763fae79SScott Long { "jbod", RT_JBOD }, 242763fae79SScott Long { "concat", RT_CONCAT }, 243763fae79SScott Long { "raid10", RT_RAID10 }, 244763fae79SScott Long { "raid1+0", RT_RAID10 }, 245763fae79SScott Long { "raid-10", RT_RAID10 }, 246763fae79SScott Long { "raid-1+0", RT_RAID10 }, 247763fae79SScott Long { "raid50", RT_RAID50 }, 248763fae79SScott Long { "raid5+0", RT_RAID50 }, 249763fae79SScott Long { "raid-50", RT_RAID50 }, 250763fae79SScott Long { "raid-5+0", RT_RAID50 }, 251763fae79SScott Long { "raid60", RT_RAID60 }, 252763fae79SScott Long { "raid6+0", RT_RAID60 }, 253763fae79SScott Long { "raid-60", RT_RAID60 }, 254763fae79SScott Long { "raid-6+0", RT_RAID60 }, 255763fae79SScott Long { NULL, 0 }, 256763fae79SScott Long }; 257763fae79SScott Long 258763fae79SScott Long struct config_id_state { 259763fae79SScott Long int array_count; 260763fae79SScott Long int log_drv_count; 261763fae79SScott Long int *arrays; 262763fae79SScott Long int *volumes; 263763fae79SScott Long uint16_t array_ref; 264763fae79SScott Long uint8_t target_id; 265763fae79SScott Long }; 266763fae79SScott Long 267763fae79SScott Long struct array_info { 268763fae79SScott Long int drive_count; 269763fae79SScott Long struct mfi_pd_info *drives; 270763fae79SScott Long struct mfi_array *array; 271763fae79SScott Long }; 272763fae79SScott Long 273763fae79SScott Long /* Parse a comma-separated list of drives for an array. */ 274763fae79SScott Long static int 275763fae79SScott Long parse_array(int fd, int raid_type, char *array_str, struct array_info *info) 276763fae79SScott Long { 277763fae79SScott Long struct mfi_pd_info *pinfo; 278763fae79SScott Long uint16_t device_id; 279763fae79SScott Long char *cp; 280763fae79SScott Long u_int count; 281763fae79SScott Long int error; 282763fae79SScott Long 283763fae79SScott Long cp = array_str; 284763fae79SScott Long for (count = 0; cp != NULL; count++) { 285763fae79SScott Long cp = strchr(cp, ','); 286763fae79SScott Long if (cp != NULL) { 287763fae79SScott Long cp++; 288763fae79SScott Long if (*cp == ',') { 289763fae79SScott Long warnx("Invalid drive list '%s'", array_str); 290763fae79SScott Long return (EINVAL); 291763fae79SScott Long } 292763fae79SScott Long } 293763fae79SScott Long } 294763fae79SScott Long 295763fae79SScott Long /* Validate the number of drives for this array. */ 296763fae79SScott Long if (count >= MAX_DRIVES_PER_ARRAY) { 297763fae79SScott Long warnx("Too many drives for a single array: max is %zu", 298763fae79SScott Long MAX_DRIVES_PER_ARRAY); 299763fae79SScott Long return (EINVAL); 300763fae79SScott Long } 301763fae79SScott Long switch (raid_type) { 302763fae79SScott Long case RT_RAID1: 303763fae79SScott Long case RT_RAID10: 304763fae79SScott Long if (count % 2 != 0) { 305763fae79SScott Long warnx("RAID1 and RAID10 require an even number of " 306763fae79SScott Long "drives in each array"); 307763fae79SScott Long return (EINVAL); 308763fae79SScott Long } 309763fae79SScott Long break; 310763fae79SScott Long case RT_RAID5: 311763fae79SScott Long case RT_RAID50: 312763fae79SScott Long if (count < 3) { 313763fae79SScott Long warnx("RAID5 and RAID50 require at least 3 drives in " 314763fae79SScott Long "each array"); 315763fae79SScott Long return (EINVAL); 316763fae79SScott Long } 317763fae79SScott Long break; 318763fae79SScott Long case RT_RAID6: 319763fae79SScott Long case RT_RAID60: 320763fae79SScott Long if (count < 4) { 321763fae79SScott Long warnx("RAID6 and RAID60 require at least 4 drives in " 322763fae79SScott Long "each array"); 323763fae79SScott Long return (EINVAL); 324763fae79SScott Long } 325763fae79SScott Long break; 326763fae79SScott Long } 327763fae79SScott Long 328763fae79SScott Long /* Validate each drive. */ 329763fae79SScott Long info->drives = calloc(count, sizeof(struct mfi_pd_info)); 330763fae79SScott Long info->drive_count = count; 331763fae79SScott Long for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 332763fae79SScott Long pinfo++) { 333763fae79SScott Long error = mfi_lookup_drive(fd, cp, &device_id); 334763fae79SScott Long if (error) 335763fae79SScott Long return (error); 336763fae79SScott Long 337763fae79SScott Long if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 338763fae79SScott Long warn("Failed to fetch drive info for drive %s", cp); 339763fae79SScott Long return (errno); 340763fae79SScott Long } 341763fae79SScott Long 342763fae79SScott Long if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 343763fae79SScott Long warnx("Drive %u is not available", device_id); 344763fae79SScott Long return (EINVAL); 345763fae79SScott Long } 346763fae79SScott Long } 347763fae79SScott Long 348763fae79SScott Long return (0); 349763fae79SScott Long } 350763fae79SScott Long 351763fae79SScott Long /* 352763fae79SScott Long * Find the next free array ref assuming that 'array_ref' is the last 353763fae79SScott Long * one used. 'array_ref' should be 0xffff for the initial test. 354763fae79SScott Long */ 355763fae79SScott Long static uint16_t 356763fae79SScott Long find_next_array(struct config_id_state *state) 357763fae79SScott Long { 358763fae79SScott Long int i; 359763fae79SScott Long 360763fae79SScott Long /* Assume the current one is used. */ 361763fae79SScott Long state->array_ref++; 362763fae79SScott Long 363763fae79SScott Long /* Find the next free one. */ 364763fae79SScott Long for (i = 0; i < state->array_count; i++) 365763fae79SScott Long if (state->arrays[i] == state->array_ref) 366763fae79SScott Long state->array_ref++; 367763fae79SScott Long return (state->array_ref); 368763fae79SScott Long } 369763fae79SScott Long 370763fae79SScott Long /* 371763fae79SScott Long * Find the next free volume ID assuming that 'target_id' is the last 372763fae79SScott Long * one used. 'target_id' should be 0xff for the initial test. 373763fae79SScott Long */ 374763fae79SScott Long static uint8_t 375763fae79SScott Long find_next_volume(struct config_id_state *state) 376763fae79SScott Long { 377763fae79SScott Long int i; 378763fae79SScott Long 379763fae79SScott Long /* Assume the current one is used. */ 380763fae79SScott Long state->target_id++; 381763fae79SScott Long 382763fae79SScott Long /* Find the next free one. */ 383763fae79SScott Long for (i = 0; i < state->log_drv_count; i++) 384763fae79SScott Long if (state->volumes[i] == state->target_id) 385763fae79SScott Long state->target_id++; 386763fae79SScott Long return (state->target_id); 387763fae79SScott Long } 388763fae79SScott Long 389763fae79SScott Long /* Populate an array with drives. */ 390763fae79SScott Long static void 391763fae79SScott Long build_array(int fd, char *arrayp, struct array_info *array_info, 392763fae79SScott Long struct config_id_state *state, int verbose) 393763fae79SScott Long { 394763fae79SScott Long struct mfi_array *ar = (struct mfi_array *)arrayp; 395763fae79SScott Long int i; 396763fae79SScott Long 397763fae79SScott Long ar->size = array_info->drives[0].coerced_size; 398763fae79SScott Long ar->num_drives = array_info->drive_count; 399763fae79SScott Long ar->array_ref = find_next_array(state); 400763fae79SScott Long for (i = 0; i < array_info->drive_count; i++) { 401763fae79SScott Long if (verbose) 402763fae79SScott Long printf("Adding drive %u to array %u\n", 403763fae79SScott Long array_info->drives[i].ref.v.device_id, 404763fae79SScott Long ar->array_ref); 405763fae79SScott Long if (ar->size > array_info->drives[i].coerced_size) 406763fae79SScott Long ar->size = array_info->drives[i].coerced_size; 407763fae79SScott Long ar->pd[i].ref = array_info->drives[i].ref; 408763fae79SScott Long ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 409763fae79SScott Long } 410763fae79SScott Long array_info->array = ar; 411763fae79SScott Long } 412763fae79SScott Long 413763fae79SScott Long /* 414763fae79SScott Long * Create a volume that spans one or more arrays. 415763fae79SScott Long */ 416763fae79SScott Long static void 417763fae79SScott Long build_volume(char *volumep, int narrays, struct array_info *arrays, 418763fae79SScott Long int raid_type, long stripe_size, struct config_id_state *state, int verbose) 419763fae79SScott Long { 420763fae79SScott Long struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 421763fae79SScott Long struct mfi_array *ar; 422763fae79SScott Long int i; 423763fae79SScott Long 424763fae79SScott Long /* properties */ 425763fae79SScott Long ld->properties.ld.v.target_id = find_next_volume(state); 426763fae79SScott Long ld->properties.ld.v.seq = 0; 427763fae79SScott Long ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 428763fae79SScott Long MR_LD_CACHE_WRITE_BACK; 429763fae79SScott Long ld->properties.access_policy = MFI_LD_ACCESS_RW; 430763fae79SScott Long ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 431763fae79SScott Long ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 432763fae79SScott Long MR_LD_CACHE_WRITE_BACK; 433763fae79SScott Long ld->properties.no_bgi = 0; 434763fae79SScott Long 435763fae79SScott Long /* params */ 436763fae79SScott Long switch (raid_type) { 437763fae79SScott Long case RT_RAID0: 438763fae79SScott Long case RT_JBOD: 439763fae79SScott Long ld->params.primary_raid_level = DDF_RAID0; 440763fae79SScott Long ld->params.raid_level_qualifier = 0; 441763fae79SScott Long ld->params.secondary_raid_level = 0; 442763fae79SScott Long break; 443763fae79SScott Long case RT_RAID1: 444763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1; 445763fae79SScott Long ld->params.raid_level_qualifier = 0; 446763fae79SScott Long ld->params.secondary_raid_level = 0; 447763fae79SScott Long break; 448763fae79SScott Long case RT_RAID5: 449763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5; 450763fae79SScott Long ld->params.raid_level_qualifier = 3; 451763fae79SScott Long ld->params.secondary_raid_level = 0; 452763fae79SScott Long break; 453763fae79SScott Long case RT_RAID6: 454763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6; 455763fae79SScott Long ld->params.raid_level_qualifier = 3; 456763fae79SScott Long ld->params.secondary_raid_level = 0; 457763fae79SScott Long break; 458763fae79SScott Long case RT_CONCAT: 459763fae79SScott Long ld->params.primary_raid_level = DDF_CONCAT; 460763fae79SScott Long ld->params.raid_level_qualifier = 0; 461763fae79SScott Long ld->params.secondary_raid_level = 0; 462763fae79SScott Long break; 463763fae79SScott Long case RT_RAID10: 464763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1; 465763fae79SScott Long ld->params.raid_level_qualifier = 0; 466763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 467763fae79SScott Long break; 468763fae79SScott Long case RT_RAID50: 469763fae79SScott Long /* 470763fae79SScott Long * XXX: This appears to work though the card's BIOS 471763fae79SScott Long * complains that the configuration is foreign. The 472763fae79SScott Long * BIOS setup does not allow for creation of RAID-50 473763fae79SScott Long * or RAID-60 arrays. The only nested array 474763fae79SScott Long * configuration it allows for is RAID-10. 475763fae79SScott Long */ 476763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5; 477763fae79SScott Long ld->params.raid_level_qualifier = 3; 478763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 479763fae79SScott Long break; 480763fae79SScott Long case RT_RAID60: 481763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6; 482763fae79SScott Long ld->params.raid_level_qualifier = 3; 483763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */ 484763fae79SScott Long break; 485763fae79SScott Long } 486763fae79SScott Long 487763fae79SScott Long /* 488763fae79SScott Long * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 489763fae79SScott Long * ffs() to simulate log2(stripe_size). 490763fae79SScott Long */ 491763fae79SScott Long ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 492763fae79SScott Long ld->params.num_drives = arrays[0].array->num_drives; 493763fae79SScott Long ld->params.span_depth = narrays; 494763fae79SScott Long ld->params.state = MFI_LD_STATE_OPTIMAL; 495763fae79SScott Long ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 496763fae79SScott Long ld->params.is_consistent = 0; 497763fae79SScott Long 498763fae79SScott Long /* spans */ 499763fae79SScott Long for (i = 0; i < narrays; i++) { 500763fae79SScott Long ar = arrays[i].array; 501763fae79SScott Long if (verbose) 502763fae79SScott Long printf("Adding array %u to volume %u\n", ar->array_ref, 503763fae79SScott Long ld->properties.ld.v.target_id); 504763fae79SScott Long ld->span[i].start_block = 0; 505763fae79SScott Long ld->span[i].num_blocks = ar->size; 506763fae79SScott Long ld->span[i].array_ref = ar->array_ref; 507763fae79SScott Long } 508763fae79SScott Long } 509763fae79SScott Long 510763fae79SScott Long static int 511763fae79SScott Long create_volume(int ac, char **av) 512763fae79SScott Long { 513763fae79SScott Long struct mfi_config_data *config; 514763fae79SScott Long struct mfi_array *ar; 515763fae79SScott Long struct mfi_ld_config *ld; 516763fae79SScott Long struct config_id_state state; 517763fae79SScott Long size_t config_size; 518763fae79SScott Long char *p, *cfg_arrays, *cfg_volumes; 519763fae79SScott Long int error, fd, i, raid_type; 520763fae79SScott Long int narrays, nvolumes, arrays_per_volume; 521763fae79SScott Long struct array_info *arrays; 522763fae79SScott Long long stripe_size; 523763fae79SScott Long #ifdef DEBUG 524763fae79SScott Long int dump; 525763fae79SScott Long #endif 526763fae79SScott Long int ch, verbose; 527763fae79SScott Long 528763fae79SScott Long /* 529763fae79SScott Long * Backwards compat. Map 'create volume' to 'create' and 530763fae79SScott Long * 'create spare' to 'add'. 531763fae79SScott Long */ 532763fae79SScott Long if (ac > 1) { 533763fae79SScott Long if (strcmp(av[1], "volume") == 0) { 534763fae79SScott Long av++; 535763fae79SScott Long ac--; 536763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) { 537763fae79SScott Long av++; 538763fae79SScott Long ac--; 539763fae79SScott Long return (add_spare(ac, av)); 540763fae79SScott Long } 541763fae79SScott Long } 542763fae79SScott Long 543763fae79SScott Long if (ac < 2) { 544763fae79SScott Long warnx("create volume: volume type required"); 545763fae79SScott Long return (EINVAL); 546763fae79SScott Long } 547763fae79SScott Long 548763fae79SScott Long 549763fae79SScott Long fd = mfi_open(mfi_unit); 550763fae79SScott Long if (fd < 0) { 551763fae79SScott Long warn("mfi_open"); 552763fae79SScott Long return (errno); 553763fae79SScott Long } 554763fae79SScott Long 555763fae79SScott Long if (!mfi_reconfig_supported()) { 556763fae79SScott Long warnx("The current mfi(4) driver does not support " 557763fae79SScott Long "configuration changes."); 558763fae79SScott Long return (EOPNOTSUPP); 559763fae79SScott Long } 560763fae79SScott Long 561763fae79SScott Long /* Lookup the RAID type first. */ 562763fae79SScott Long raid_type = -1; 563763fae79SScott Long for (i = 0; raid_type_table[i].name != NULL; i++) 564763fae79SScott Long if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 565763fae79SScott Long raid_type = raid_type_table[i].raid_type; 566763fae79SScott Long break; 567763fae79SScott Long } 568763fae79SScott Long 569763fae79SScott Long if (raid_type == -1) { 570763fae79SScott Long warnx("Unknown or unsupported volume type %s", av[1]); 571763fae79SScott Long return (EINVAL); 572763fae79SScott Long } 573763fae79SScott Long 574763fae79SScott Long /* Parse any options. */ 575763fae79SScott Long optind = 2; 576763fae79SScott Long #ifdef DEBUG 577763fae79SScott Long dump = 0; 578763fae79SScott Long #endif 579763fae79SScott Long verbose = 0; 580763fae79SScott Long stripe_size = 64 * 1024; 581763fae79SScott Long 582763fae79SScott Long while ((ch = getopt(ac, av, "ds:v")) != -1) { 583763fae79SScott Long switch (ch) { 584763fae79SScott Long #ifdef DEBUG 585763fae79SScott Long case 'd': 586763fae79SScott Long dump = 1; 587763fae79SScott Long break; 588763fae79SScott Long #endif 589763fae79SScott Long case 's': 590763fae79SScott Long stripe_size = dehumanize(optarg); 591763fae79SScott Long if ((stripe_size < 512) || (!powerof2(stripe_size))) 592763fae79SScott Long stripe_size = 64 * 1024; 593763fae79SScott Long break; 594763fae79SScott Long case 'v': 595763fae79SScott Long verbose = 1; 596763fae79SScott Long break; 597763fae79SScott Long case '?': 598763fae79SScott Long default: 599763fae79SScott Long return (EINVAL); 600763fae79SScott Long } 601763fae79SScott Long } 602763fae79SScott Long ac -= optind; 603763fae79SScott Long av += optind; 604763fae79SScott Long 605763fae79SScott Long /* Parse all the arrays. */ 606763fae79SScott Long narrays = ac; 607763fae79SScott Long if (narrays == 0) { 608763fae79SScott Long warnx("At least one drive list is required"); 609763fae79SScott Long return (EINVAL); 610763fae79SScott Long } 611763fae79SScott Long switch (raid_type) { 612763fae79SScott Long case RT_RAID0: 613763fae79SScott Long case RT_RAID1: 614763fae79SScott Long case RT_RAID5: 615763fae79SScott Long case RT_RAID6: 616763fae79SScott Long case RT_CONCAT: 617763fae79SScott Long if (narrays != 1) { 618763fae79SScott Long warnx("Only one drive list can be specified"); 619763fae79SScott Long return (EINVAL); 620763fae79SScott Long } 621763fae79SScott Long break; 622763fae79SScott Long case RT_RAID10: 623763fae79SScott Long case RT_RAID50: 624763fae79SScott Long case RT_RAID60: 625763fae79SScott Long if (narrays < 1) { 626763fae79SScott Long warnx("RAID10, RAID50, and RAID60 require at least " 627763fae79SScott Long "two drive lists"); 628763fae79SScott Long return (EINVAL); 629763fae79SScott Long } 630763fae79SScott Long if (narrays > MFI_MAX_SPAN_DEPTH) { 631763fae79SScott Long warnx("Volume spans more than %d arrays", 632763fae79SScott Long MFI_MAX_SPAN_DEPTH); 633763fae79SScott Long return (EINVAL); 634763fae79SScott Long } 635763fae79SScott Long break; 636763fae79SScott Long } 637763fae79SScott Long arrays = calloc(narrays, sizeof(*arrays)); 638763fae79SScott Long for (i = 0; i < narrays; i++) { 639763fae79SScott Long error = parse_array(fd, raid_type, av[i], &arrays[i]); 640763fae79SScott Long if (error) 641763fae79SScott Long return (error); 642763fae79SScott Long } 643763fae79SScott Long 644763fae79SScott Long switch (raid_type) { 645763fae79SScott Long case RT_RAID10: 646763fae79SScott Long case RT_RAID50: 647763fae79SScott Long case RT_RAID60: 648763fae79SScott Long for (i = 1; i < narrays; i++) { 649763fae79SScott Long if (arrays[i].drive_count != arrays[0].drive_count) { 650763fae79SScott Long warnx("All arrays must contain the same " 651763fae79SScott Long "number of drives"); 652763fae79SScott Long return (EINVAL); 653763fae79SScott Long } 654763fae79SScott Long } 655763fae79SScott Long break; 656763fae79SScott Long } 657763fae79SScott Long 658763fae79SScott Long /* 659763fae79SScott Long * Fetch the current config and build sorted lists of existing 660763fae79SScott Long * array and volume identifiers. 661763fae79SScott Long */ 662763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 663763fae79SScott Long warn("Failed to read configuration"); 664763fae79SScott Long return (errno); 665763fae79SScott Long } 666763fae79SScott Long p = (char *)config->array; 667763fae79SScott Long state.array_ref = 0xffff; 668763fae79SScott Long state.target_id = 0xff; 669763fae79SScott Long state.array_count = config->array_count; 670763fae79SScott Long if (config->array_count > 0) { 671763fae79SScott Long state.arrays = calloc(config->array_count, sizeof(int)); 672763fae79SScott Long for (i = 0; i < config->array_count; i++) { 673763fae79SScott Long ar = (struct mfi_array *)p; 674763fae79SScott Long state.arrays[i] = ar->array_ref; 675763fae79SScott Long p += config->array_size; 676763fae79SScott Long } 677763fae79SScott Long qsort(state.arrays, config->array_count, sizeof(int), 678763fae79SScott Long compare_int); 679763fae79SScott Long } else 680763fae79SScott Long state.arrays = NULL; 681763fae79SScott Long state.log_drv_count = config->log_drv_count; 682763fae79SScott Long if (config->log_drv_count) { 683763fae79SScott Long state.volumes = calloc(config->log_drv_count, sizeof(int)); 684763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 685763fae79SScott Long ld = (struct mfi_ld_config *)p; 686763fae79SScott Long state.volumes[i] = ld->properties.ld.v.target_id; 687763fae79SScott Long p += config->log_drv_size; 688763fae79SScott Long } 689763fae79SScott Long qsort(state.volumes, config->log_drv_count, sizeof(int), 690763fae79SScott Long compare_int); 691763fae79SScott Long } else 692763fae79SScott Long state.volumes = NULL; 693763fae79SScott Long free(config); 694763fae79SScott Long 695763fae79SScott Long /* Determine the size of the configuration we will build. */ 696763fae79SScott Long switch (raid_type) { 697763fae79SScott Long case RT_RAID0: 698763fae79SScott Long case RT_RAID1: 699763fae79SScott Long case RT_RAID5: 700763fae79SScott Long case RT_RAID6: 701763fae79SScott Long case RT_CONCAT: 702763fae79SScott Long case RT_JBOD: 703763fae79SScott Long /* Each volume spans a single array. */ 704763fae79SScott Long nvolumes = narrays; 705763fae79SScott Long break; 706763fae79SScott Long case RT_RAID10: 707763fae79SScott Long case RT_RAID50: 708763fae79SScott Long case RT_RAID60: 709763fae79SScott Long /* A single volume spans multiple arrays. */ 710763fae79SScott Long nvolumes = 1; 711763fae79SScott Long break; 712763fae79SScott Long default: 713763fae79SScott Long /* Pacify gcc. */ 714763fae79SScott Long abort(); 715763fae79SScott Long } 716763fae79SScott Long 717763fae79SScott Long config_size = sizeof(struct mfi_config_data) + 718763fae79SScott Long sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 719763fae79SScott Long config = calloc(1, config_size); 720763fae79SScott Long config->size = config_size; 721763fae79SScott Long config->array_count = narrays; 722763fae79SScott Long config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 723763fae79SScott Long config->log_drv_count = nvolumes; 724763fae79SScott Long config->log_drv_size = sizeof(struct mfi_ld_config); 725763fae79SScott Long config->spares_count = 0; 726763fae79SScott Long config->spares_size = 40; /* XXX: Firmware hardcode */ 727763fae79SScott Long cfg_arrays = (char *)config->array; 728763fae79SScott Long cfg_volumes = cfg_arrays + config->array_size * narrays; 729763fae79SScott Long 730763fae79SScott Long /* Build the arrays. */ 731763fae79SScott Long for (i = 0; i < narrays; i++) { 732763fae79SScott Long build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 733763fae79SScott Long cfg_arrays += config->array_size; 734763fae79SScott Long } 735763fae79SScott Long 736763fae79SScott Long /* Now build the volume(s). */ 737763fae79SScott Long arrays_per_volume = narrays / nvolumes; 738763fae79SScott Long for (i = 0; i < nvolumes; i++) { 739763fae79SScott Long build_volume(cfg_volumes, arrays_per_volume, 740763fae79SScott Long &arrays[i * arrays_per_volume], raid_type, stripe_size, 741763fae79SScott Long &state, verbose); 742763fae79SScott Long cfg_volumes += config->log_drv_size; 743763fae79SScott Long } 744763fae79SScott Long 745763fae79SScott Long #ifdef DEBUG 746763fae79SScott Long if (dump) 747763fae79SScott Long dump_config(fd, config); 748763fae79SScott Long else 749763fae79SScott Long #endif 750763fae79SScott Long 751763fae79SScott Long /* Send the new config to the controller. */ 752763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 753763fae79SScott Long NULL, 0, NULL) < 0) { 754763fae79SScott Long warn("Failed to add volume"); 755763fae79SScott Long return (errno); 756763fae79SScott Long } 757763fae79SScott Long 758763fae79SScott Long /* Clean up. */ 759763fae79SScott Long free(config); 760763fae79SScott Long if (state.log_drv_count > 0) 761763fae79SScott Long free(state.volumes); 762763fae79SScott Long if (state.array_count > 0) 763763fae79SScott Long free(state.arrays); 764763fae79SScott Long for (i = 0; i < narrays; i++) 765763fae79SScott Long free(arrays[i].drives); 766763fae79SScott Long free(arrays); 767763fae79SScott Long close(fd); 768763fae79SScott Long 769763fae79SScott Long return (0); 770763fae79SScott Long } 771763fae79SScott Long MFI_COMMAND(top, create, create_volume); 772763fae79SScott Long 773763fae79SScott Long static int 774763fae79SScott Long delete_volume(int ac, char **av) 775763fae79SScott Long { 776763fae79SScott Long struct mfi_ld_info info; 777763fae79SScott Long int fd; 778763fae79SScott Long uint8_t target_id, mbox[4]; 779763fae79SScott Long 780763fae79SScott Long /* 781763fae79SScott Long * Backwards compat. Map 'delete volume' to 'delete' and 782763fae79SScott Long * 'delete spare' to 'remove'. 783763fae79SScott Long */ 784763fae79SScott Long if (ac > 1) { 785763fae79SScott Long if (strcmp(av[1], "volume") == 0) { 786763fae79SScott Long av++; 787763fae79SScott Long ac--; 788763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) { 789763fae79SScott Long av++; 790763fae79SScott Long ac--; 791763fae79SScott Long return (remove_spare(ac, av)); 792763fae79SScott Long } 793763fae79SScott Long } 794763fae79SScott Long 795763fae79SScott Long if (ac != 2) { 796763fae79SScott Long warnx("delete volume: volume required"); 797763fae79SScott Long return (EINVAL); 798763fae79SScott Long } 799763fae79SScott Long 800763fae79SScott Long fd = mfi_open(mfi_unit); 801763fae79SScott Long if (fd < 0) { 802763fae79SScott Long warn("mfi_open"); 803763fae79SScott Long return (errno); 804763fae79SScott Long } 805763fae79SScott Long 806763fae79SScott Long if (!mfi_reconfig_supported()) { 807763fae79SScott Long warnx("The current mfi(4) driver does not support " 808763fae79SScott Long "configuration changes."); 809763fae79SScott Long return (EOPNOTSUPP); 810763fae79SScott Long } 811763fae79SScott Long 812763fae79SScott Long if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 813763fae79SScott Long warn("Invalid volume %s", av[1]); 814763fae79SScott Long return (errno); 815763fae79SScott Long } 816763fae79SScott Long 817763fae79SScott Long if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 818763fae79SScott Long warn("Failed to get info for volume %d", target_id); 819763fae79SScott Long return (errno); 820763fae79SScott Long } 821763fae79SScott Long 822763fae79SScott Long if (mfi_volume_busy(fd, target_id)) { 823763fae79SScott Long warnx("Volume %s is busy and cannot be deleted", 824763fae79SScott Long mfi_volume_name(fd, target_id)); 825763fae79SScott Long return (EBUSY); 826763fae79SScott Long } 827763fae79SScott Long 828763fae79SScott Long mbox_store_ldref(mbox, &info.ld_config.properties.ld); 829763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 830763fae79SScott Long sizeof(mbox), NULL) < 0) { 831763fae79SScott Long warn("Failed to delete volume"); 832763fae79SScott Long return (errno); 833763fae79SScott Long } 834763fae79SScott Long 835763fae79SScott Long close(fd); 836763fae79SScott Long 837763fae79SScott Long return (0); 838763fae79SScott Long } 839763fae79SScott Long MFI_COMMAND(top, delete, delete_volume); 840763fae79SScott Long 841763fae79SScott Long static int 842763fae79SScott Long add_spare(int ac, char **av) 843763fae79SScott Long { 844763fae79SScott Long struct mfi_pd_info info; 845763fae79SScott Long struct mfi_config_data *config; 846763fae79SScott Long struct mfi_array *ar; 847763fae79SScott Long struct mfi_ld_config *ld; 848763fae79SScott Long struct mfi_spare *spare; 849763fae79SScott Long uint16_t device_id; 850763fae79SScott Long uint8_t target_id; 851763fae79SScott Long char *p; 852763fae79SScott Long int error, fd, i; 853763fae79SScott Long 854763fae79SScott Long if (ac < 2) { 855763fae79SScott Long warnx("add spare: drive required"); 856763fae79SScott Long return (EINVAL); 857763fae79SScott Long } 858763fae79SScott Long 859763fae79SScott Long fd = mfi_open(mfi_unit); 860763fae79SScott Long if (fd < 0) { 861763fae79SScott Long warn("mfi_open"); 862763fae79SScott Long return (errno); 863763fae79SScott Long } 864763fae79SScott Long 865763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id); 866763fae79SScott Long if (error) 867763fae79SScott Long return (error); 868763fae79SScott Long 869763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 870763fae79SScott Long warn("Failed to fetch drive info"); 871763fae79SScott Long return (errno); 872763fae79SScott Long } 873763fae79SScott Long 874763fae79SScott Long if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 875763fae79SScott Long warnx("Drive %u is not available", device_id); 876763fae79SScott Long return (EINVAL); 877763fae79SScott Long } 878763fae79SScott Long 879763fae79SScott Long if (ac > 2) { 880763fae79SScott Long if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 881763fae79SScott Long warn("Invalid volume %s", av[2]); 882763fae79SScott Long return (errno); 883763fae79SScott Long } 884763fae79SScott Long } 885763fae79SScott Long 886763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 887763fae79SScott Long warn("Failed to read configuration"); 888763fae79SScott Long return (errno); 889763fae79SScott Long } 890763fae79SScott Long 891763fae79SScott Long spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 892763fae79SScott Long config->array_count); 893763fae79SScott Long bzero(spare, sizeof(struct mfi_spare)); 894763fae79SScott Long spare->ref = info.ref; 895763fae79SScott Long 896763fae79SScott Long if (ac == 2) { 897763fae79SScott Long /* Global spare backs all arrays. */ 898763fae79SScott Long p = (char *)config->array; 899763fae79SScott Long for (i = 0; i < config->array_count; i++) { 900763fae79SScott Long ar = (struct mfi_array *)p; 901763fae79SScott Long if (ar->size > info.coerced_size) { 902763fae79SScott Long warnx("Spare isn't large enough for array %u", 903763fae79SScott Long ar->array_ref); 904763fae79SScott Long return (EINVAL); 905763fae79SScott Long } 906763fae79SScott Long p += config->array_size; 907763fae79SScott Long } 908763fae79SScott Long spare->array_count = 0; 909763fae79SScott Long } else { 910763fae79SScott Long /* 911763fae79SScott Long * Dedicated spares only back the arrays for a 912763fae79SScott Long * specific volume. 913763fae79SScott Long */ 914763fae79SScott Long ld = mfi_config_lookup_volume(config, target_id); 915763fae79SScott Long if (ld == NULL) { 916763fae79SScott Long warnx("Did not find volume %d", target_id); 917763fae79SScott Long return (EINVAL); 918763fae79SScott Long } 919763fae79SScott Long 920763fae79SScott Long spare->spare_type |= MFI_SPARE_DEDICATED; 921763fae79SScott Long spare->array_count = ld->params.span_depth; 922763fae79SScott Long for (i = 0; i < ld->params.span_depth; i++) { 923763fae79SScott Long ar = mfi_config_lookup_array(config, 924763fae79SScott Long ld->span[i].array_ref); 925763fae79SScott Long if (ar == NULL) { 926763fae79SScott Long warnx("Missing array; inconsistent config?"); 927763fae79SScott Long return (ENXIO); 928763fae79SScott Long } 929763fae79SScott Long if (ar->size > info.coerced_size) { 930763fae79SScott Long warnx("Spare isn't large enough for array %u", 931763fae79SScott Long ar->array_ref); 932763fae79SScott Long return (EINVAL); 933763fae79SScott Long } 934763fae79SScott Long spare->array_ref[i] = ar->array_ref; 935763fae79SScott Long } 936763fae79SScott Long } 937763fae79SScott Long free(config); 938763fae79SScott Long 939763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 940763fae79SScott Long sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 941763fae79SScott Long NULL, 0, NULL) < 0) { 942763fae79SScott Long warn("Failed to assign spare"); 943763fae79SScott Long return (errno); 944763fae79SScott Long } 945763fae79SScott Long 946763fae79SScott Long close(fd); 947763fae79SScott Long 948763fae79SScott Long return (0); 949763fae79SScott Long } 950763fae79SScott Long MFI_COMMAND(top, add, add_spare); 951763fae79SScott Long 952763fae79SScott Long static int 953763fae79SScott Long remove_spare(int ac, char **av) 954763fae79SScott Long { 955763fae79SScott Long struct mfi_pd_info info; 956763fae79SScott Long int error, fd; 957763fae79SScott Long uint16_t device_id; 958763fae79SScott Long uint8_t mbox[4]; 959763fae79SScott Long 960763fae79SScott Long if (ac != 2) { 961763fae79SScott Long warnx("remove spare: drive required"); 962763fae79SScott Long return (EINVAL); 963763fae79SScott Long } 964763fae79SScott Long 965763fae79SScott Long fd = mfi_open(mfi_unit); 966763fae79SScott Long if (fd < 0) { 967763fae79SScott Long warn("mfi_open"); 968763fae79SScott Long return (errno); 969763fae79SScott Long } 970763fae79SScott Long 971763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id); 972763fae79SScott Long if (error) 973763fae79SScott Long return (error); 974763fae79SScott Long 975763fae79SScott Long /* Get the info for this drive. */ 976763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 977763fae79SScott Long warn("Failed to fetch info for drive %u", device_id); 978763fae79SScott Long return (errno); 979763fae79SScott Long } 980763fae79SScott Long 981763fae79SScott Long if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 982763fae79SScott Long warnx("Drive %u is not a hot spare", device_id); 983763fae79SScott Long return (EINVAL); 984763fae79SScott Long } 985763fae79SScott Long 986763fae79SScott Long mbox_store_pdref(mbox, &info.ref); 987763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 988763fae79SScott Long sizeof(mbox), NULL) < 0) { 989763fae79SScott Long warn("Failed to delete spare"); 990763fae79SScott Long return (errno); 991763fae79SScott Long } 992763fae79SScott Long 993763fae79SScott Long close(fd); 994763fae79SScott Long 995763fae79SScott Long return (0); 996763fae79SScott Long } 997763fae79SScott Long MFI_COMMAND(top, remove, remove_spare); 998763fae79SScott Long 999763fae79SScott Long #ifdef DEBUG 1000763fae79SScott Long /* Display raw data about a config. */ 1001763fae79SScott Long static void 1002763fae79SScott Long dump_config(int fd, struct mfi_config_data *config) 1003763fae79SScott Long { 1004763fae79SScott Long struct mfi_array *ar; 1005763fae79SScott Long struct mfi_ld_config *ld; 1006763fae79SScott Long struct mfi_spare *sp; 1007763fae79SScott Long struct mfi_pd_info pinfo; 1008763fae79SScott Long uint16_t device_id; 1009763fae79SScott Long char *p; 1010763fae79SScott Long int i, j; 1011763fae79SScott Long 1012763fae79SScott Long printf( 1013763fae79SScott Long "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n", 1014763fae79SScott Long mfi_unit, config->array_count, config->log_drv_count, 1015763fae79SScott Long config->spares_count); 1016763fae79SScott Long printf(" array size: %u\n", config->array_size); 1017763fae79SScott Long printf(" volume size: %u\n", config->log_drv_size); 1018763fae79SScott Long printf(" spare size: %u\n", config->spares_size); 1019763fae79SScott Long p = (char *)config->array; 1020763fae79SScott Long 1021763fae79SScott Long for (i = 0; i < config->array_count; i++) { 1022763fae79SScott Long ar = (struct mfi_array *)p; 1023763fae79SScott Long printf(" array %u of %u drives:\n", ar->array_ref, 1024763fae79SScott Long ar->num_drives); 1025763fae79SScott Long printf(" size = %ju\n", (uintmax_t)ar->size); 1026763fae79SScott Long for (j = 0; j < ar->num_drives; j++) { 1027*0c019d8cSRandi Harper device_id = ar->pd[j].ref.v.device_id; 1028763fae79SScott Long if (device_id == 0xffff) 1029763fae79SScott Long printf(" drive MISSING\n"); 1030763fae79SScott Long else { 1031763fae79SScott Long printf(" drive %u %s\n", device_id, 1032763fae79SScott Long mfi_pdstate(ar->pd[j].fw_state)); 1033763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &pinfo, 1034763fae79SScott Long NULL) >= 0) { 1035763fae79SScott Long printf(" raw size: %ju\n", 1036763fae79SScott Long (uintmax_t)pinfo.raw_size); 1037763fae79SScott Long printf(" non-coerced size: %ju\n", 1038763fae79SScott Long (uintmax_t)pinfo.non_coerced_size); 1039763fae79SScott Long printf(" coerced size: %ju\n", 1040763fae79SScott Long (uintmax_t)pinfo.coerced_size); 1041763fae79SScott Long } 1042763fae79SScott Long } 1043763fae79SScott Long } 1044763fae79SScott Long p += config->array_size; 1045763fae79SScott Long } 1046763fae79SScott Long 1047763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) { 1048763fae79SScott Long ld = (struct mfi_ld_config *)p; 1049763fae79SScott Long printf(" volume %s ", 1050763fae79SScott Long mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1051763fae79SScott Long printf("%s %s", 1052763fae79SScott Long mfi_raid_level(ld->params.primary_raid_level, 1053763fae79SScott Long ld->params.secondary_raid_level), 1054763fae79SScott Long mfi_ldstate(ld->params.state)); 1055763fae79SScott Long if (ld->properties.name[0] != '\0') 1056763fae79SScott Long printf(" <%s>", ld->properties.name); 1057763fae79SScott Long printf("\n"); 1058763fae79SScott Long printf(" primary raid level: %u\n", 1059763fae79SScott Long ld->params.primary_raid_level); 1060763fae79SScott Long printf(" raid level qualifier: %u\n", 1061763fae79SScott Long ld->params.raid_level_qualifier); 1062763fae79SScott Long printf(" secondary raid level: %u\n", 1063763fae79SScott Long ld->params.secondary_raid_level); 1064763fae79SScott Long printf(" stripe size: %u\n", ld->params.stripe_size); 1065763fae79SScott Long printf(" num drives: %u\n", ld->params.num_drives); 1066763fae79SScott Long printf(" init state: %u\n", ld->params.init_state); 1067763fae79SScott Long printf(" consistent: %u\n", ld->params.is_consistent); 1068763fae79SScott Long printf(" no bgi: %u\n", ld->properties.no_bgi); 1069763fae79SScott Long printf(" spans:\n"); 1070763fae79SScott Long for (j = 0; j < ld->params.span_depth; j++) { 1071763fae79SScott Long printf(" array %u @ ", ld->span[j].array_ref); 1072763fae79SScott Long printf("%ju : %ju\n", 1073763fae79SScott Long (uintmax_t)ld->span[j].start_block, 1074763fae79SScott Long (uintmax_t)ld->span[j].num_blocks); 1075763fae79SScott Long } 1076763fae79SScott Long p += config->log_drv_size; 1077763fae79SScott Long } 1078763fae79SScott Long 1079763fae79SScott Long for (i = 0; i < config->spares_count; i++) { 1080763fae79SScott Long sp = (struct mfi_spare *)p; 1081763fae79SScott Long printf(" %s spare %u ", 1082763fae79SScott Long sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1083*0c019d8cSRandi Harper "global", sp->ref.v.device_id); 1084763fae79SScott Long printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1085763fae79SScott Long printf(" backs:\n"); 1086763fae79SScott Long for (j = 0; j < sp->array_count; j++) 1087763fae79SScott Long printf(" array %u\n", sp->array_ref[j]); 1088763fae79SScott Long p += config->spares_size; 1089763fae79SScott Long } 1090763fae79SScott Long } 1091763fae79SScott Long 1092763fae79SScott Long static int 1093763fae79SScott Long debug_config(int ac, char **av) 1094763fae79SScott Long { 1095763fae79SScott Long struct mfi_config_data *config; 1096763fae79SScott Long int fd; 1097763fae79SScott Long 1098763fae79SScott Long if (ac != 1) { 1099763fae79SScott Long warnx("debug: extra arguments"); 1100763fae79SScott Long return (EINVAL); 1101763fae79SScott Long } 1102763fae79SScott Long 1103763fae79SScott Long fd = mfi_open(mfi_unit); 1104763fae79SScott Long if (fd < 0) { 1105763fae79SScott Long warn("mfi_open"); 1106763fae79SScott Long return (errno); 1107763fae79SScott Long } 1108763fae79SScott Long 1109763fae79SScott Long /* Get the config from the controller. */ 1110763fae79SScott Long if (mfi_config_read(fd, &config) < 0) { 1111763fae79SScott Long warn("Failed to get config"); 1112763fae79SScott Long return (errno); 1113763fae79SScott Long } 1114763fae79SScott Long 1115763fae79SScott Long /* Dump out the configuration. */ 1116763fae79SScott Long dump_config(fd, config); 1117763fae79SScott Long free(config); 1118763fae79SScott Long close(fd); 1119763fae79SScott Long 1120763fae79SScott Long return (0); 1121763fae79SScott Long } 1122763fae79SScott Long MFI_COMMAND(top, debug, debug_config); 1123763fae79SScott Long 1124763fae79SScott Long static int 1125763fae79SScott Long dump(int ac, char **av) 1126763fae79SScott Long { 1127763fae79SScott Long struct mfi_config_data *config; 1128763fae79SScott Long char buf[64]; 1129763fae79SScott Long size_t len; 1130763fae79SScott Long int fd; 1131763fae79SScott Long 1132763fae79SScott Long if (ac != 1) { 1133763fae79SScott Long warnx("dump: extra arguments"); 1134763fae79SScott Long return (EINVAL); 1135763fae79SScott Long } 1136763fae79SScott Long 1137763fae79SScott Long fd = mfi_open(mfi_unit); 1138763fae79SScott Long if (fd < 0) { 1139763fae79SScott Long warn("mfi_open"); 1140763fae79SScott Long return (errno); 1141763fae79SScott Long } 1142763fae79SScott Long 1143763fae79SScott Long /* Get the stashed copy of the last dcmd from the driver. */ 1144763fae79SScott Long snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1145763fae79SScott Long if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1146763fae79SScott Long warn("Failed to read debug command"); 1147763fae79SScott Long if (errno == ENOENT) 1148763fae79SScott Long errno = EOPNOTSUPP; 1149763fae79SScott Long return (errno); 1150763fae79SScott Long } 1151763fae79SScott Long 1152763fae79SScott Long config = malloc(len); 1153763fae79SScott Long if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1154763fae79SScott Long warn("Failed to read debug command"); 1155763fae79SScott Long return (errno); 1156763fae79SScott Long } 1157763fae79SScott Long dump_config(fd, config); 1158763fae79SScott Long free(config); 1159763fae79SScott Long close(fd); 1160763fae79SScott Long 1161763fae79SScott Long return (0); 1162763fae79SScott Long } 1163763fae79SScott Long MFI_COMMAND(top, dump, dump); 1164763fae79SScott Long #endif 1165