mfi_config.c revision 237260
1189251Ssam/*- 2189251Ssam * Copyright (c) 2008, 2009 Yahoo!, Inc. 3189251Ssam * All rights reserved. 4189251Ssam * 5189251Ssam * Redistribution and use in source and binary forms, with or without 6189251Ssam * modification, are permitted provided that the following conditions 7189251Ssam * are met: 8189251Ssam * 1. Redistributions of source code must retain the above copyright 9189251Ssam * notice, this list of conditions and the following disclaimer. 10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 11189251Ssam * notice, this list of conditions and the following disclaimer in the 12189251Ssam * documentation and/or other materials provided with the distribution. 13189251Ssam * 3. The names of the authors may not be used to endorse or promote 14189251Ssam * products derived from this software without specific prior written 15189251Ssam * permission. 16189251Ssam * 17189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20189251Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27189251Ssam * SUCH DAMAGE. 28189251Ssam * 29189251Ssam * $FreeBSD: head/usr.sbin/mfiutil/mfi_config.c 237260 2012-06-19 06:18:42Z eadler $ 30189251Ssam */ 31189251Ssam 32189251Ssam#include <sys/param.h> 33189251Ssam#ifdef DEBUG 34189251Ssam#include <sys/sysctl.h> 35189251Ssam#endif 36189251Ssam#include <err.h> 37189251Ssam#include <errno.h> 38189251Ssam#include <fcntl.h> 39189251Ssam#include <libutil.h> 40189251Ssam#ifdef DEBUG 41189251Ssam#include <stdint.h> 42189251Ssam#endif 43189251Ssam#include <stdio.h> 44189251Ssam#include <stdlib.h> 45189251Ssam#include <string.h> 46189251Ssam#include <unistd.h> 47189251Ssam#include "mfiutil.h" 48189251Ssam 49189251Ssam#ifdef DEBUG 50189251Ssamstatic void dump_config(int fd, struct mfi_config_data *config); 51189251Ssam#endif 52189251Ssam 53189251Ssamstatic int add_spare(int ac, char **av); 54189251Ssamstatic int remove_spare(int ac, char **av); 55189251Ssam 56189251Ssamstatic long 57189251Ssamdehumanize(const char *value) 58189251Ssam{ 59189251Ssam char *vtp; 60189251Ssam long iv; 61189251Ssam 62189251Ssam if (value == NULL) 63189251Ssam return (0); 64189251Ssam iv = strtoq(value, &vtp, 0); 65189251Ssam if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 66189251Ssam return (0); 67189251Ssam } 68189251Ssam switch (vtp[0]) { 69189251Ssam case 't': case 'T': 70189251Ssam iv *= 1024; 71189251Ssam case 'g': case 'G': 72189251Ssam iv *= 1024; 73189251Ssam case 'm': case 'M': 74189251Ssam iv *= 1024; 75189251Ssam case 'k': case 'K': 76189251Ssam iv *= 1024; 77189251Ssam case '\0': 78189251Ssam break; 79189251Ssam default: 80189251Ssam return (0); 81189251Ssam } 82189251Ssam return (iv); 83189251Ssam} 84189251Ssamint 85189251Ssammfi_config_read(int fd, struct mfi_config_data **configp) 86189251Ssam{ 87189251Ssam struct mfi_config_data *config; 88189251Ssam uint32_t config_size; 89189251Ssam int error; 90189251Ssam 91189251Ssam /* 92189251Ssam * Keep fetching the config in a loop until we have a large enough 93189251Ssam * buffer to hold the entire configuration. 94189251Ssam */ 95189251Ssam config = NULL; 96189251Ssam config_size = 1024; 97189251Ssamfetch: 98189251Ssam config = reallocf(config, config_size); 99189251Ssam if (config == NULL) 100189251Ssam return (-1); 101189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config, 102189251Ssam config_size, NULL, 0, NULL) < 0) { 103189251Ssam error = errno; 104189251Ssam free(config); 105189251Ssam errno = error; 106189251Ssam return (-1); 107189251Ssam } 108189251Ssam 109189251Ssam if (config->size > config_size) { 110189251Ssam config_size = config->size; 111189251Ssam goto fetch; 112189251Ssam } 113189251Ssam 114189251Ssam *configp = config; 115189251Ssam return (0); 116189251Ssam} 117189251Ssam 118189251Ssamstatic struct mfi_array * 119189251Ssammfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 120209158Srpaulo{ 121209158Srpaulo struct mfi_array *ar; 122189251Ssam char *p; 123189251Ssam int i; 124189251Ssam 125189251Ssam p = (char *)config->array; 126189251Ssam for (i = 0; i < config->array_count; i++) { 127189251Ssam ar = (struct mfi_array *)p; 128189251Ssam if (ar->array_ref == array_ref) 129189251Ssam return (ar); 130189251Ssam p += config->array_size; 131189251Ssam } 132189251Ssam 133189251Ssam return (NULL); 134189251Ssam} 135189251Ssam 136189251Ssamstatic struct mfi_ld_config * 137189251Ssammfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 138189251Ssam{ 139189251Ssam struct mfi_ld_config *ld; 140189251Ssam char *p; 141189251Ssam int i; 142189251Ssam 143189251Ssam p = (char *)config->array + config->array_count * config->array_size; 144189251Ssam for (i = 0; i < config->log_drv_count; i++) { 145189251Ssam ld = (struct mfi_ld_config *)p; 146189251Ssam if (ld->properties.ld.v.target_id == target_id) 147189251Ssam return (ld); 148189251Ssam p += config->log_drv_size; 149189251Ssam } 150189251Ssam 151189251Ssam return (NULL); 152189251Ssam} 153189251Ssam 154189251Ssamstatic int 155189251Ssamclear_config(int ac __unused, char **av __unused) 156189251Ssam{ 157189251Ssam struct mfi_ld_list list; 158189251Ssam int ch, error, fd; 159189251Ssam u_int i; 160189251Ssam 161189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 162189251Ssam if (fd < 0) { 163189251Ssam error = errno; 164189251Ssam warn("mfi_open"); 165189251Ssam return (error); 166189251Ssam } 167189251Ssam 168189251Ssam if (!mfi_reconfig_supported()) { 169189251Ssam warnx("The current mfi(4) driver does not support " 170189251Ssam "configuration changes."); 171189251Ssam close(fd); 172189251Ssam return (EOPNOTSUPP); 173189251Ssam } 174189251Ssam 175189251Ssam if (mfi_ld_get_list(fd, &list, NULL) < 0) { 176189251Ssam error = errno; 177189251Ssam warn("Failed to get volume list"); 178189251Ssam close(fd); 179189251Ssam return (error); 180189251Ssam } 181189251Ssam 182189251Ssam for (i = 0; i < list.ld_count; i++) { 183209158Srpaulo if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 184189251Ssam warnx("Volume %s is busy and cannot be deleted", 185189251Ssam mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 186189251Ssam close(fd); 187189251Ssam return (EBUSY); 188189251Ssam } 189189251Ssam } 190189251Ssam 191189251Ssam printf( 192189251Ssam "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 193189251Ssam mfi_unit); 194189251Ssam ch = getchar(); 195189251Ssam if (ch != 'y' && ch != 'Y') { 196189251Ssam printf("\nAborting\n"); 197189251Ssam close(fd); 198189251Ssam return (0); 199189251Ssam } 200189251Ssam 201189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 202189251Ssam error = errno; 203189251Ssam warn("Failed to clear configuration"); 204189251Ssam close(fd); 205189251Ssam return (error); 206189251Ssam } 207189251Ssam 208189251Ssam printf("mfi%d: Configuration cleared\n", mfi_unit); 209189251Ssam close(fd); 210189251Ssam 211189251Ssam return (0); 212189251Ssam} 213189251SsamMFI_COMMAND(top, clear, clear_config); 214189251Ssam 215189251Ssam#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE 216189251Ssam#define MFI_ARRAY_SIZE sizeof(struct mfi_array) 217189251Ssam 218189251Ssam#define RT_RAID0 0 219189251Ssam#define RT_RAID1 1 220189251Ssam#define RT_RAID5 2 221189251Ssam#define RT_RAID6 3 222189251Ssam#define RT_JBOD 4 223189251Ssam#define RT_CONCAT 5 224189251Ssam#define RT_RAID10 6 225189251Ssam#define RT_RAID50 7 226189251Ssam#define RT_RAID60 8 227189251Ssam 228189251Ssamstatic int 229189251Ssamcompare_int(const void *one, const void *two) 230189251Ssam{ 231189251Ssam int first, second; 232189251Ssam 233189251Ssam first = *(const int *)one; 234189251Ssam second = *(const int *)two; 235189251Ssam 236189251Ssam return (first - second); 237189251Ssam} 238189251Ssam 239189251Ssamstatic struct raid_type_entry { 240189251Ssam const char *name; 241189251Ssam int raid_type; 242189251Ssam} raid_type_table[] = { 243189251Ssam { "raid0", RT_RAID0 }, 244189251Ssam { "raid-0", RT_RAID0 }, 245189251Ssam { "raid1", RT_RAID1 }, 246189251Ssam { "raid-1", RT_RAID1 }, 247189251Ssam { "mirror", RT_RAID1 }, 248189251Ssam { "raid5", RT_RAID5 }, 249189251Ssam { "raid-5", RT_RAID5 }, 250189251Ssam { "raid6", RT_RAID6 }, 251189251Ssam { "raid-6", RT_RAID6 }, 252189251Ssam { "jbod", RT_JBOD }, 253189251Ssam { "concat", RT_CONCAT }, 254189251Ssam { "raid10", RT_RAID10 }, 255189251Ssam { "raid1+0", RT_RAID10 }, 256189251Ssam { "raid-10", RT_RAID10 }, 257189251Ssam { "raid-1+0", RT_RAID10 }, 258189251Ssam { "raid50", RT_RAID50 }, 259189251Ssam { "raid5+0", RT_RAID50 }, 260189251Ssam { "raid-50", RT_RAID50 }, 261189251Ssam { "raid-5+0", RT_RAID50 }, 262189251Ssam { "raid60", RT_RAID60 }, 263189251Ssam { "raid6+0", RT_RAID60 }, 264189251Ssam { "raid-60", RT_RAID60 }, 265189251Ssam { "raid-6+0", RT_RAID60 }, 266189251Ssam { NULL, 0 }, 267189251Ssam}; 268189251Ssam 269189251Ssamstruct config_id_state { 270189251Ssam int array_count; 271189251Ssam int log_drv_count; 272189251Ssam int *arrays; 273189251Ssam int *volumes; 274189251Ssam uint16_t array_ref; 275189251Ssam uint8_t target_id; 276189251Ssam}; 277189251Ssam 278189251Ssamstruct array_info { 279189251Ssam int drive_count; 280189251Ssam struct mfi_pd_info *drives; 281189251Ssam struct mfi_array *array; 282189251Ssam}; 283189251Ssam 284189251Ssam/* Parse a comma-separated list of drives for an array. */ 285189251Ssamstatic int 286189251Ssamparse_array(int fd, int raid_type, char *array_str, struct array_info *info) 287189251Ssam{ 288189251Ssam struct mfi_pd_info *pinfo; 289189251Ssam uint16_t device_id; 290189251Ssam char *cp; 291189251Ssam u_int count; 292189251Ssam int error; 293189251Ssam 294189251Ssam cp = array_str; 295189251Ssam for (count = 0; cp != NULL; count++) { 296189251Ssam cp = strchr(cp, ','); 297189251Ssam if (cp != NULL) { 298189251Ssam cp++; 299189251Ssam if (*cp == ',') { 300189251Ssam warnx("Invalid drive list '%s'", array_str); 301189251Ssam return (EINVAL); 302189251Ssam } 303189251Ssam } 304189251Ssam } 305189251Ssam 306189251Ssam /* Validate the number of drives for this array. */ 307189251Ssam if (count >= MAX_DRIVES_PER_ARRAY) { 308189251Ssam warnx("Too many drives for a single array: max is %d", 309189251Ssam MAX_DRIVES_PER_ARRAY); 310189251Ssam return (EINVAL); 311189251Ssam } 312189251Ssam switch (raid_type) { 313189251Ssam case RT_RAID1: 314189251Ssam case RT_RAID10: 315189251Ssam if (count % 2 != 0) { 316189251Ssam warnx("RAID1 and RAID10 require an even number of " 317189251Ssam "drives in each array"); 318189251Ssam return (EINVAL); 319189251Ssam } 320189251Ssam break; 321189251Ssam case RT_RAID5: 322189251Ssam case RT_RAID50: 323189251Ssam if (count < 3) { 324189251Ssam warnx("RAID5 and RAID50 require at least 3 drives in " 325189251Ssam "each array"); 326189251Ssam return (EINVAL); 327189251Ssam } 328189251Ssam break; 329189251Ssam case RT_RAID6: 330189251Ssam case RT_RAID60: 331189251Ssam if (count < 4) { 332189251Ssam warnx("RAID6 and RAID60 require at least 4 drives in " 333189251Ssam "each array"); 334189251Ssam return (EINVAL); 335189251Ssam } 336189251Ssam break; 337189251Ssam } 338189251Ssam 339189251Ssam /* Validate each drive. */ 340189251Ssam info->drives = calloc(count, sizeof(struct mfi_pd_info)); 341189251Ssam if (info->drives == NULL) { 342189251Ssam warnx("malloc failed"); 343189251Ssam return (ENOMEM); 344189251Ssam } 345189251Ssam info->drive_count = count; 346189251Ssam for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 347189251Ssam pinfo++) { 348189251Ssam error = mfi_lookup_drive(fd, cp, &device_id); 349189251Ssam if (error) { 350189251Ssam free(info->drives); 351189251Ssam info->drives = NULL; 352189251Ssam return (error); 353189251Ssam } 354189251Ssam 355189251Ssam if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 356189251Ssam error = errno; 357189251Ssam warn("Failed to fetch drive info for drive %s", cp); 358189251Ssam free(info->drives); 359189251Ssam info->drives = NULL; 360189251Ssam return (error); 361189251Ssam } 362189251Ssam 363189251Ssam if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 364189251Ssam warnx("Drive %u is not available", device_id); 365189251Ssam free(info->drives); 366189251Ssam info->drives = NULL; 367189251Ssam return (EINVAL); 368189251Ssam } 369189251Ssam } 370189251Ssam 371189251Ssam return (0); 372189251Ssam} 373189251Ssam 374189251Ssam/* 375189251Ssam * Find the next free array ref assuming that 'array_ref' is the last 376189251Ssam * one used. 'array_ref' should be 0xffff for the initial test. 377189251Ssam */ 378189251Ssamstatic uint16_t 379189251Ssamfind_next_array(struct config_id_state *state) 380189251Ssam{ 381189251Ssam int i; 382189251Ssam 383189251Ssam /* Assume the current one is used. */ 384189251Ssam state->array_ref++; 385189251Ssam 386189251Ssam /* Find the next free one. */ 387189251Ssam for (i = 0; i < state->array_count; i++) 388189251Ssam if (state->arrays[i] == state->array_ref) 389189251Ssam state->array_ref++; 390189251Ssam return (state->array_ref); 391189251Ssam} 392189251Ssam 393189251Ssam/* 394189251Ssam * Find the next free volume ID assuming that 'target_id' is the last 395189251Ssam * one used. 'target_id' should be 0xff for the initial test. 396189251Ssam */ 397189251Ssamstatic uint8_t 398189251Ssamfind_next_volume(struct config_id_state *state) 399189251Ssam{ 400189251Ssam int i; 401189251Ssam 402189251Ssam /* Assume the current one is used. */ 403189251Ssam state->target_id++; 404189251Ssam 405189251Ssam /* Find the next free one. */ 406189251Ssam for (i = 0; i < state->log_drv_count; i++) 407189251Ssam if (state->volumes[i] == state->target_id) 408189251Ssam state->target_id++; 409189251Ssam return (state->target_id); 410189251Ssam} 411189251Ssam 412189251Ssam/* Populate an array with drives. */ 413189251Ssamstatic void 414189251Ssambuild_array(int fd __unused, char *arrayp, struct array_info *array_info, 415189251Ssam struct config_id_state *state, int verbose) 416189251Ssam{ 417189251Ssam struct mfi_array *ar = (struct mfi_array *)arrayp; 418189251Ssam int i; 419189251Ssam 420189251Ssam ar->size = array_info->drives[0].coerced_size; 421189251Ssam ar->num_drives = array_info->drive_count; 422189251Ssam ar->array_ref = find_next_array(state); 423189251Ssam for (i = 0; i < array_info->drive_count; i++) { 424189251Ssam if (verbose) 425189251Ssam printf("Adding drive %s to array %u\n", 426189251Ssam mfi_drive_name(NULL, 427189251Ssam array_info->drives[i].ref.v.device_id, 428189251Ssam MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS), 429189251Ssam ar->array_ref); 430189251Ssam if (ar->size > array_info->drives[i].coerced_size) 431189251Ssam ar->size = array_info->drives[i].coerced_size; 432189251Ssam ar->pd[i].ref = array_info->drives[i].ref; 433189251Ssam ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 434189251Ssam } 435189251Ssam array_info->array = ar; 436189251Ssam} 437189251Ssam 438189251Ssam/* 439189251Ssam * Create a volume that spans one or more arrays. 440189251Ssam */ 441189251Ssamstatic void 442189251Ssambuild_volume(char *volumep, int narrays, struct array_info *arrays, 443189251Ssam int raid_type, long stripe_size, struct config_id_state *state, int verbose) 444189251Ssam{ 445189251Ssam struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 446189251Ssam struct mfi_array *ar; 447189251Ssam int i; 448189251Ssam 449189251Ssam /* properties */ 450189251Ssam ld->properties.ld.v.target_id = find_next_volume(state); 451189251Ssam ld->properties.ld.v.seq = 0; 452189251Ssam ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 453189251Ssam MR_LD_CACHE_WRITE_BACK; 454189251Ssam ld->properties.access_policy = MFI_LD_ACCESS_RW; 455189251Ssam ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 456189251Ssam ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 457189251Ssam MR_LD_CACHE_WRITE_BACK; 458189251Ssam ld->properties.no_bgi = 0; 459189251Ssam 460189251Ssam /* params */ 461189251Ssam switch (raid_type) { 462189251Ssam case RT_RAID0: 463189251Ssam case RT_JBOD: 464189251Ssam ld->params.primary_raid_level = DDF_RAID0; 465189251Ssam ld->params.raid_level_qualifier = 0; 466189251Ssam ld->params.secondary_raid_level = 0; 467189251Ssam break; 468189251Ssam case RT_RAID1: 469189251Ssam ld->params.primary_raid_level = DDF_RAID1; 470189251Ssam ld->params.raid_level_qualifier = 0; 471189251Ssam ld->params.secondary_raid_level = 0; 472189251Ssam break; 473189251Ssam case RT_RAID5: 474189251Ssam ld->params.primary_raid_level = DDF_RAID5; 475189251Ssam ld->params.raid_level_qualifier = 3; 476189251Ssam ld->params.secondary_raid_level = 0; 477189251Ssam break; 478189251Ssam case RT_RAID6: 479189251Ssam ld->params.primary_raid_level = DDF_RAID6; 480189251Ssam ld->params.raid_level_qualifier = 3; 481189251Ssam ld->params.secondary_raid_level = 0; 482189251Ssam break; 483189251Ssam case RT_CONCAT: 484189251Ssam ld->params.primary_raid_level = DDF_CONCAT; 485189251Ssam ld->params.raid_level_qualifier = 0; 486189251Ssam ld->params.secondary_raid_level = 0; 487189251Ssam break; 488189251Ssam case RT_RAID10: 489189251Ssam ld->params.primary_raid_level = DDF_RAID1; 490189251Ssam ld->params.raid_level_qualifier = 0; 491189251Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 492189251Ssam break; 493189251Ssam case RT_RAID50: 494189251Ssam /* 495209158Srpaulo * XXX: This appears to work though the card's BIOS 496189251Ssam * complains that the configuration is foreign. The 497189251Ssam * BIOS setup does not allow for creation of RAID-50 498189251Ssam * or RAID-60 arrays. The only nested array 499189251Ssam * configuration it allows for is RAID-10. 500189251Ssam */ 501189251Ssam ld->params.primary_raid_level = DDF_RAID5; 502189251Ssam ld->params.raid_level_qualifier = 3; 503189251Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 504189251Ssam break; 505189251Ssam case RT_RAID60: 506189251Ssam ld->params.primary_raid_level = DDF_RAID6; 507189251Ssam ld->params.raid_level_qualifier = 3; 508189251Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 509189251Ssam break; 510189251Ssam } 511189251Ssam 512189251Ssam /* 513189251Ssam * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 514189251Ssam * ffs() to simulate log2(stripe_size). 515189251Ssam */ 516189251Ssam ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 517189251Ssam ld->params.num_drives = arrays[0].array->num_drives; 518189251Ssam ld->params.span_depth = narrays; 519189251Ssam ld->params.state = MFI_LD_STATE_OPTIMAL; 520189251Ssam ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 521189251Ssam ld->params.is_consistent = 0; 522189251Ssam 523189251Ssam /* spans */ 524189251Ssam for (i = 0; i < narrays; i++) { 525189251Ssam ar = arrays[i].array; 526189251Ssam if (verbose) 527189251Ssam printf("Adding array %u to volume %u\n", ar->array_ref, 528189251Ssam ld->properties.ld.v.target_id); 529189251Ssam ld->span[i].start_block = 0; 530189251Ssam ld->span[i].num_blocks = ar->size; 531189251Ssam ld->span[i].array_ref = ar->array_ref; 532189251Ssam } 533189251Ssam} 534189251Ssam 535189251Ssamstatic int 536189251Ssamcreate_volume(int ac, char **av) 537189251Ssam{ 538189251Ssam struct mfi_config_data *config; 539189251Ssam struct mfi_array *ar; 540189251Ssam struct mfi_ld_config *ld; 541189251Ssam struct config_id_state state; 542189251Ssam size_t config_size; 543189251Ssam char *p, *cfg_arrays, *cfg_volumes; 544189251Ssam int error, fd, i, raid_type; 545189251Ssam int narrays, nvolumes, arrays_per_volume; 546189251Ssam struct array_info *arrays; 547189251Ssam long stripe_size; 548189251Ssam#ifdef DEBUG 549189251Ssam int dump; 550189251Ssam#endif 551189251Ssam int ch, verbose; 552189251Ssam 553189251Ssam /* 554189251Ssam * Backwards compat. Map 'create volume' to 'create' and 555189251Ssam * 'create spare' to 'add'. 556189251Ssam */ 557189251Ssam if (ac > 1) { 558189251Ssam if (strcmp(av[1], "volume") == 0) { 559189251Ssam av++; 560189251Ssam ac--; 561189251Ssam } else if (strcmp(av[1], "spare") == 0) { 562189251Ssam av++; 563189251Ssam ac--; 564189251Ssam return (add_spare(ac, av)); 565189251Ssam } 566189251Ssam } 567189251Ssam 568189251Ssam if (ac < 2) { 569189251Ssam warnx("create volume: volume type required"); 570189251Ssam return (EINVAL); 571189251Ssam } 572189251Ssam 573189251Ssam bzero(&state, sizeof(state)); 574189251Ssam config = NULL; 575189251Ssam arrays = NULL; 576189251Ssam narrays = 0; 577189251Ssam error = 0; 578189251Ssam 579189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 580189251Ssam if (fd < 0) { 581189251Ssam error = errno; 582189251Ssam warn("mfi_open"); 583189251Ssam return (error); 584189251Ssam } 585189251Ssam 586189251Ssam if (!mfi_reconfig_supported()) { 587189251Ssam warnx("The current mfi(4) driver does not support " 588189251Ssam "configuration changes."); 589189251Ssam error = EOPNOTSUPP; 590189251Ssam goto error; 591189251Ssam } 592189251Ssam 593189251Ssam /* Lookup the RAID type first. */ 594189251Ssam raid_type = -1; 595189251Ssam for (i = 0; raid_type_table[i].name != NULL; i++) 596189251Ssam if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 597189251Ssam raid_type = raid_type_table[i].raid_type; 598189251Ssam break; 599189251Ssam } 600189251Ssam 601189251Ssam if (raid_type == -1) { 602189251Ssam warnx("Unknown or unsupported volume type %s", av[1]); 603189251Ssam error = EINVAL; 604189251Ssam goto error; 605189251Ssam } 606209158Srpaulo 607209158Srpaulo /* Parse any options. */ 608209158Srpaulo optind = 2; 609189251Ssam#ifdef DEBUG 610189251Ssam dump = 0; 611189251Ssam#endif 612189251Ssam verbose = 0; 613189251Ssam stripe_size = 64 * 1024; 614189251Ssam 615189251Ssam while ((ch = getopt(ac, av, "ds:v")) != -1) { 616189251Ssam switch (ch) { 617189251Ssam#ifdef DEBUG 618189251Ssam case 'd': 619189251Ssam dump = 1; 620189251Ssam break; 621189251Ssam#endif 622189251Ssam case 's': 623189251Ssam stripe_size = dehumanize(optarg); 624189251Ssam if ((stripe_size < 512) || (!powerof2(stripe_size))) 625189251Ssam stripe_size = 64 * 1024; 626189251Ssam break; 627189251Ssam case 'v': 628189251Ssam verbose = 1; 629189251Ssam break; 630189251Ssam case '?': 631189251Ssam default: 632189251Ssam error = EINVAL; 633189251Ssam goto error; 634189251Ssam } 635189251Ssam } 636189251Ssam ac -= optind; 637189251Ssam av += optind; 638189251Ssam 639189251Ssam /* Parse all the arrays. */ 640189251Ssam narrays = ac; 641189251Ssam if (narrays == 0) { 642189251Ssam warnx("At least one drive list is required"); 643189251Ssam error = EINVAL; 644189251Ssam goto error; 645189251Ssam } 646189251Ssam switch (raid_type) { 647189251Ssam case RT_RAID0: 648189251Ssam case RT_RAID1: 649189251Ssam case RT_RAID5: 650209158Srpaulo case RT_RAID6: 651189251Ssam case RT_CONCAT: 652189251Ssam if (narrays != 1) { 653189251Ssam warnx("Only one drive list can be specified"); 654189251Ssam error = EINVAL; 655189251Ssam goto error; 656189251Ssam } 657189251Ssam break; 658189251Ssam case RT_RAID10: 659189251Ssam case RT_RAID50: 660189251Ssam case RT_RAID60: 661189251Ssam if (narrays < 1) { 662189251Ssam warnx("RAID10, RAID50, and RAID60 require at least " 663189251Ssam "two drive lists"); 664189251Ssam error = EINVAL; 665189251Ssam goto error; 666189251Ssam } 667189251Ssam if (narrays > MFI_MAX_SPAN_DEPTH) { 668189251Ssam warnx("Volume spans more than %d arrays", 669209158Srpaulo MFI_MAX_SPAN_DEPTH); 670209158Srpaulo error = EINVAL; 671209158Srpaulo goto error; 672209158Srpaulo } 673209158Srpaulo break; 674209158Srpaulo } 675209158Srpaulo arrays = calloc(narrays, sizeof(*arrays)); 676189251Ssam if (arrays == NULL) { 677189251Ssam warnx("malloc failed"); 678189251Ssam error = ENOMEM; 679189251Ssam goto error; 680189251Ssam } 681189251Ssam for (i = 0; i < narrays; i++) { 682189251Ssam error = parse_array(fd, raid_type, av[i], &arrays[i]); 683189251Ssam if (error) 684189251Ssam goto error; 685189251Ssam } 686189251Ssam 687189251Ssam switch (raid_type) { 688189251Ssam case RT_RAID10: 689189251Ssam case RT_RAID50: 690209158Srpaulo case RT_RAID60: 691189251Ssam for (i = 1; i < narrays; i++) { 692189251Ssam if (arrays[i].drive_count != arrays[0].drive_count) { 693189251Ssam warnx("All arrays must contain the same " 694189251Ssam "number of drives"); 695189251Ssam error = EINVAL; 696189251Ssam goto error; 697189251Ssam } 698209158Srpaulo } 699209158Srpaulo break; 700189251Ssam } 701209158Srpaulo 702189251Ssam /* 703189251Ssam * Fetch the current config and build sorted lists of existing 704189251Ssam * array and volume identifiers. 705189251Ssam */ 706189251Ssam if (mfi_config_read(fd, &config) < 0) { 707209158Srpaulo error = errno; 708189251Ssam warn("Failed to read configuration"); 709189251Ssam goto error; 710189251Ssam } 711189251Ssam p = (char *)config->array; 712209158Srpaulo state.array_ref = 0xffff; 713209158Srpaulo state.target_id = 0xff; 714189251Ssam state.array_count = config->array_count; 715189251Ssam if (config->array_count > 0) { 716189251Ssam state.arrays = calloc(config->array_count, sizeof(int)); 717209158Srpaulo if (state.arrays == NULL) { 718189251Ssam warnx("malloc failed"); 719189251Ssam error = ENOMEM; 720189251Ssam goto error; 721189251Ssam } 722189251Ssam for (i = 0; i < config->array_count; i++) { 723189251Ssam ar = (struct mfi_array *)p; 724189251Ssam state.arrays[i] = ar->array_ref; 725189251Ssam p += config->array_size; 726189251Ssam } 727189251Ssam qsort(state.arrays, config->array_count, sizeof(int), 728189251Ssam compare_int); 729189251Ssam } else 730189251Ssam state.arrays = NULL; 731189251Ssam state.log_drv_count = config->log_drv_count; 732189251Ssam if (config->log_drv_count) { 733189251Ssam state.volumes = calloc(config->log_drv_count, sizeof(int)); 734189251Ssam if (state.volumes == NULL) { 735189251Ssam warnx("malloc failed"); 736189251Ssam error = ENOMEM; 737189251Ssam goto error; 738189251Ssam } 739189251Ssam for (i = 0; i < config->log_drv_count; i++) { 740189251Ssam ld = (struct mfi_ld_config *)p; 741189251Ssam state.volumes[i] = ld->properties.ld.v.target_id; 742189251Ssam p += config->log_drv_size; 743189251Ssam } 744189251Ssam qsort(state.volumes, config->log_drv_count, sizeof(int), 745189251Ssam compare_int); 746189251Ssam } else 747189251Ssam state.volumes = NULL; 748189251Ssam free(config); 749189251Ssam 750189251Ssam /* Determine the size of the configuration we will build. */ 751189251Ssam switch (raid_type) { 752189251Ssam case RT_RAID0: 753189251Ssam case RT_RAID1: 754189251Ssam case RT_RAID5: 755189251Ssam case RT_RAID6: 756189251Ssam case RT_CONCAT: 757189251Ssam case RT_JBOD: 758189251Ssam /* Each volume spans a single array. */ 759189251Ssam nvolumes = narrays; 760189251Ssam break; 761189251Ssam case RT_RAID10: 762189251Ssam case RT_RAID50: 763189251Ssam case RT_RAID60: 764189251Ssam /* A single volume spans multiple arrays. */ 765189251Ssam nvolumes = 1; 766189251Ssam break; 767189251Ssam default: 768189251Ssam /* Pacify gcc. */ 769189251Ssam abort(); 770189251Ssam } 771189251Ssam 772189251Ssam config_size = sizeof(struct mfi_config_data) + 773189251Ssam sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 774189251Ssam config = calloc(1, config_size); 775209158Srpaulo if (config == NULL) { 776209158Srpaulo warnx("malloc failed"); 777209158Srpaulo error = ENOMEM; 778209158Srpaulo goto error; 779209158Srpaulo } 780209158Srpaulo config->size = config_size; 781209158Srpaulo config->array_count = narrays; 782209158Srpaulo config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 783209158Srpaulo config->log_drv_count = nvolumes; 784209158Srpaulo config->log_drv_size = sizeof(struct mfi_ld_config); 785209158Srpaulo config->spares_count = 0; 786209158Srpaulo config->spares_size = 40; /* XXX: Firmware hardcode */ 787209158Srpaulo cfg_arrays = (char *)config->array; 788209158Srpaulo cfg_volumes = cfg_arrays + config->array_size * narrays; 789209158Srpaulo 790209158Srpaulo /* Build the arrays. */ 791189251Ssam for (i = 0; i < narrays; i++) { 792189251Ssam build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 793189251Ssam cfg_arrays += config->array_size; 794189251Ssam } 795189251Ssam 796189251Ssam /* Now build the volume(s). */ 797189251Ssam arrays_per_volume = narrays / nvolumes; 798189251Ssam for (i = 0; i < nvolumes; i++) { 799189251Ssam build_volume(cfg_volumes, arrays_per_volume, 800189251Ssam &arrays[i * arrays_per_volume], raid_type, stripe_size, 801189251Ssam &state, verbose); 802209158Srpaulo cfg_volumes += config->log_drv_size; 803209158Srpaulo } 804189251Ssam 805189251Ssam#ifdef DEBUG 806189251Ssam if (dump) 807189251Ssam dump_config(fd, config); 808189251Ssam#endif 809189251Ssam 810189251Ssam /* Send the new config to the controller. */ 811189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 812189251Ssam NULL, 0, NULL) < 0) { 813189251Ssam error = errno; 814189251Ssam warn("Failed to add volume"); 815189251Ssam /* FALLTHROUGH */ 816189251Ssam } 817189251Ssam 818189251Ssamerror: 819189251Ssam /* Clean up. */ 820189251Ssam free(config); 821189251Ssam free(state.volumes); 822189251Ssam free(state.arrays); 823189251Ssam if (arrays != NULL) { 824189251Ssam for (i = 0; i < narrays; i++) 825189251Ssam free(arrays[i].drives); 826189251Ssam free(arrays); 827189251Ssam } 828189251Ssam close(fd); 829189251Ssam 830189251Ssam return (error); 831189251Ssam} 832189251SsamMFI_COMMAND(top, create, create_volume); 833189251Ssam 834189251Ssamstatic int 835189251Ssamdelete_volume(int ac, char **av) 836189251Ssam{ 837189251Ssam struct mfi_ld_info info; 838189251Ssam int error, fd; 839189251Ssam uint8_t target_id, mbox[4]; 840189251Ssam 841189251Ssam /* 842189251Ssam * Backwards compat. Map 'delete volume' to 'delete' and 843189251Ssam * 'delete spare' to 'remove'. 844189251Ssam */ 845189251Ssam if (ac > 1) { 846189251Ssam if (strcmp(av[1], "volume") == 0) { 847189251Ssam av++; 848189251Ssam ac--; 849189251Ssam } else if (strcmp(av[1], "spare") == 0) { 850189251Ssam av++; 851189251Ssam ac--; 852189251Ssam return (remove_spare(ac, av)); 853189251Ssam } 854189251Ssam } 855189251Ssam 856189251Ssam if (ac != 2) { 857189251Ssam warnx("delete volume: volume required"); 858189251Ssam return (EINVAL); 859189251Ssam } 860189251Ssam 861189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 862189251Ssam if (fd < 0) { 863189251Ssam error = errno; 864189251Ssam warn("mfi_open"); 865189251Ssam return (error); 866189251Ssam } 867189251Ssam 868189251Ssam if (!mfi_reconfig_supported()) { 869189251Ssam warnx("The current mfi(4) driver does not support " 870189251Ssam "configuration changes."); 871189251Ssam close(fd); 872189251Ssam return (EOPNOTSUPP); 873189251Ssam } 874189251Ssam 875189251Ssam if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 876189251Ssam error = errno; 877189251Ssam warn("Invalid volume %s", av[1]); 878189251Ssam close(fd); 879189251Ssam return (error); 880189251Ssam } 881189251Ssam 882189251Ssam if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 883189251Ssam error = errno; 884189251Ssam warn("Failed to get info for volume %d", target_id); 885189251Ssam close(fd); 886189251Ssam return (error); 887189251Ssam } 888189251Ssam 889189251Ssam if (mfi_volume_busy(fd, target_id)) { 890189251Ssam warnx("Volume %s is busy and cannot be deleted", 891189251Ssam mfi_volume_name(fd, target_id)); 892189251Ssam close(fd); 893189251Ssam return (EBUSY); 894189251Ssam } 895189251Ssam 896189251Ssam mbox_store_ldref(mbox, &info.ld_config.properties.ld); 897189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 898189251Ssam sizeof(mbox), NULL) < 0) { 899189251Ssam error = errno; 900189251Ssam warn("Failed to delete volume"); 901189251Ssam close(fd); 902189251Ssam return (error); 903189251Ssam } 904189251Ssam 905189251Ssam close(fd); 906189251Ssam 907189251Ssam return (0); 908189251Ssam} 909189251SsamMFI_COMMAND(top, delete, delete_volume); 910189251Ssam 911189251Ssamstatic int 912189251Ssamadd_spare(int ac, char **av) 913189251Ssam{ 914189251Ssam struct mfi_pd_info info; 915189251Ssam struct mfi_config_data *config; 916189251Ssam struct mfi_array *ar; 917189251Ssam struct mfi_ld_config *ld; 918189251Ssam struct mfi_spare *spare; 919189251Ssam uint16_t device_id; 920189251Ssam uint8_t target_id; 921189251Ssam char *p; 922189251Ssam int error, fd, i; 923189251Ssam 924189251Ssam if (ac < 2) { 925189251Ssam warnx("add spare: drive required"); 926189251Ssam return (EINVAL); 927189251Ssam } 928189251Ssam 929189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 930189251Ssam if (fd < 0) { 931189251Ssam error = errno; 932189251Ssam warn("mfi_open"); 933189251Ssam return (error); 934189251Ssam } 935189251Ssam 936189251Ssam config = NULL; 937189251Ssam spare = NULL; 938189251Ssam error = mfi_lookup_drive(fd, av[1], &device_id); 939189251Ssam if (error) 940189251Ssam goto error; 941189251Ssam 942189251Ssam if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 943189251Ssam error = errno; 944189251Ssam warn("Failed to fetch drive info"); 945189251Ssam goto error; 946189251Ssam } 947189251Ssam 948189251Ssam if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 949189251Ssam warnx("Drive %u is not available", device_id); 950189251Ssam error = EINVAL; 951189251Ssam goto error; 952189251Ssam } 953189251Ssam 954189251Ssam if (ac > 2) { 955189251Ssam if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 956189251Ssam error = errno; 957189251Ssam warn("Invalid volume %s", av[2]); 958189251Ssam goto error; 959189251Ssam } 960189251Ssam } 961189251Ssam 962189251Ssam if (mfi_config_read(fd, &config) < 0) { 963189251Ssam error = errno; 964189251Ssam warn("Failed to read configuration"); 965189251Ssam goto error; 966189251Ssam } 967189251Ssam 968189251Ssam spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 969189251Ssam config->array_count); 970189251Ssam if (spare == NULL) { 971189251Ssam warnx("malloc failed"); 972189251Ssam error = ENOMEM; 973189251Ssam goto error; 974189251Ssam } 975189251Ssam bzero(spare, sizeof(struct mfi_spare)); 976189251Ssam spare->ref = info.ref; 977189251Ssam 978189251Ssam if (ac == 2) { 979189251Ssam /* Global spare backs all arrays. */ 980189251Ssam p = (char *)config->array; 981189251Ssam for (i = 0; i < config->array_count; i++) { 982189251Ssam ar = (struct mfi_array *)p; 983189251Ssam if (ar->size > info.coerced_size) { 984189251Ssam warnx("Spare isn't large enough for array %u", 985189251Ssam ar->array_ref); 986189251Ssam error = EINVAL; 987189251Ssam goto error; 988189251Ssam } 989189251Ssam p += config->array_size; 990189251Ssam } 991189251Ssam spare->array_count = 0; 992189251Ssam } else { 993189251Ssam /* 994189251Ssam * Dedicated spares only back the arrays for a 995189251Ssam * specific volume. 996189251Ssam */ 997189251Ssam ld = mfi_config_lookup_volume(config, target_id); 998189251Ssam if (ld == NULL) { 999189251Ssam warnx("Did not find volume %d", target_id); 1000189251Ssam error = EINVAL; 1001189251Ssam goto error; 1002189251Ssam } 1003189251Ssam 1004189251Ssam spare->spare_type |= MFI_SPARE_DEDICATED; 1005189251Ssam spare->array_count = ld->params.span_depth; 1006189251Ssam for (i = 0; i < ld->params.span_depth; i++) { 1007189251Ssam ar = mfi_config_lookup_array(config, 1008189251Ssam ld->span[i].array_ref); 1009189251Ssam if (ar == NULL) { 1010189251Ssam warnx("Missing array; inconsistent config?"); 1011189251Ssam error = ENXIO; 1012189251Ssam goto error; 1013189251Ssam } 1014189251Ssam if (ar->size > info.coerced_size) { 1015189251Ssam warnx("Spare isn't large enough for array %u", 1016189251Ssam ar->array_ref); 1017189251Ssam error = EINVAL; 1018189251Ssam goto error; 1019189251Ssam } 1020189251Ssam spare->array_ref[i] = ar->array_ref; 1021189251Ssam } 1022189251Ssam } 1023189251Ssam 1024189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 1025189251Ssam sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 1026189251Ssam NULL, 0, NULL) < 0) { 1027189251Ssam error = errno; 1028189251Ssam warn("Failed to assign spare"); 1029189251Ssam /* FALLTHROUGH. */ 1030189251Ssam } 1031189251Ssam 1032189251Ssamerror: 1033189251Ssam free(spare); 1034189251Ssam free(config); 1035189251Ssam close(fd); 1036189251Ssam 1037189251Ssam return (error); 1038189251Ssam} 1039189251SsamMFI_COMMAND(top, add, add_spare); 1040189251Ssam 1041189251Ssamstatic int 1042189251Ssamremove_spare(int ac, char **av) 1043189251Ssam{ 1044189251Ssam struct mfi_pd_info info; 1045189251Ssam int error, fd; 1046189251Ssam uint16_t device_id; 1047189251Ssam uint8_t mbox[4]; 1048189251Ssam 1049189251Ssam if (ac != 2) { 1050189251Ssam warnx("remove spare: drive required"); 1051189251Ssam return (EINVAL); 1052189251Ssam } 1053189251Ssam 1054189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 1055189251Ssam if (fd < 0) { 1056189251Ssam error = errno; 1057189251Ssam warn("mfi_open"); 1058189251Ssam return (error); 1059189251Ssam } 1060189251Ssam 1061189251Ssam error = mfi_lookup_drive(fd, av[1], &device_id); 1062189251Ssam if (error) { 1063189251Ssam close(fd); 1064189251Ssam return (error); 1065189251Ssam } 1066189251Ssam 1067189251Ssam /* Get the info for this drive. */ 1068189251Ssam if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 1069189251Ssam error = errno; 1070189251Ssam warn("Failed to fetch info for drive %u", device_id); 1071189251Ssam close(fd); 1072189251Ssam return (error); 1073189251Ssam } 1074189251Ssam 1075189251Ssam if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 1076189251Ssam warnx("Drive %u is not a hot spare", device_id); 1077189251Ssam close(fd); 1078189251Ssam return (EINVAL); 1079189251Ssam } 1080189251Ssam 1081189251Ssam mbox_store_pdref(mbox, &info.ref); 1082189251Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 1083189251Ssam sizeof(mbox), NULL) < 0) { 1084189251Ssam error = errno; 1085189251Ssam warn("Failed to delete spare"); 1086189251Ssam close(fd); 1087189251Ssam return (error); 1088189251Ssam } 1089189251Ssam 1090189251Ssam close(fd); 1091189251Ssam 1092189251Ssam return (0); 1093189251Ssam} 1094189251SsamMFI_COMMAND(top, remove, remove_spare); 1095189251Ssam 1096189251Ssam#ifdef DEBUG 1097189251Ssam/* Display raw data about a config. */ 1098189251Ssamstatic void 1099189251Ssamdump_config(int fd, struct mfi_config_data *config) 1100189251Ssam{ 1101189251Ssam struct mfi_array *ar; 1102189251Ssam struct mfi_ld_config *ld; 1103189251Ssam struct mfi_spare *sp; 1104189251Ssam struct mfi_pd_info pinfo; 1105189251Ssam uint16_t device_id; 1106189251Ssam char *p; 1107189251Ssam int i, j; 1108189251Ssam 1109189251Ssam printf( 1110189251Ssam "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n", 1111189251Ssam mfi_unit, config->array_count, config->log_drv_count, 1112189251Ssam config->spares_count); 1113189251Ssam printf(" array size: %u\n", config->array_size); 1114189251Ssam printf(" volume size: %u\n", config->log_drv_size); 1115189251Ssam printf(" spare size: %u\n", config->spares_size); 1116189251Ssam p = (char *)config->array; 1117189251Ssam 1118189251Ssam for (i = 0; i < config->array_count; i++) { 1119189251Ssam ar = (struct mfi_array *)p; 1120189251Ssam printf(" array %u of %u drives:\n", ar->array_ref, 1121189251Ssam ar->num_drives); 1122189251Ssam printf(" size = %ju\n", (uintmax_t)ar->size); 1123189251Ssam for (j = 0; j < ar->num_drives; j++) { 1124189251Ssam device_id = ar->pd[j].ref.v.device_id; 1125189251Ssam if (device_id == 0xffff) 1126189251Ssam printf(" drive MISSING\n"); 1127189251Ssam else { 1128189251Ssam printf(" drive %u %s\n", device_id, 1129189251Ssam mfi_pdstate(ar->pd[j].fw_state)); 1130189251Ssam if (mfi_pd_get_info(fd, device_id, &pinfo, 1131189251Ssam NULL) >= 0) { 1132189251Ssam printf(" raw size: %ju\n", 1133189251Ssam (uintmax_t)pinfo.raw_size); 1134189251Ssam printf(" non-coerced size: %ju\n", 1135189251Ssam (uintmax_t)pinfo.non_coerced_size); 1136189251Ssam printf(" coerced size: %ju\n", 1137189251Ssam (uintmax_t)pinfo.coerced_size); 1138189251Ssam } 1139189251Ssam } 1140189251Ssam } 1141189251Ssam p += config->array_size; 1142189251Ssam } 1143189251Ssam 1144189251Ssam for (i = 0; i < config->log_drv_count; i++) { 1145189251Ssam ld = (struct mfi_ld_config *)p; 1146189251Ssam printf(" volume %s ", 1147189251Ssam mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1148189251Ssam printf("%s %s", 1149189251Ssam mfi_raid_level(ld->params.primary_raid_level, 1150189251Ssam ld->params.secondary_raid_level), 1151189251Ssam mfi_ldstate(ld->params.state)); 1152189251Ssam if (ld->properties.name[0] != '\0') 1153189251Ssam printf(" <%s>", ld->properties.name); 1154189251Ssam printf("\n"); 1155189251Ssam printf(" primary raid level: %u\n", 1156189251Ssam ld->params.primary_raid_level); 1157189251Ssam printf(" raid level qualifier: %u\n", 1158189251Ssam ld->params.raid_level_qualifier); 1159189251Ssam printf(" secondary raid level: %u\n", 1160189251Ssam ld->params.secondary_raid_level); 1161189251Ssam printf(" stripe size: %u\n", ld->params.stripe_size); 1162189251Ssam printf(" num drives: %u\n", ld->params.num_drives); 1163189251Ssam printf(" init state: %u\n", ld->params.init_state); 1164189251Ssam printf(" consistent: %u\n", ld->params.is_consistent); 1165189251Ssam printf(" no bgi: %u\n", ld->properties.no_bgi); 1166189251Ssam printf(" spans:\n"); 1167189251Ssam for (j = 0; j < ld->params.span_depth; j++) { 1168189251Ssam printf(" array %u @ ", ld->span[j].array_ref); 1169189251Ssam printf("%ju : %ju\n", 1170189251Ssam (uintmax_t)ld->span[j].start_block, 1171189251Ssam (uintmax_t)ld->span[j].num_blocks); 1172189251Ssam } 1173189251Ssam p += config->log_drv_size; 1174189251Ssam } 1175189251Ssam 1176189251Ssam for (i = 0; i < config->spares_count; i++) { 1177189251Ssam sp = (struct mfi_spare *)p; 1178189251Ssam printf(" %s spare %u ", 1179189251Ssam sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1180189251Ssam "global", sp->ref.v.device_id); 1181189251Ssam printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1182189251Ssam printf(" backs:\n"); 1183189251Ssam for (j = 0; j < sp->array_count; j++) 1184189251Ssam printf(" array %u\n", sp->array_ref[j]); 1185189251Ssam p += config->spares_size; 1186189251Ssam } 1187189251Ssam} 1188189251Ssam 1189189251Ssamstatic int 1190189251Ssamdebug_config(int ac, char **av) 1191189251Ssam{ 1192189251Ssam struct mfi_config_data *config; 1193189251Ssam int error, fd; 1194189251Ssam 1195189251Ssam if (ac != 1) { 1196189251Ssam warnx("debug: extra arguments"); 1197189251Ssam return (EINVAL); 1198189251Ssam } 1199189251Ssam 1200189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 1201189251Ssam if (fd < 0) { 1202189251Ssam error = errno; 1203189251Ssam warn("mfi_open"); 1204189251Ssam return (error); 1205189251Ssam } 1206189251Ssam 1207189251Ssam /* Get the config from the controller. */ 1208189251Ssam if (mfi_config_read(fd, &config) < 0) { 1209189251Ssam error = errno; 1210189251Ssam warn("Failed to get config"); 1211189251Ssam close(fd); 1212189251Ssam return (error); 1213189251Ssam } 1214189251Ssam 1215189251Ssam /* Dump out the configuration. */ 1216189251Ssam dump_config(fd, config); 1217189251Ssam free(config); 1218189251Ssam close(fd); 1219189251Ssam 1220189251Ssam return (0); 1221189251Ssam} 1222189251SsamMFI_COMMAND(top, debug, debug_config); 1223189251Ssam 1224189251Ssamstatic int 1225189251Ssamdump(int ac, char **av) 1226189251Ssam{ 1227189251Ssam struct mfi_config_data *config; 1228189251Ssam char buf[64]; 1229189251Ssam size_t len; 1230189251Ssam int error, fd; 1231189251Ssam 1232189251Ssam if (ac != 1) { 1233189251Ssam warnx("dump: extra arguments"); 1234189251Ssam return (EINVAL); 1235189251Ssam } 1236189251Ssam 1237189251Ssam fd = mfi_open(mfi_unit, O_RDWR); 1238189251Ssam if (fd < 0) { 1239189251Ssam error = errno; 1240189251Ssam warn("mfi_open"); 1241189251Ssam return (error); 1242189251Ssam } 1243189251Ssam 1244189251Ssam /* Get the stashed copy of the last dcmd from the driver. */ 1245189251Ssam snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1246189251Ssam if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1247189251Ssam error = errno; 1248189251Ssam warn("Failed to read debug command"); 1249189251Ssam if (error == ENOENT) 1250189251Ssam error = EOPNOTSUPP; 1251189251Ssam close(fd); 1252189251Ssam return (error); 1253189251Ssam } 1254189251Ssam 1255189251Ssam config = malloc(len); 1256189251Ssam if (config == NULL) { 1257189251Ssam warnx("malloc failed"); 1258189251Ssam close(fd); 1259189251Ssam return (ENOMEM); 1260189251Ssam } 1261189251Ssam if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1262189251Ssam error = errno; 1263189251Ssam warn("Failed to read debug command"); 1264189251Ssam free(config); 1265189251Ssam close(fd); 1266189251Ssam return (error); 1267189251Ssam } 1268189251Ssam dump_config(fd, config); 1269189251Ssam free(config); 1270189251Ssam close(fd); 1271189251Ssam 1272189251Ssam return (0); 1273189251Ssam} 1274189251SsamMFI_COMMAND(top, dump, dump); 1275189251Ssam#endif 1276189251Ssam