mfi_config.c revision 253244
1180750Sdes/*- 2180750Sdes * Copyright (c) 2008, 2009 Yahoo!, Inc. 3180750Sdes * All rights reserved. 4180750Sdes * 5180750Sdes * Redistribution and use in source and binary forms, with or without 6180750Sdes * modification, are permitted provided that the following conditions 7180750Sdes * are met: 8180750Sdes * 1. Redistributions of source code must retain the above copyright 9180750Sdes * notice, this list of conditions and the following disclaimer. 10180750Sdes * 2. Redistributions in binary form must reproduce the above copyright 11180750Sdes * notice, this list of conditions and the following disclaimer in the 12180750Sdes * documentation and/or other materials provided with the distribution. 13180750Sdes * 3. The names of the authors may not be used to endorse or promote 14180750Sdes * products derived from this software without specific prior written 15180750Sdes * permission. 16180750Sdes * 17180750Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18180750Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19180750Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20180750Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21180750Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22180750Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23180750Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24180750Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25180750Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26180750Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27180750Sdes * SUCH DAMAGE. 28180750Sdes * 29180750Sdes * $FreeBSD: stable/9/usr.sbin/mfiutil/mfi_config.c 253244 2013-07-12 02:26:15Z sbruno $ 30180750Sdes */ 31180750Sdes 32180750Sdes#include <sys/param.h> 33180750Sdes#ifdef DEBUG 34180750Sdes#include <sys/sysctl.h> 35180750Sdes#endif 36180750Sdes#include <err.h> 37180750Sdes#include <errno.h> 38180750Sdes#include <fcntl.h> 39180750Sdes#include <libutil.h> 40180750Sdes#include <stdint.h> 41180750Sdes#include <stdio.h> 42180750Sdes#include <stdlib.h> 43180750Sdes#include <string.h> 44180750Sdes#include <unistd.h> 45180750Sdes#include "mfiutil.h" 46180750Sdes 47180750Sdesstatic int add_spare(int ac, char **av); 48180750Sdesstatic int remove_spare(int ac, char **av); 49180750Sdes 50180750Sdesstatic long 51180750Sdesdehumanize(const char *value) 52180750Sdes{ 53180750Sdes char *vtp; 54180750Sdes long iv; 55180750Sdes 56180750Sdes if (value == NULL) 57180750Sdes return (0); 58180750Sdes iv = strtoq(value, &vtp, 0); 59180750Sdes if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 60180750Sdes return (0); 61180750Sdes } 62180750Sdes switch (vtp[0]) { 63180750Sdes case 't': case 'T': 64180750Sdes iv *= 1024; 65180750Sdes case 'g': case 'G': 66180750Sdes iv *= 1024; 67180750Sdes case 'm': case 'M': 68180750Sdes iv *= 1024; 69180750Sdes case 'k': case 'K': 70180750Sdes iv *= 1024; 71180750Sdes case '\0': 72180750Sdes break; 73180750Sdes default: 74180750Sdes return (0); 75180750Sdes } 76180750Sdes return (iv); 77180750Sdes} 78180750Sdes 79180750Sdesint 80180750Sdesmfi_config_read(int fd, struct mfi_config_data **configp) 81180750Sdes{ 82180750Sdes return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0); 83180750Sdes} 84180750Sdes 85180750Sdesint 86180750Sdesmfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp, 87180750Sdes uint8_t *mbox, size_t mboxlen) 88180750Sdes{ 89180750Sdes struct mfi_config_data *config; 90180750Sdes uint32_t config_size; 91180750Sdes int error; 92180750Sdes 93180750Sdes /* 94180750Sdes * Keep fetching the config in a loop until we have a large enough 95180750Sdes * buffer to hold the entire configuration. 96180750Sdes */ 97180750Sdes config = NULL; 98180750Sdes config_size = 1024; 99180750Sdesfetch: 100180750Sdes config = reallocf(config, config_size); 101180750Sdes if (config == NULL) 102180750Sdes return (-1); 103180750Sdes if (mfi_dcmd_command(fd, opcode, config, 104180750Sdes config_size, mbox, mboxlen, NULL) < 0) { 105180750Sdes error = errno; 106180750Sdes free(config); 107180750Sdes errno = error; 108180750Sdes return (-1); 109180750Sdes } 110180750Sdes 111180750Sdes if (config->size > config_size) { 112180750Sdes config_size = config->size; 113180750Sdes goto fetch; 114180750Sdes } 115180750Sdes 116180750Sdes *configp = config; 117180750Sdes return (0); 118180750Sdes} 119180750Sdes 120180750Sdesstatic struct mfi_array * 121180750Sdesmfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 122180750Sdes{ 123180750Sdes struct mfi_array *ar; 124180750Sdes char *p; 125180750Sdes int i; 126180750Sdes 127180750Sdes p = (char *)config->array; 128180750Sdes for (i = 0; i < config->array_count; i++) { 129180750Sdes ar = (struct mfi_array *)p; 130180750Sdes if (ar->array_ref == array_ref) 131180750Sdes return (ar); 132180750Sdes p += config->array_size; 133180750Sdes } 134180750Sdes 135180750Sdes return (NULL); 136180750Sdes} 137180750Sdes 138180750Sdesstatic struct mfi_ld_config * 139180750Sdesmfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 140180750Sdes{ 141180750Sdes struct mfi_ld_config *ld; 142180750Sdes char *p; 143180750Sdes int i; 144180750Sdes 145180750Sdes p = (char *)config->array + config->array_count * config->array_size; 146180750Sdes for (i = 0; i < config->log_drv_count; i++) { 147180750Sdes ld = (struct mfi_ld_config *)p; 148180750Sdes if (ld->properties.ld.v.target_id == target_id) 149180750Sdes return (ld); 150180750Sdes p += config->log_drv_size; 151180750Sdes } 152180750Sdes 153180750Sdes return (NULL); 154180750Sdes} 155180750Sdes 156180750Sdesstatic int 157180750Sdesclear_config(int ac __unused, char **av __unused) 158180750Sdes{ 159180750Sdes struct mfi_ld_list list; 160180750Sdes int ch, error, fd; 161180750Sdes u_int i; 162180750Sdes 163180750Sdes fd = mfi_open(mfi_unit, O_RDWR); 164180750Sdes if (fd < 0) { 165180750Sdes error = errno; 166180750Sdes warn("mfi_open"); 167180750Sdes return (error); 168180750Sdes } 169180750Sdes 170180750Sdes if (!mfi_reconfig_supported()) { 171180750Sdes warnx("The current mfi(4) driver does not support " 172180750Sdes "configuration changes."); 173180750Sdes close(fd); 174180750Sdes return (EOPNOTSUPP); 175180750Sdes } 176180750Sdes 177180750Sdes if (mfi_ld_get_list(fd, &list, NULL) < 0) { 178180750Sdes error = errno; 179180750Sdes warn("Failed to get volume list"); 180180750Sdes close(fd); 181180750Sdes return (error); 182180750Sdes } 183180750Sdes 184180750Sdes for (i = 0; i < list.ld_count; i++) { 185180750Sdes if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 186180750Sdes warnx("Volume %s is busy and cannot be deleted", 187180750Sdes mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 188180750Sdes close(fd); 189180750Sdes return (EBUSY); 190180750Sdes } 191180750Sdes } 192180750Sdes 193180750Sdes printf( 194180750Sdes "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 195180750Sdes mfi_unit); 196180750Sdes ch = getchar(); 197180750Sdes if (ch != 'y' && ch != 'Y') { 198180750Sdes printf("\nAborting\n"); 199180750Sdes close(fd); 200180750Sdes return (0); 201180750Sdes } 202180750Sdes 203180750Sdes if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 204180750Sdes error = errno; 205180750Sdes warn("Failed to clear configuration"); 206180750Sdes close(fd); 207180750Sdes return (error); 208180750Sdes } 209180750Sdes 210180750Sdes printf("mfi%d: Configuration cleared\n", mfi_unit); 211180750Sdes close(fd); 212180750Sdes 213180750Sdes return (0); 214180750Sdes} 215180750SdesMFI_COMMAND(top, clear, clear_config); 216180750Sdes 217180750Sdes#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE 218180750Sdes#define MFI_ARRAY_SIZE sizeof(struct mfi_array) 219180750Sdes 220180750Sdes#define RT_RAID0 0 221180750Sdes#define RT_RAID1 1 222180750Sdes#define RT_RAID5 2 223180750Sdes#define RT_RAID6 3 224180750Sdes#define RT_JBOD 4 225180750Sdes#define RT_CONCAT 5 226180750Sdes#define RT_RAID10 6 227180750Sdes#define RT_RAID50 7 228180750Sdes#define RT_RAID60 8 229180750Sdes 230180750Sdesstatic int 231180750Sdescompare_int(const void *one, const void *two) 232180750Sdes{ 233180750Sdes int first, second; 234180750Sdes 235180750Sdes first = *(const int *)one; 236180750Sdes second = *(const int *)two; 237180750Sdes 238180750Sdes return (first - second); 239180750Sdes} 240180750Sdes 241180750Sdesstatic struct raid_type_entry { 242180750Sdes const char *name; 243180750Sdes int raid_type; 244180750Sdes} raid_type_table[] = { 245180750Sdes { "raid0", RT_RAID0 }, 246180750Sdes { "raid-0", RT_RAID0 }, 247180750Sdes { "raid1", RT_RAID1 }, 248180750Sdes { "raid-1", RT_RAID1 }, 249180750Sdes { "mirror", RT_RAID1 }, 250180750Sdes { "raid5", RT_RAID5 }, 251180750Sdes { "raid-5", RT_RAID5 }, 252180750Sdes { "raid6", RT_RAID6 }, 253180750Sdes { "raid-6", RT_RAID6 }, 254180750Sdes { "jbod", RT_JBOD }, 255180750Sdes { "concat", RT_CONCAT }, 256180750Sdes { "raid10", RT_RAID10 }, 257180750Sdes { "raid1+0", RT_RAID10 }, 258180750Sdes { "raid-10", RT_RAID10 }, 259180750Sdes { "raid-1+0", RT_RAID10 }, 260180750Sdes { "raid50", RT_RAID50 }, 261180750Sdes { "raid5+0", RT_RAID50 }, 262180750Sdes { "raid-50", RT_RAID50 }, 263180750Sdes { "raid-5+0", RT_RAID50 }, 264180750Sdes { "raid60", RT_RAID60 }, 265180750Sdes { "raid6+0", RT_RAID60 }, 266180750Sdes { "raid-60", RT_RAID60 }, 267180750Sdes { "raid-6+0", RT_RAID60 }, 268180750Sdes { NULL, 0 }, 269180750Sdes}; 270180750Sdes 271180750Sdesstruct config_id_state { 272180750Sdes int array_count; 273180750Sdes int log_drv_count; 274180750Sdes int *arrays; 275180750Sdes int *volumes; 276180750Sdes uint16_t array_ref; 277180750Sdes uint8_t target_id; 278180750Sdes}; 279180750Sdes 280180750Sdesstruct array_info { 281180750Sdes int drive_count; 282180750Sdes struct mfi_pd_info *drives; 283180750Sdes struct mfi_array *array; 284180750Sdes}; 285180750Sdes 286180750Sdes/* Parse a comma-separated list of drives for an array. */ 287180750Sdesstatic int 288180750Sdesparse_array(int fd, int raid_type, char *array_str, struct array_info *info) 289180750Sdes{ 290180750Sdes struct mfi_pd_info *pinfo; 291180750Sdes uint16_t device_id; 292180750Sdes char *cp; 293180750Sdes u_int count; 294180750Sdes int error; 295180750Sdes 296180750Sdes cp = array_str; 297180750Sdes for (count = 0; cp != NULL; count++) { 298180750Sdes cp = strchr(cp, ','); 299180750Sdes if (cp != NULL) { 300180750Sdes cp++; 301180750Sdes if (*cp == ',') { 302180750Sdes warnx("Invalid drive list '%s'", array_str); 303180750Sdes return (EINVAL); 304180750Sdes } 305180750Sdes } 306180750Sdes } 307180750Sdes 308180750Sdes /* Validate the number of drives for this array. */ 309180750Sdes if (count >= MAX_DRIVES_PER_ARRAY) { 310180750Sdes warnx("Too many drives for a single array: max is %d", 311180750Sdes MAX_DRIVES_PER_ARRAY); 312180750Sdes return (EINVAL); 313180750Sdes } 314180750Sdes switch (raid_type) { 315180750Sdes case RT_RAID1: 316180750Sdes case RT_RAID10: 317180750Sdes if (count % 2 != 0) { 318180750Sdes warnx("RAID1 and RAID10 require an even number of " 319180750Sdes "drives in each array"); 320180750Sdes return (EINVAL); 321180750Sdes } 322180750Sdes break; 323180750Sdes case RT_RAID5: 324180750Sdes case RT_RAID50: 325180750Sdes if (count < 3) { 326180750Sdes warnx("RAID5 and RAID50 require at least 3 drives in " 327180750Sdes "each array"); 328180750Sdes return (EINVAL); 329180750Sdes } 330180750Sdes break; 331180750Sdes case RT_RAID6: 332180750Sdes case RT_RAID60: 333180750Sdes if (count < 4) { 334180750Sdes warnx("RAID6 and RAID60 require at least 4 drives in " 335180750Sdes "each array"); 336180750Sdes return (EINVAL); 337180750Sdes } 338180750Sdes break; 339180750Sdes } 340180750Sdes 341180750Sdes /* Validate each drive. */ 342180750Sdes info->drives = calloc(count, sizeof(struct mfi_pd_info)); 343180750Sdes if (info->drives == NULL) { 344180750Sdes warnx("malloc failed"); 345180750Sdes return (ENOMEM); 346180750Sdes } 347180750Sdes info->drive_count = count; 348180750Sdes for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 349180750Sdes pinfo++) { 350180750Sdes error = mfi_lookup_drive(fd, cp, &device_id); 351180750Sdes if (error) { 352180750Sdes free(info->drives); 353180750Sdes info->drives = NULL; 354180750Sdes return (error); 355180750Sdes } 356180750Sdes 357180750Sdes if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 358180750Sdes error = errno; 359180750Sdes warn("Failed to fetch drive info for drive %s", cp); 360180750Sdes free(info->drives); 361180750Sdes info->drives = NULL; 362180750Sdes return (error); 363180750Sdes } 364180750Sdes 365180750Sdes if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 366180750Sdes warnx("Drive %u is not available", device_id); 367180750Sdes free(info->drives); 368180750Sdes info->drives = NULL; 369180750Sdes return (EINVAL); 370180750Sdes } 371180750Sdes 372180750Sdes if (pinfo->state.ddf.v.pd_type.is_foreign) { 373180750Sdes warnx("Drive %u is foreign", device_id); 374180750Sdes free(info->drives); 375180750Sdes info->drives = NULL; 376180750Sdes return (EINVAL); 377180750Sdes } 378180750Sdes } 379180750Sdes 380180750Sdes return (0); 381180750Sdes} 382180750Sdes 383180750Sdes/* 384180750Sdes * Find the next free array ref assuming that 'array_ref' is the last 385180750Sdes * one used. 'array_ref' should be 0xffff for the initial test. 386180750Sdes */ 387180750Sdesstatic uint16_t 388180750Sdesfind_next_array(struct config_id_state *state) 389180750Sdes{ 390180750Sdes int i; 391180750Sdes 392180750Sdes /* Assume the current one is used. */ 393180750Sdes state->array_ref++; 394180750Sdes 395180750Sdes /* Find the next free one. */ 396180750Sdes for (i = 0; i < state->array_count; i++) 397180750Sdes if (state->arrays[i] == state->array_ref) 398180750Sdes state->array_ref++; 399180750Sdes return (state->array_ref); 400180750Sdes} 401180750Sdes 402180750Sdes/* 403180750Sdes * Find the next free volume ID assuming that 'target_id' is the last 404180750Sdes * one used. 'target_id' should be 0xff for the initial test. 405180750Sdes */ 406180750Sdesstatic uint8_t 407180750Sdesfind_next_volume(struct config_id_state *state) 408180750Sdes{ 409180750Sdes int i; 410180750Sdes 411180750Sdes /* Assume the current one is used. */ 412180750Sdes state->target_id++; 413180750Sdes 414180750Sdes /* Find the next free one. */ 415180750Sdes for (i = 0; i < state->log_drv_count; i++) 416180750Sdes if (state->volumes[i] == state->target_id) 417180750Sdes state->target_id++; 418180750Sdes return (state->target_id); 419180750Sdes} 420180750Sdes 421180750Sdes/* Populate an array with drives. */ 422static void 423build_array(int fd __unused, char *arrayp, struct array_info *array_info, 424 struct config_id_state *state, int verbose) 425{ 426 struct mfi_array *ar = (struct mfi_array *)arrayp; 427 int i; 428 429 ar->size = array_info->drives[0].coerced_size; 430 ar->num_drives = array_info->drive_count; 431 ar->array_ref = find_next_array(state); 432 for (i = 0; i < array_info->drive_count; i++) { 433 if (verbose) 434 printf("Adding drive %s to array %u\n", 435 mfi_drive_name(NULL, 436 array_info->drives[i].ref.v.device_id, 437 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS), 438 ar->array_ref); 439 if (ar->size > array_info->drives[i].coerced_size) 440 ar->size = array_info->drives[i].coerced_size; 441 ar->pd[i].ref = array_info->drives[i].ref; 442 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 443 } 444 array_info->array = ar; 445} 446 447/* 448 * Create a volume that spans one or more arrays. 449 */ 450static void 451build_volume(char *volumep, int narrays, struct array_info *arrays, 452 int raid_type, long stripe_size, struct config_id_state *state, int verbose) 453{ 454 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 455 struct mfi_array *ar; 456 int i; 457 458 /* properties */ 459 ld->properties.ld.v.target_id = find_next_volume(state); 460 ld->properties.ld.v.seq = 0; 461 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 462 MR_LD_CACHE_WRITE_BACK; 463 ld->properties.access_policy = MFI_LD_ACCESS_RW; 464 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 465 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 466 MR_LD_CACHE_WRITE_BACK; 467 ld->properties.no_bgi = 0; 468 469 /* params */ 470 switch (raid_type) { 471 case RT_RAID0: 472 case RT_JBOD: 473 ld->params.primary_raid_level = DDF_RAID0; 474 ld->params.raid_level_qualifier = 0; 475 ld->params.secondary_raid_level = 0; 476 break; 477 case RT_RAID1: 478 ld->params.primary_raid_level = DDF_RAID1; 479 ld->params.raid_level_qualifier = 0; 480 ld->params.secondary_raid_level = 0; 481 break; 482 case RT_RAID5: 483 ld->params.primary_raid_level = DDF_RAID5; 484 ld->params.raid_level_qualifier = 3; 485 ld->params.secondary_raid_level = 0; 486 break; 487 case RT_RAID6: 488 ld->params.primary_raid_level = DDF_RAID6; 489 ld->params.raid_level_qualifier = 3; 490 ld->params.secondary_raid_level = 0; 491 break; 492 case RT_CONCAT: 493 ld->params.primary_raid_level = DDF_CONCAT; 494 ld->params.raid_level_qualifier = 0; 495 ld->params.secondary_raid_level = 0; 496 break; 497 case RT_RAID10: 498 ld->params.primary_raid_level = DDF_RAID1; 499 ld->params.raid_level_qualifier = 0; 500 ld->params.secondary_raid_level = 3; /* XXX? */ 501 break; 502 case RT_RAID50: 503 /* 504 * XXX: This appears to work though the card's BIOS 505 * complains that the configuration is foreign. The 506 * BIOS setup does not allow for creation of RAID-50 507 * or RAID-60 arrays. The only nested array 508 * configuration it allows for is RAID-10. 509 */ 510 ld->params.primary_raid_level = DDF_RAID5; 511 ld->params.raid_level_qualifier = 3; 512 ld->params.secondary_raid_level = 3; /* XXX? */ 513 break; 514 case RT_RAID60: 515 ld->params.primary_raid_level = DDF_RAID6; 516 ld->params.raid_level_qualifier = 3; 517 ld->params.secondary_raid_level = 3; /* XXX? */ 518 break; 519 } 520 521 /* 522 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 523 * ffs() to simulate log2(stripe_size). 524 */ 525 ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 526 ld->params.num_drives = arrays[0].array->num_drives; 527 ld->params.span_depth = narrays; 528 ld->params.state = MFI_LD_STATE_OPTIMAL; 529 ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 530 ld->params.is_consistent = 0; 531 532 /* spans */ 533 for (i = 0; i < narrays; i++) { 534 ar = arrays[i].array; 535 if (verbose) 536 printf("Adding array %u to volume %u\n", ar->array_ref, 537 ld->properties.ld.v.target_id); 538 ld->span[i].start_block = 0; 539 ld->span[i].num_blocks = ar->size; 540 ld->span[i].array_ref = ar->array_ref; 541 } 542} 543 544static int 545create_volume(int ac, char **av) 546{ 547 struct mfi_config_data *config; 548 struct mfi_array *ar; 549 struct mfi_ld_config *ld; 550 struct config_id_state state; 551 size_t config_size; 552 char *p, *cfg_arrays, *cfg_volumes; 553 int error, fd, i, raid_type; 554 int narrays, nvolumes, arrays_per_volume; 555 struct array_info *arrays; 556 long stripe_size; 557#ifdef DEBUG 558 int dump; 559#endif 560 int ch, verbose; 561 562 /* 563 * Backwards compat. Map 'create volume' to 'create' and 564 * 'create spare' to 'add'. 565 */ 566 if (ac > 1) { 567 if (strcmp(av[1], "volume") == 0) { 568 av++; 569 ac--; 570 } else if (strcmp(av[1], "spare") == 0) { 571 av++; 572 ac--; 573 return (add_spare(ac, av)); 574 } 575 } 576 577 if (ac < 2) { 578 warnx("create volume: volume type required"); 579 return (EINVAL); 580 } 581 582 bzero(&state, sizeof(state)); 583 config = NULL; 584 arrays = NULL; 585 narrays = 0; 586 error = 0; 587 588 fd = mfi_open(mfi_unit, O_RDWR); 589 if (fd < 0) { 590 error = errno; 591 warn("mfi_open"); 592 return (error); 593 } 594 595 if (!mfi_reconfig_supported()) { 596 warnx("The current mfi(4) driver does not support " 597 "configuration changes."); 598 error = EOPNOTSUPP; 599 goto error; 600 } 601 602 /* Lookup the RAID type first. */ 603 raid_type = -1; 604 for (i = 0; raid_type_table[i].name != NULL; i++) 605 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 606 raid_type = raid_type_table[i].raid_type; 607 break; 608 } 609 610 if (raid_type == -1) { 611 warnx("Unknown or unsupported volume type %s", av[1]); 612 error = EINVAL; 613 goto error; 614 } 615 616 /* Parse any options. */ 617 optind = 2; 618#ifdef DEBUG 619 dump = 0; 620#endif 621 verbose = 0; 622 stripe_size = 64 * 1024; 623 624 while ((ch = getopt(ac, av, "ds:v")) != -1) { 625 switch (ch) { 626#ifdef DEBUG 627 case 'd': 628 dump = 1; 629 break; 630#endif 631 case 's': 632 stripe_size = dehumanize(optarg); 633 if ((stripe_size < 512) || (!powerof2(stripe_size))) 634 stripe_size = 64 * 1024; 635 break; 636 case 'v': 637 verbose = 1; 638 break; 639 case '?': 640 default: 641 error = EINVAL; 642 goto error; 643 } 644 } 645 ac -= optind; 646 av += optind; 647 648 /* Parse all the arrays. */ 649 narrays = ac; 650 if (narrays == 0) { 651 warnx("At least one drive list is required"); 652 error = EINVAL; 653 goto error; 654 } 655 switch (raid_type) { 656 case RT_RAID0: 657 case RT_RAID1: 658 case RT_RAID5: 659 case RT_RAID6: 660 case RT_CONCAT: 661 if (narrays != 1) { 662 warnx("Only one drive list can be specified"); 663 error = EINVAL; 664 goto error; 665 } 666 break; 667 case RT_RAID10: 668 case RT_RAID50: 669 case RT_RAID60: 670 if (narrays < 1) { 671 warnx("RAID10, RAID50, and RAID60 require at least " 672 "two drive lists"); 673 error = EINVAL; 674 goto error; 675 } 676 if (narrays > MFI_MAX_SPAN_DEPTH) { 677 warnx("Volume spans more than %d arrays", 678 MFI_MAX_SPAN_DEPTH); 679 error = EINVAL; 680 goto error; 681 } 682 break; 683 } 684 arrays = calloc(narrays, sizeof(*arrays)); 685 if (arrays == NULL) { 686 warnx("malloc failed"); 687 error = ENOMEM; 688 goto error; 689 } 690 for (i = 0; i < narrays; i++) { 691 error = parse_array(fd, raid_type, av[i], &arrays[i]); 692 if (error) 693 goto error; 694 } 695 696 switch (raid_type) { 697 case RT_RAID10: 698 case RT_RAID50: 699 case RT_RAID60: 700 for (i = 1; i < narrays; i++) { 701 if (arrays[i].drive_count != arrays[0].drive_count) { 702 warnx("All arrays must contain the same " 703 "number of drives"); 704 error = EINVAL; 705 goto error; 706 } 707 } 708 break; 709 } 710 711 /* 712 * Fetch the current config and build sorted lists of existing 713 * array and volume identifiers. 714 */ 715 if (mfi_config_read(fd, &config) < 0) { 716 error = errno; 717 warn("Failed to read configuration"); 718 goto error; 719 } 720 p = (char *)config->array; 721 state.array_ref = 0xffff; 722 state.target_id = 0xff; 723 state.array_count = config->array_count; 724 if (config->array_count > 0) { 725 state.arrays = calloc(config->array_count, sizeof(int)); 726 if (state.arrays == NULL) { 727 warnx("malloc failed"); 728 error = ENOMEM; 729 goto error; 730 } 731 for (i = 0; i < config->array_count; i++) { 732 ar = (struct mfi_array *)p; 733 state.arrays[i] = ar->array_ref; 734 p += config->array_size; 735 } 736 qsort(state.arrays, config->array_count, sizeof(int), 737 compare_int); 738 } else 739 state.arrays = NULL; 740 state.log_drv_count = config->log_drv_count; 741 if (config->log_drv_count) { 742 state.volumes = calloc(config->log_drv_count, sizeof(int)); 743 if (state.volumes == NULL) { 744 warnx("malloc failed"); 745 error = ENOMEM; 746 goto error; 747 } 748 for (i = 0; i < config->log_drv_count; i++) { 749 ld = (struct mfi_ld_config *)p; 750 state.volumes[i] = ld->properties.ld.v.target_id; 751 p += config->log_drv_size; 752 } 753 qsort(state.volumes, config->log_drv_count, sizeof(int), 754 compare_int); 755 } else 756 state.volumes = NULL; 757 free(config); 758 759 /* Determine the size of the configuration we will build. */ 760 switch (raid_type) { 761 case RT_RAID0: 762 case RT_RAID1: 763 case RT_RAID5: 764 case RT_RAID6: 765 case RT_CONCAT: 766 case RT_JBOD: 767 /* Each volume spans a single array. */ 768 nvolumes = narrays; 769 break; 770 case RT_RAID10: 771 case RT_RAID50: 772 case RT_RAID60: 773 /* A single volume spans multiple arrays. */ 774 nvolumes = 1; 775 break; 776 default: 777 /* Pacify gcc. */ 778 abort(); 779 } 780 781 config_size = sizeof(struct mfi_config_data) + 782 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 783 config = calloc(1, config_size); 784 if (config == NULL) { 785 warnx("malloc failed"); 786 error = ENOMEM; 787 goto error; 788 } 789 config->size = config_size; 790 config->array_count = narrays; 791 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 792 config->log_drv_count = nvolumes; 793 config->log_drv_size = sizeof(struct mfi_ld_config); 794 config->spares_count = 0; 795 config->spares_size = 40; /* XXX: Firmware hardcode */ 796 cfg_arrays = (char *)config->array; 797 cfg_volumes = cfg_arrays + config->array_size * narrays; 798 799 /* Build the arrays. */ 800 for (i = 0; i < narrays; i++) { 801 build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 802 cfg_arrays += config->array_size; 803 } 804 805 /* Now build the volume(s). */ 806 arrays_per_volume = narrays / nvolumes; 807 for (i = 0; i < nvolumes; i++) { 808 build_volume(cfg_volumes, arrays_per_volume, 809 &arrays[i * arrays_per_volume], raid_type, stripe_size, 810 &state, verbose); 811 cfg_volumes += config->log_drv_size; 812 } 813 814#ifdef DEBUG 815 if (dump) 816 dump_config(fd, config, NULL); 817#endif 818 819 /* Send the new config to the controller. */ 820 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 821 NULL, 0, NULL) < 0) { 822 error = errno; 823 warn("Failed to add volume"); 824 /* FALLTHROUGH */ 825 } 826 827error: 828 /* Clean up. */ 829 free(config); 830 free(state.volumes); 831 free(state.arrays); 832 if (arrays != NULL) { 833 for (i = 0; i < narrays; i++) 834 free(arrays[i].drives); 835 free(arrays); 836 } 837 close(fd); 838 839 return (error); 840} 841MFI_COMMAND(top, create, create_volume); 842 843static int 844delete_volume(int ac, char **av) 845{ 846 struct mfi_ld_info info; 847 int error, fd; 848 uint8_t target_id, mbox[4]; 849 850 /* 851 * Backwards compat. Map 'delete volume' to 'delete' and 852 * 'delete spare' to 'remove'. 853 */ 854 if (ac > 1) { 855 if (strcmp(av[1], "volume") == 0) { 856 av++; 857 ac--; 858 } else if (strcmp(av[1], "spare") == 0) { 859 av++; 860 ac--; 861 return (remove_spare(ac, av)); 862 } 863 } 864 865 if (ac != 2) { 866 warnx("delete volume: volume required"); 867 return (EINVAL); 868 } 869 870 fd = mfi_open(mfi_unit, O_RDWR); 871 if (fd < 0) { 872 error = errno; 873 warn("mfi_open"); 874 return (error); 875 } 876 877 if (!mfi_reconfig_supported()) { 878 warnx("The current mfi(4) driver does not support " 879 "configuration changes."); 880 close(fd); 881 return (EOPNOTSUPP); 882 } 883 884 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 885 error = errno; 886 warn("Invalid volume %s", av[1]); 887 close(fd); 888 return (error); 889 } 890 891 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 892 error = errno; 893 warn("Failed to get info for volume %d", target_id); 894 close(fd); 895 return (error); 896 } 897 898 if (mfi_volume_busy(fd, target_id)) { 899 warnx("Volume %s is busy and cannot be deleted", 900 mfi_volume_name(fd, target_id)); 901 close(fd); 902 return (EBUSY); 903 } 904 905 mbox_store_ldref(mbox, &info.ld_config.properties.ld); 906 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 907 sizeof(mbox), NULL) < 0) { 908 error = errno; 909 warn("Failed to delete volume"); 910 close(fd); 911 return (error); 912 } 913 914 close(fd); 915 916 return (0); 917} 918MFI_COMMAND(top, delete, delete_volume); 919 920static int 921add_spare(int ac, char **av) 922{ 923 struct mfi_pd_info info; 924 struct mfi_config_data *config; 925 struct mfi_array *ar; 926 struct mfi_ld_config *ld; 927 struct mfi_spare *spare; 928 uint16_t device_id; 929 uint8_t target_id; 930 char *p; 931 int error, fd, i; 932 933 if (ac < 2) { 934 warnx("add spare: drive required"); 935 return (EINVAL); 936 } 937 938 fd = mfi_open(mfi_unit, O_RDWR); 939 if (fd < 0) { 940 error = errno; 941 warn("mfi_open"); 942 return (error); 943 } 944 945 config = NULL; 946 spare = NULL; 947 error = mfi_lookup_drive(fd, av[1], &device_id); 948 if (error) 949 goto error; 950 951 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 952 error = errno; 953 warn("Failed to fetch drive info"); 954 goto error; 955 } 956 957 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 958 warnx("Drive %u is not available", device_id); 959 error = EINVAL; 960 goto error; 961 } 962 963 if (ac > 2) { 964 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 965 error = errno; 966 warn("Invalid volume %s", av[2]); 967 goto error; 968 } 969 } 970 971 if (mfi_config_read(fd, &config) < 0) { 972 error = errno; 973 warn("Failed to read configuration"); 974 goto error; 975 } 976 977 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 978 config->array_count); 979 if (spare == NULL) { 980 warnx("malloc failed"); 981 error = ENOMEM; 982 goto error; 983 } 984 bzero(spare, sizeof(struct mfi_spare)); 985 spare->ref = info.ref; 986 987 if (ac == 2) { 988 /* Global spare backs all arrays. */ 989 p = (char *)config->array; 990 for (i = 0; i < config->array_count; i++) { 991 ar = (struct mfi_array *)p; 992 if (ar->size > info.coerced_size) { 993 warnx("Spare isn't large enough for array %u", 994 ar->array_ref); 995 error = EINVAL; 996 goto error; 997 } 998 p += config->array_size; 999 } 1000 spare->array_count = 0; 1001 } else { 1002 /* 1003 * Dedicated spares only back the arrays for a 1004 * specific volume. 1005 */ 1006 ld = mfi_config_lookup_volume(config, target_id); 1007 if (ld == NULL) { 1008 warnx("Did not find volume %d", target_id); 1009 error = EINVAL; 1010 goto error; 1011 } 1012 1013 spare->spare_type |= MFI_SPARE_DEDICATED; 1014 spare->array_count = ld->params.span_depth; 1015 for (i = 0; i < ld->params.span_depth; i++) { 1016 ar = mfi_config_lookup_array(config, 1017 ld->span[i].array_ref); 1018 if (ar == NULL) { 1019 warnx("Missing array; inconsistent config?"); 1020 error = ENXIO; 1021 goto error; 1022 } 1023 if (ar->size > info.coerced_size) { 1024 warnx("Spare isn't large enough for array %u", 1025 ar->array_ref); 1026 error = EINVAL; 1027 goto error; 1028 } 1029 spare->array_ref[i] = ar->array_ref; 1030 } 1031 } 1032 1033 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 1034 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 1035 NULL, 0, NULL) < 0) { 1036 error = errno; 1037 warn("Failed to assign spare"); 1038 /* FALLTHROUGH. */ 1039 } 1040 1041error: 1042 free(spare); 1043 free(config); 1044 close(fd); 1045 1046 return (error); 1047} 1048MFI_COMMAND(top, add, add_spare); 1049 1050static int 1051remove_spare(int ac, char **av) 1052{ 1053 struct mfi_pd_info info; 1054 int error, fd; 1055 uint16_t device_id; 1056 uint8_t mbox[4]; 1057 1058 if (ac != 2) { 1059 warnx("remove spare: drive required"); 1060 return (EINVAL); 1061 } 1062 1063 fd = mfi_open(mfi_unit, O_RDWR); 1064 if (fd < 0) { 1065 error = errno; 1066 warn("mfi_open"); 1067 return (error); 1068 } 1069 1070 error = mfi_lookup_drive(fd, av[1], &device_id); 1071 if (error) { 1072 close(fd); 1073 return (error); 1074 } 1075 1076 /* Get the info for this drive. */ 1077 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 1078 error = errno; 1079 warn("Failed to fetch info for drive %u", device_id); 1080 close(fd); 1081 return (error); 1082 } 1083 1084 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 1085 warnx("Drive %u is not a hot spare", device_id); 1086 close(fd); 1087 return (EINVAL); 1088 } 1089 1090 mbox_store_pdref(mbox, &info.ref); 1091 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 1092 sizeof(mbox), NULL) < 0) { 1093 error = errno; 1094 warn("Failed to delete spare"); 1095 close(fd); 1096 return (error); 1097 } 1098 1099 close(fd); 1100 1101 return (0); 1102} 1103MFI_COMMAND(top, remove, remove_spare); 1104 1105/* Display raw data about a config. */ 1106void 1107dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix) 1108{ 1109 struct mfi_array *ar; 1110 struct mfi_ld_config *ld; 1111 struct mfi_spare *sp; 1112 struct mfi_pd_info pinfo; 1113 uint16_t device_id; 1114 char *p; 1115 int i, j; 1116 1117 if (NULL == msg_prefix) 1118 msg_prefix = "Configuration (Debug)"; 1119 1120 printf( 1121 "mfi%d %s: %d arrays, %d volumes, %d spares\n", mfi_unit, 1122 msg_prefix, config->array_count, config->log_drv_count, 1123 config->spares_count); 1124 printf(" array size: %u\n", config->array_size); 1125 printf(" volume size: %u\n", config->log_drv_size); 1126 printf(" spare size: %u\n", config->spares_size); 1127 p = (char *)config->array; 1128 1129 for (i = 0; i < config->array_count; i++) { 1130 ar = (struct mfi_array *)p; 1131 printf(" array %u of %u drives:\n", ar->array_ref, 1132 ar->num_drives); 1133 printf(" size = %ju\n", (uintmax_t)ar->size); 1134 for (j = 0; j < ar->num_drives; j++) { 1135 device_id = ar->pd[j].ref.v.device_id; 1136 if (device_id == 0xffff) 1137 printf(" drive MISSING\n"); 1138 else { 1139 printf(" drive %u %s\n", device_id, 1140 mfi_pdstate(ar->pd[j].fw_state)); 1141 if (mfi_pd_get_info(fd, device_id, &pinfo, 1142 NULL) >= 0) { 1143 printf(" raw size: %ju\n", 1144 (uintmax_t)pinfo.raw_size); 1145 printf(" non-coerced size: %ju\n", 1146 (uintmax_t)pinfo.non_coerced_size); 1147 printf(" coerced size: %ju\n", 1148 (uintmax_t)pinfo.coerced_size); 1149 } 1150 } 1151 } 1152 p += config->array_size; 1153 } 1154 1155 for (i = 0; i < config->log_drv_count; i++) { 1156 ld = (struct mfi_ld_config *)p; 1157 printf(" volume %s ", 1158 mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1159 printf("%s %s", 1160 mfi_raid_level(ld->params.primary_raid_level, 1161 ld->params.secondary_raid_level), 1162 mfi_ldstate(ld->params.state)); 1163 if (ld->properties.name[0] != '\0') 1164 printf(" <%s>", ld->properties.name); 1165 printf("\n"); 1166 printf(" primary raid level: %u\n", 1167 ld->params.primary_raid_level); 1168 printf(" raid level qualifier: %u\n", 1169 ld->params.raid_level_qualifier); 1170 printf(" secondary raid level: %u\n", 1171 ld->params.secondary_raid_level); 1172 printf(" stripe size: %u\n", ld->params.stripe_size); 1173 printf(" num drives: %u\n", ld->params.num_drives); 1174 printf(" init state: %u\n", ld->params.init_state); 1175 printf(" consistent: %u\n", ld->params.is_consistent); 1176 printf(" no bgi: %u\n", ld->properties.no_bgi); 1177 printf(" spans:\n"); 1178 for (j = 0; j < ld->params.span_depth; j++) { 1179 printf(" array %u @ ", ld->span[j].array_ref); 1180 printf("%ju : %ju\n", 1181 (uintmax_t)ld->span[j].start_block, 1182 (uintmax_t)ld->span[j].num_blocks); 1183 } 1184 p += config->log_drv_size; 1185 } 1186 1187 for (i = 0; i < config->spares_count; i++) { 1188 sp = (struct mfi_spare *)p; 1189 printf(" %s spare %u ", 1190 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1191 "global", sp->ref.v.device_id); 1192 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1193 printf(" backs:\n"); 1194 for (j = 0; j < sp->array_count; j++) 1195 printf(" array %u\n", sp->array_ref[j]); 1196 p += config->spares_size; 1197 } 1198} 1199 1200#ifdef DEBUG 1201static int 1202debug_config(int ac, char **av) 1203{ 1204 struct mfi_config_data *config; 1205 int error, fd; 1206 1207 if (ac != 1) { 1208 warnx("debug: extra arguments"); 1209 return (EINVAL); 1210 } 1211 1212 fd = mfi_open(mfi_unit, O_RDWR); 1213 if (fd < 0) { 1214 error = errno; 1215 warn("mfi_open"); 1216 return (error); 1217 } 1218 1219 /* Get the config from the controller. */ 1220 if (mfi_config_read(fd, &config) < 0) { 1221 error = errno; 1222 warn("Failed to get config"); 1223 close(fd); 1224 return (error); 1225 } 1226 1227 /* Dump out the configuration. */ 1228 dump_config(fd, config, NULL); 1229 free(config); 1230 close(fd); 1231 1232 return (0); 1233} 1234MFI_COMMAND(top, debug, debug_config); 1235 1236static int 1237dump(int ac, char **av) 1238{ 1239 struct mfi_config_data *config; 1240 char buf[64]; 1241 size_t len; 1242 int error, fd; 1243 1244 if (ac != 1) { 1245 warnx("dump: extra arguments"); 1246 return (EINVAL); 1247 } 1248 1249 fd = mfi_open(mfi_unit, O_RDWR); 1250 if (fd < 0) { 1251 error = errno; 1252 warn("mfi_open"); 1253 return (error); 1254 } 1255 1256 /* Get the stashed copy of the last dcmd from the driver. */ 1257 snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1258 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1259 error = errno; 1260 warn("Failed to read debug command"); 1261 if (error == ENOENT) 1262 error = EOPNOTSUPP; 1263 close(fd); 1264 return (error); 1265 } 1266 1267 config = malloc(len); 1268 if (config == NULL) { 1269 warnx("malloc failed"); 1270 close(fd); 1271 return (ENOMEM); 1272 } 1273 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1274 error = errno; 1275 warn("Failed to read debug command"); 1276 free(config); 1277 close(fd); 1278 return (error); 1279 } 1280 dump_config(fd, config, NULL); 1281 free(config); 1282 close(fd); 1283 1284 return (0); 1285} 1286MFI_COMMAND(top, dump, dump); 1287#endif 1288