mfi_config.c revision 196200
1196200Sscottl/*- 2196200Sscottl * Copyright (c) 2008, 2009 Yahoo!, Inc. 3196200Sscottl * All rights reserved. 4196200Sscottl * 5196200Sscottl * Redistribution and use in source and binary forms, with or without 6196200Sscottl * modification, are permitted provided that the following conditions 7196200Sscottl * are met: 8196200Sscottl * 1. Redistributions of source code must retain the above copyright 9196200Sscottl * notice, this list of conditions and the following disclaimer. 10196200Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11196200Sscottl * notice, this list of conditions and the following disclaimer in the 12196200Sscottl * documentation and/or other materials provided with the distribution. 13196200Sscottl * 3. The names of the authors may not be used to endorse or promote 14196200Sscottl * products derived from this software without specific prior written 15196200Sscottl * permission. 16196200Sscottl * 17196200Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18196200Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19196200Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20196200Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21196200Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22196200Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23196200Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24196200Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25196200Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26196200Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27196200Sscottl * SUCH DAMAGE. 28196200Sscottl * 29196200Sscottl * $FreeBSD: head/usr.sbin/mfiutil/mfi_config.c 196200 2009-08-13 23:18:45Z scottl $ 30196200Sscottl */ 31196200Sscottl 32196200Sscottl#include <sys/types.h> 33196200Sscottl#ifdef DEBUG 34196200Sscottl#include <sys/sysctl.h> 35196200Sscottl#endif 36196200Sscottl#include <sys/errno.h> 37196200Sscottl#include <err.h> 38196200Sscottl#include <libutil.h> 39196200Sscottl#ifdef DEBUG 40196200Sscottl#include <stdint.h> 41196200Sscottl#endif 42196200Sscottl#include <stdio.h> 43196200Sscottl#include <stdlib.h> 44196200Sscottl#include <string.h> 45196200Sscottl#include <unistd.h> 46196200Sscottl#include "mfiutil.h" 47196200Sscottl 48196200Sscottl#ifdef DEBUG 49196200Sscottlstatic void dump_config(int fd, struct mfi_config_data *config); 50196200Sscottl#endif 51196200Sscottl 52196200Sscottlstatic int add_spare(int ac, char **av); 53196200Sscottlstatic int remove_spare(int ac, char **av); 54196200Sscottl 55196200Sscottl#define powerof2(x) ((((x)-1)&(x))==0) 56196200Sscottl 57196200Sscottlstatic long 58196200Sscottldehumanize(const char *value) 59196200Sscottl{ 60196200Sscottl char *vtp; 61196200Sscottl long iv; 62196200Sscottl 63196200Sscottl if (value == NULL) 64196200Sscottl return (0); 65196200Sscottl iv = strtoq(value, &vtp, 0); 66196200Sscottl if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 67196200Sscottl return (0); 68196200Sscottl } 69196200Sscottl switch (vtp[0]) { 70196200Sscottl case 't': case 'T': 71196200Sscottl iv *= 1024; 72196200Sscottl case 'g': case 'G': 73196200Sscottl iv *= 1024; 74196200Sscottl case 'm': case 'M': 75196200Sscottl iv *= 1024; 76196200Sscottl case 'k': case 'K': 77196200Sscottl iv *= 1024; 78196200Sscottl case '\0': 79196200Sscottl break; 80196200Sscottl default: 81196200Sscottl return (0); 82196200Sscottl } 83196200Sscottl return (iv); 84196200Sscottl} 85196200Sscottlint 86196200Sscottlmfi_config_read(int fd, struct mfi_config_data **configp) 87196200Sscottl{ 88196200Sscottl struct mfi_config_data *config; 89196200Sscottl uint32_t config_size; 90196200Sscottl 91196200Sscottl /* 92196200Sscottl * Keep fetching the config in a loop until we have a large enough 93196200Sscottl * buffer to hold the entire configuration. 94196200Sscottl */ 95196200Sscottl config = NULL; 96196200Sscottl config_size = 1024; 97196200Sscottlfetch: 98196200Sscottl config = reallocf(config, config_size); 99196200Sscottl if (config == NULL) 100196200Sscottl return (-1); 101196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config, 102196200Sscottl config_size, NULL, 0, NULL) < 0) 103196200Sscottl return (-1); 104196200Sscottl 105196200Sscottl if (config->size > config_size) { 106196200Sscottl config_size = config->size; 107196200Sscottl goto fetch; 108196200Sscottl } 109196200Sscottl 110196200Sscottl *configp = config; 111196200Sscottl return (0); 112196200Sscottl} 113196200Sscottl 114196200Sscottlstatic struct mfi_array * 115196200Sscottlmfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 116196200Sscottl{ 117196200Sscottl struct mfi_array *ar; 118196200Sscottl char *p; 119196200Sscottl int i; 120196200Sscottl 121196200Sscottl p = (char *)config->array; 122196200Sscottl for (i = 0; i < config->array_count; i++) { 123196200Sscottl ar = (struct mfi_array *)p; 124196200Sscottl if (ar->array_ref == array_ref) 125196200Sscottl return (ar); 126196200Sscottl p += config->array_size; 127196200Sscottl } 128196200Sscottl 129196200Sscottl return (NULL); 130196200Sscottl} 131196200Sscottl 132196200Sscottlstatic struct mfi_ld_config * 133196200Sscottlmfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 134196200Sscottl{ 135196200Sscottl struct mfi_ld_config *ld; 136196200Sscottl char *p; 137196200Sscottl int i; 138196200Sscottl 139196200Sscottl p = (char *)config->array + config->array_count * config->array_size; 140196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 141196200Sscottl ld = (struct mfi_ld_config *)p; 142196200Sscottl if (ld->properties.ld.v.target_id == target_id) 143196200Sscottl return (ld); 144196200Sscottl p += config->log_drv_size; 145196200Sscottl } 146196200Sscottl 147196200Sscottl return (NULL); 148196200Sscottl} 149196200Sscottl 150196200Sscottlstatic int 151196200Sscottlclear_config(int ac, char **av) 152196200Sscottl{ 153196200Sscottl struct mfi_ld_list list; 154196200Sscottl int ch, fd; 155196200Sscottl u_int i; 156196200Sscottl 157196200Sscottl fd = mfi_open(mfi_unit); 158196200Sscottl if (fd < 0) { 159196200Sscottl warn("mfi_open"); 160196200Sscottl return (errno); 161196200Sscottl } 162196200Sscottl 163196200Sscottl if (!mfi_reconfig_supported()) { 164196200Sscottl warnx("The current mfi(4) driver does not support " 165196200Sscottl "configuration changes."); 166196200Sscottl return (EOPNOTSUPP); 167196200Sscottl } 168196200Sscottl 169196200Sscottl if (mfi_ld_get_list(fd, &list, NULL) < 0) { 170196200Sscottl warn("Failed to get volume list"); 171196200Sscottl return (errno); 172196200Sscottl } 173196200Sscottl 174196200Sscottl for (i = 0; i < list.ld_count; i++) { 175196200Sscottl if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 176196200Sscottl warnx("Volume %s is busy and cannot be deleted", 177196200Sscottl mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 178196200Sscottl return (EBUSY); 179196200Sscottl } 180196200Sscottl } 181196200Sscottl 182196200Sscottl printf( 183196200Sscottl "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 184196200Sscottl mfi_unit); 185196200Sscottl ch = getchar(); 186196200Sscottl if (ch != 'y' && ch != 'Y') { 187196200Sscottl printf("\nAborting\n"); 188196200Sscottl return (0); 189196200Sscottl } 190196200Sscottl 191196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 192196200Sscottl warn("Failed to clear configuration"); 193196200Sscottl return (errno); 194196200Sscottl } 195196200Sscottl 196196200Sscottl printf("mfi%d: Configuration cleared\n", mfi_unit); 197196200Sscottl close(fd); 198196200Sscottl 199196200Sscottl return (0); 200196200Sscottl} 201196200SscottlMFI_COMMAND(top, clear, clear_config); 202196200Sscottl 203196200Sscottl#define MFI_ARRAY_SIZE 288 204196200Sscottl#define MAX_DRIVES_PER_ARRAY \ 205196200Sscottl ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8) 206196200Sscottl 207196200Sscottl#define RT_RAID0 0 208196200Sscottl#define RT_RAID1 1 209196200Sscottl#define RT_RAID5 2 210196200Sscottl#define RT_RAID6 3 211196200Sscottl#define RT_JBOD 4 212196200Sscottl#define RT_CONCAT 5 213196200Sscottl#define RT_RAID10 6 214196200Sscottl#define RT_RAID50 7 215196200Sscottl#define RT_RAID60 8 216196200Sscottl 217196200Sscottlstatic int 218196200Sscottlcompare_int(const void *one, const void *two) 219196200Sscottl{ 220196200Sscottl int first, second; 221196200Sscottl 222196200Sscottl first = *(const int *)one; 223196200Sscottl second = *(const int *)two; 224196200Sscottl 225196200Sscottl return (first - second); 226196200Sscottl} 227196200Sscottl 228196200Sscottlstatic struct raid_type_entry { 229196200Sscottl const char *name; 230196200Sscottl int raid_type; 231196200Sscottl} raid_type_table[] = { 232196200Sscottl { "raid0", RT_RAID0 }, 233196200Sscottl { "raid-0", RT_RAID0 }, 234196200Sscottl { "raid1", RT_RAID1 }, 235196200Sscottl { "raid-1", RT_RAID1 }, 236196200Sscottl { "mirror", RT_RAID1 }, 237196200Sscottl { "raid5", RT_RAID5 }, 238196200Sscottl { "raid-5", RT_RAID5 }, 239196200Sscottl { "raid6", RT_RAID6 }, 240196200Sscottl { "raid-6", RT_RAID6 }, 241196200Sscottl { "jbod", RT_JBOD }, 242196200Sscottl { "concat", RT_CONCAT }, 243196200Sscottl { "raid10", RT_RAID10 }, 244196200Sscottl { "raid1+0", RT_RAID10 }, 245196200Sscottl { "raid-10", RT_RAID10 }, 246196200Sscottl { "raid-1+0", RT_RAID10 }, 247196200Sscottl { "raid50", RT_RAID50 }, 248196200Sscottl { "raid5+0", RT_RAID50 }, 249196200Sscottl { "raid-50", RT_RAID50 }, 250196200Sscottl { "raid-5+0", RT_RAID50 }, 251196200Sscottl { "raid60", RT_RAID60 }, 252196200Sscottl { "raid6+0", RT_RAID60 }, 253196200Sscottl { "raid-60", RT_RAID60 }, 254196200Sscottl { "raid-6+0", RT_RAID60 }, 255196200Sscottl { NULL, 0 }, 256196200Sscottl}; 257196200Sscottl 258196200Sscottlstruct config_id_state { 259196200Sscottl int array_count; 260196200Sscottl int log_drv_count; 261196200Sscottl int *arrays; 262196200Sscottl int *volumes; 263196200Sscottl uint16_t array_ref; 264196200Sscottl uint8_t target_id; 265196200Sscottl}; 266196200Sscottl 267196200Sscottlstruct array_info { 268196200Sscottl int drive_count; 269196200Sscottl struct mfi_pd_info *drives; 270196200Sscottl struct mfi_array *array; 271196200Sscottl}; 272196200Sscottl 273196200Sscottl/* Parse a comma-separated list of drives for an array. */ 274196200Sscottlstatic int 275196200Sscottlparse_array(int fd, int raid_type, char *array_str, struct array_info *info) 276196200Sscottl{ 277196200Sscottl struct mfi_pd_info *pinfo; 278196200Sscottl uint16_t device_id; 279196200Sscottl char *cp; 280196200Sscottl u_int count; 281196200Sscottl int error; 282196200Sscottl 283196200Sscottl cp = array_str; 284196200Sscottl for (count = 0; cp != NULL; count++) { 285196200Sscottl cp = strchr(cp, ','); 286196200Sscottl if (cp != NULL) { 287196200Sscottl cp++; 288196200Sscottl if (*cp == ',') { 289196200Sscottl warnx("Invalid drive list '%s'", array_str); 290196200Sscottl return (EINVAL); 291196200Sscottl } 292196200Sscottl } 293196200Sscottl } 294196200Sscottl 295196200Sscottl /* Validate the number of drives for this array. */ 296196200Sscottl if (count >= MAX_DRIVES_PER_ARRAY) { 297196200Sscottl warnx("Too many drives for a single array: max is %zu", 298196200Sscottl MAX_DRIVES_PER_ARRAY); 299196200Sscottl return (EINVAL); 300196200Sscottl } 301196200Sscottl switch (raid_type) { 302196200Sscottl case RT_RAID1: 303196200Sscottl case RT_RAID10: 304196200Sscottl if (count % 2 != 0) { 305196200Sscottl warnx("RAID1 and RAID10 require an even number of " 306196200Sscottl "drives in each array"); 307196200Sscottl return (EINVAL); 308196200Sscottl } 309196200Sscottl break; 310196200Sscottl case RT_RAID5: 311196200Sscottl case RT_RAID50: 312196200Sscottl if (count < 3) { 313196200Sscottl warnx("RAID5 and RAID50 require at least 3 drives in " 314196200Sscottl "each array"); 315196200Sscottl return (EINVAL); 316196200Sscottl } 317196200Sscottl break; 318196200Sscottl case RT_RAID6: 319196200Sscottl case RT_RAID60: 320196200Sscottl if (count < 4) { 321196200Sscottl warnx("RAID6 and RAID60 require at least 4 drives in " 322196200Sscottl "each array"); 323196200Sscottl return (EINVAL); 324196200Sscottl } 325196200Sscottl break; 326196200Sscottl } 327196200Sscottl 328196200Sscottl /* Validate each drive. */ 329196200Sscottl info->drives = calloc(count, sizeof(struct mfi_pd_info)); 330196200Sscottl info->drive_count = count; 331196200Sscottl for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 332196200Sscottl pinfo++) { 333196200Sscottl error = mfi_lookup_drive(fd, cp, &device_id); 334196200Sscottl if (error) 335196200Sscottl return (error); 336196200Sscottl 337196200Sscottl if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 338196200Sscottl warn("Failed to fetch drive info for drive %s", cp); 339196200Sscottl return (errno); 340196200Sscottl } 341196200Sscottl 342196200Sscottl if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 343196200Sscottl warnx("Drive %u is not available", device_id); 344196200Sscottl return (EINVAL); 345196200Sscottl } 346196200Sscottl } 347196200Sscottl 348196200Sscottl return (0); 349196200Sscottl} 350196200Sscottl 351196200Sscottl/* 352196200Sscottl * Find the next free array ref assuming that 'array_ref' is the last 353196200Sscottl * one used. 'array_ref' should be 0xffff for the initial test. 354196200Sscottl */ 355196200Sscottlstatic uint16_t 356196200Sscottlfind_next_array(struct config_id_state *state) 357196200Sscottl{ 358196200Sscottl int i; 359196200Sscottl 360196200Sscottl /* Assume the current one is used. */ 361196200Sscottl state->array_ref++; 362196200Sscottl 363196200Sscottl /* Find the next free one. */ 364196200Sscottl for (i = 0; i < state->array_count; i++) 365196200Sscottl if (state->arrays[i] == state->array_ref) 366196200Sscottl state->array_ref++; 367196200Sscottl return (state->array_ref); 368196200Sscottl} 369196200Sscottl 370196200Sscottl/* 371196200Sscottl * Find the next free volume ID assuming that 'target_id' is the last 372196200Sscottl * one used. 'target_id' should be 0xff for the initial test. 373196200Sscottl */ 374196200Sscottlstatic uint8_t 375196200Sscottlfind_next_volume(struct config_id_state *state) 376196200Sscottl{ 377196200Sscottl int i; 378196200Sscottl 379196200Sscottl /* Assume the current one is used. */ 380196200Sscottl state->target_id++; 381196200Sscottl 382196200Sscottl /* Find the next free one. */ 383196200Sscottl for (i = 0; i < state->log_drv_count; i++) 384196200Sscottl if (state->volumes[i] == state->target_id) 385196200Sscottl state->target_id++; 386196200Sscottl return (state->target_id); 387196200Sscottl} 388196200Sscottl 389196200Sscottl/* Populate an array with drives. */ 390196200Sscottlstatic void 391196200Sscottlbuild_array(int fd, char *arrayp, struct array_info *array_info, 392196200Sscottl struct config_id_state *state, int verbose) 393196200Sscottl{ 394196200Sscottl struct mfi_array *ar = (struct mfi_array *)arrayp; 395196200Sscottl int i; 396196200Sscottl 397196200Sscottl ar->size = array_info->drives[0].coerced_size; 398196200Sscottl ar->num_drives = array_info->drive_count; 399196200Sscottl ar->array_ref = find_next_array(state); 400196200Sscottl for (i = 0; i < array_info->drive_count; i++) { 401196200Sscottl if (verbose) 402196200Sscottl printf("Adding drive %u to array %u\n", 403196200Sscottl array_info->drives[i].ref.v.device_id, 404196200Sscottl ar->array_ref); 405196200Sscottl if (ar->size > array_info->drives[i].coerced_size) 406196200Sscottl ar->size = array_info->drives[i].coerced_size; 407196200Sscottl ar->pd[i].ref = array_info->drives[i].ref; 408196200Sscottl ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 409196200Sscottl } 410196200Sscottl array_info->array = ar; 411196200Sscottl} 412196200Sscottl 413196200Sscottl/* 414196200Sscottl * Create a volume that spans one or more arrays. 415196200Sscottl */ 416196200Sscottlstatic void 417196200Sscottlbuild_volume(char *volumep, int narrays, struct array_info *arrays, 418196200Sscottl int raid_type, long stripe_size, struct config_id_state *state, int verbose) 419196200Sscottl{ 420196200Sscottl struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 421196200Sscottl struct mfi_array *ar; 422196200Sscottl int i; 423196200Sscottl 424196200Sscottl /* properties */ 425196200Sscottl ld->properties.ld.v.target_id = find_next_volume(state); 426196200Sscottl ld->properties.ld.v.seq = 0; 427196200Sscottl ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 428196200Sscottl MR_LD_CACHE_WRITE_BACK; 429196200Sscottl ld->properties.access_policy = MFI_LD_ACCESS_RW; 430196200Sscottl ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 431196200Sscottl ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 432196200Sscottl MR_LD_CACHE_WRITE_BACK; 433196200Sscottl ld->properties.no_bgi = 0; 434196200Sscottl 435196200Sscottl /* params */ 436196200Sscottl switch (raid_type) { 437196200Sscottl case RT_RAID0: 438196200Sscottl case RT_JBOD: 439196200Sscottl ld->params.primary_raid_level = DDF_RAID0; 440196200Sscottl ld->params.raid_level_qualifier = 0; 441196200Sscottl ld->params.secondary_raid_level = 0; 442196200Sscottl break; 443196200Sscottl case RT_RAID1: 444196200Sscottl ld->params.primary_raid_level = DDF_RAID1; 445196200Sscottl ld->params.raid_level_qualifier = 0; 446196200Sscottl ld->params.secondary_raid_level = 0; 447196200Sscottl break; 448196200Sscottl case RT_RAID5: 449196200Sscottl ld->params.primary_raid_level = DDF_RAID5; 450196200Sscottl ld->params.raid_level_qualifier = 3; 451196200Sscottl ld->params.secondary_raid_level = 0; 452196200Sscottl break; 453196200Sscottl case RT_RAID6: 454196200Sscottl ld->params.primary_raid_level = DDF_RAID6; 455196200Sscottl ld->params.raid_level_qualifier = 3; 456196200Sscottl ld->params.secondary_raid_level = 0; 457196200Sscottl break; 458196200Sscottl case RT_CONCAT: 459196200Sscottl ld->params.primary_raid_level = DDF_CONCAT; 460196200Sscottl ld->params.raid_level_qualifier = 0; 461196200Sscottl ld->params.secondary_raid_level = 0; 462196200Sscottl break; 463196200Sscottl case RT_RAID10: 464196200Sscottl ld->params.primary_raid_level = DDF_RAID1; 465196200Sscottl ld->params.raid_level_qualifier = 0; 466196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 467196200Sscottl break; 468196200Sscottl case RT_RAID50: 469196200Sscottl /* 470196200Sscottl * XXX: This appears to work though the card's BIOS 471196200Sscottl * complains that the configuration is foreign. The 472196200Sscottl * BIOS setup does not allow for creation of RAID-50 473196200Sscottl * or RAID-60 arrays. The only nested array 474196200Sscottl * configuration it allows for is RAID-10. 475196200Sscottl */ 476196200Sscottl ld->params.primary_raid_level = DDF_RAID5; 477196200Sscottl ld->params.raid_level_qualifier = 3; 478196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 479196200Sscottl break; 480196200Sscottl case RT_RAID60: 481196200Sscottl ld->params.primary_raid_level = DDF_RAID6; 482196200Sscottl ld->params.raid_level_qualifier = 3; 483196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 484196200Sscottl break; 485196200Sscottl } 486196200Sscottl 487196200Sscottl /* 488196200Sscottl * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 489196200Sscottl * ffs() to simulate log2(stripe_size). 490196200Sscottl */ 491196200Sscottl ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 492196200Sscottl ld->params.num_drives = arrays[0].array->num_drives; 493196200Sscottl ld->params.span_depth = narrays; 494196200Sscottl ld->params.state = MFI_LD_STATE_OPTIMAL; 495196200Sscottl ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 496196200Sscottl ld->params.is_consistent = 0; 497196200Sscottl 498196200Sscottl /* spans */ 499196200Sscottl for (i = 0; i < narrays; i++) { 500196200Sscottl ar = arrays[i].array; 501196200Sscottl if (verbose) 502196200Sscottl printf("Adding array %u to volume %u\n", ar->array_ref, 503196200Sscottl ld->properties.ld.v.target_id); 504196200Sscottl ld->span[i].start_block = 0; 505196200Sscottl ld->span[i].num_blocks = ar->size; 506196200Sscottl ld->span[i].array_ref = ar->array_ref; 507196200Sscottl } 508196200Sscottl} 509196200Sscottl 510196200Sscottlstatic int 511196200Sscottlcreate_volume(int ac, char **av) 512196200Sscottl{ 513196200Sscottl struct mfi_config_data *config; 514196200Sscottl struct mfi_array *ar; 515196200Sscottl struct mfi_ld_config *ld; 516196200Sscottl struct config_id_state state; 517196200Sscottl size_t config_size; 518196200Sscottl char *p, *cfg_arrays, *cfg_volumes; 519196200Sscottl int error, fd, i, raid_type; 520196200Sscottl int narrays, nvolumes, arrays_per_volume; 521196200Sscottl struct array_info *arrays; 522196200Sscottl long stripe_size; 523196200Sscottl#ifdef DEBUG 524196200Sscottl int dump; 525196200Sscottl#endif 526196200Sscottl int ch, verbose; 527196200Sscottl 528196200Sscottl /* 529196200Sscottl * Backwards compat. Map 'create volume' to 'create' and 530196200Sscottl * 'create spare' to 'add'. 531196200Sscottl */ 532196200Sscottl if (ac > 1) { 533196200Sscottl if (strcmp(av[1], "volume") == 0) { 534196200Sscottl av++; 535196200Sscottl ac--; 536196200Sscottl } else if (strcmp(av[1], "spare") == 0) { 537196200Sscottl av++; 538196200Sscottl ac--; 539196200Sscottl return (add_spare(ac, av)); 540196200Sscottl } 541196200Sscottl } 542196200Sscottl 543196200Sscottl if (ac < 2) { 544196200Sscottl warnx("create volume: volume type required"); 545196200Sscottl return (EINVAL); 546196200Sscottl } 547196200Sscottl 548196200Sscottl 549196200Sscottl fd = mfi_open(mfi_unit); 550196200Sscottl if (fd < 0) { 551196200Sscottl warn("mfi_open"); 552196200Sscottl return (errno); 553196200Sscottl } 554196200Sscottl 555196200Sscottl if (!mfi_reconfig_supported()) { 556196200Sscottl warnx("The current mfi(4) driver does not support " 557196200Sscottl "configuration changes."); 558196200Sscottl return (EOPNOTSUPP); 559196200Sscottl } 560196200Sscottl 561196200Sscottl /* Lookup the RAID type first. */ 562196200Sscottl raid_type = -1; 563196200Sscottl for (i = 0; raid_type_table[i].name != NULL; i++) 564196200Sscottl if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 565196200Sscottl raid_type = raid_type_table[i].raid_type; 566196200Sscottl break; 567196200Sscottl } 568196200Sscottl 569196200Sscottl if (raid_type == -1) { 570196200Sscottl warnx("Unknown or unsupported volume type %s", av[1]); 571196200Sscottl return (EINVAL); 572196200Sscottl } 573196200Sscottl 574196200Sscottl /* Parse any options. */ 575196200Sscottl optind = 2; 576196200Sscottl#ifdef DEBUG 577196200Sscottl dump = 0; 578196200Sscottl#endif 579196200Sscottl verbose = 0; 580196200Sscottl stripe_size = 64 * 1024; 581196200Sscottl 582196200Sscottl while ((ch = getopt(ac, av, "ds:v")) != -1) { 583196200Sscottl switch (ch) { 584196200Sscottl#ifdef DEBUG 585196200Sscottl case 'd': 586196200Sscottl dump = 1; 587196200Sscottl break; 588196200Sscottl#endif 589196200Sscottl case 's': 590196200Sscottl stripe_size = dehumanize(optarg); 591196200Sscottl if ((stripe_size < 512) || (!powerof2(stripe_size))) 592196200Sscottl stripe_size = 64 * 1024; 593196200Sscottl break; 594196200Sscottl case 'v': 595196200Sscottl verbose = 1; 596196200Sscottl break; 597196200Sscottl case '?': 598196200Sscottl default: 599196200Sscottl return (EINVAL); 600196200Sscottl } 601196200Sscottl } 602196200Sscottl ac -= optind; 603196200Sscottl av += optind; 604196200Sscottl 605196200Sscottl /* Parse all the arrays. */ 606196200Sscottl narrays = ac; 607196200Sscottl if (narrays == 0) { 608196200Sscottl warnx("At least one drive list is required"); 609196200Sscottl return (EINVAL); 610196200Sscottl } 611196200Sscottl switch (raid_type) { 612196200Sscottl case RT_RAID0: 613196200Sscottl case RT_RAID1: 614196200Sscottl case RT_RAID5: 615196200Sscottl case RT_RAID6: 616196200Sscottl case RT_CONCAT: 617196200Sscottl if (narrays != 1) { 618196200Sscottl warnx("Only one drive list can be specified"); 619196200Sscottl return (EINVAL); 620196200Sscottl } 621196200Sscottl break; 622196200Sscottl case RT_RAID10: 623196200Sscottl case RT_RAID50: 624196200Sscottl case RT_RAID60: 625196200Sscottl if (narrays < 1) { 626196200Sscottl warnx("RAID10, RAID50, and RAID60 require at least " 627196200Sscottl "two drive lists"); 628196200Sscottl return (EINVAL); 629196200Sscottl } 630196200Sscottl if (narrays > MFI_MAX_SPAN_DEPTH) { 631196200Sscottl warnx("Volume spans more than %d arrays", 632196200Sscottl MFI_MAX_SPAN_DEPTH); 633196200Sscottl return (EINVAL); 634196200Sscottl } 635196200Sscottl break; 636196200Sscottl } 637196200Sscottl arrays = calloc(narrays, sizeof(*arrays)); 638196200Sscottl for (i = 0; i < narrays; i++) { 639196200Sscottl error = parse_array(fd, raid_type, av[i], &arrays[i]); 640196200Sscottl if (error) 641196200Sscottl return (error); 642196200Sscottl } 643196200Sscottl 644196200Sscottl switch (raid_type) { 645196200Sscottl case RT_RAID10: 646196200Sscottl case RT_RAID50: 647196200Sscottl case RT_RAID60: 648196200Sscottl for (i = 1; i < narrays; i++) { 649196200Sscottl if (arrays[i].drive_count != arrays[0].drive_count) { 650196200Sscottl warnx("All arrays must contain the same " 651196200Sscottl "number of drives"); 652196200Sscottl return (EINVAL); 653196200Sscottl } 654196200Sscottl } 655196200Sscottl break; 656196200Sscottl } 657196200Sscottl 658196200Sscottl /* 659196200Sscottl * Fetch the current config and build sorted lists of existing 660196200Sscottl * array and volume identifiers. 661196200Sscottl */ 662196200Sscottl if (mfi_config_read(fd, &config) < 0) { 663196200Sscottl warn("Failed to read configuration"); 664196200Sscottl return (errno); 665196200Sscottl } 666196200Sscottl p = (char *)config->array; 667196200Sscottl state.array_ref = 0xffff; 668196200Sscottl state.target_id = 0xff; 669196200Sscottl state.array_count = config->array_count; 670196200Sscottl if (config->array_count > 0) { 671196200Sscottl state.arrays = calloc(config->array_count, sizeof(int)); 672196200Sscottl for (i = 0; i < config->array_count; i++) { 673196200Sscottl ar = (struct mfi_array *)p; 674196200Sscottl state.arrays[i] = ar->array_ref; 675196200Sscottl p += config->array_size; 676196200Sscottl } 677196200Sscottl qsort(state.arrays, config->array_count, sizeof(int), 678196200Sscottl compare_int); 679196200Sscottl } else 680196200Sscottl state.arrays = NULL; 681196200Sscottl state.log_drv_count = config->log_drv_count; 682196200Sscottl if (config->log_drv_count) { 683196200Sscottl state.volumes = calloc(config->log_drv_count, sizeof(int)); 684196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 685196200Sscottl ld = (struct mfi_ld_config *)p; 686196200Sscottl state.volumes[i] = ld->properties.ld.v.target_id; 687196200Sscottl p += config->log_drv_size; 688196200Sscottl } 689196200Sscottl qsort(state.volumes, config->log_drv_count, sizeof(int), 690196200Sscottl compare_int); 691196200Sscottl } else 692196200Sscottl state.volumes = NULL; 693196200Sscottl free(config); 694196200Sscottl 695196200Sscottl /* Determine the size of the configuration we will build. */ 696196200Sscottl switch (raid_type) { 697196200Sscottl case RT_RAID0: 698196200Sscottl case RT_RAID1: 699196200Sscottl case RT_RAID5: 700196200Sscottl case RT_RAID6: 701196200Sscottl case RT_CONCAT: 702196200Sscottl case RT_JBOD: 703196200Sscottl /* Each volume spans a single array. */ 704196200Sscottl nvolumes = narrays; 705196200Sscottl break; 706196200Sscottl case RT_RAID10: 707196200Sscottl case RT_RAID50: 708196200Sscottl case RT_RAID60: 709196200Sscottl /* A single volume spans multiple arrays. */ 710196200Sscottl nvolumes = 1; 711196200Sscottl break; 712196200Sscottl default: 713196200Sscottl /* Pacify gcc. */ 714196200Sscottl abort(); 715196200Sscottl } 716196200Sscottl 717196200Sscottl config_size = sizeof(struct mfi_config_data) + 718196200Sscottl sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 719196200Sscottl config = calloc(1, config_size); 720196200Sscottl config->size = config_size; 721196200Sscottl config->array_count = narrays; 722196200Sscottl config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 723196200Sscottl config->log_drv_count = nvolumes; 724196200Sscottl config->log_drv_size = sizeof(struct mfi_ld_config); 725196200Sscottl config->spares_count = 0; 726196200Sscottl config->spares_size = 40; /* XXX: Firmware hardcode */ 727196200Sscottl cfg_arrays = (char *)config->array; 728196200Sscottl cfg_volumes = cfg_arrays + config->array_size * narrays; 729196200Sscottl 730196200Sscottl /* Build the arrays. */ 731196200Sscottl for (i = 0; i < narrays; i++) { 732196200Sscottl build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 733196200Sscottl cfg_arrays += config->array_size; 734196200Sscottl } 735196200Sscottl 736196200Sscottl /* Now build the volume(s). */ 737196200Sscottl arrays_per_volume = narrays / nvolumes; 738196200Sscottl for (i = 0; i < nvolumes; i++) { 739196200Sscottl build_volume(cfg_volumes, arrays_per_volume, 740196200Sscottl &arrays[i * arrays_per_volume], raid_type, stripe_size, 741196200Sscottl &state, verbose); 742196200Sscottl cfg_volumes += config->log_drv_size; 743196200Sscottl } 744196200Sscottl 745196200Sscottl#ifdef DEBUG 746196200Sscottl if (dump) 747196200Sscottl dump_config(fd, config); 748196200Sscottl else 749196200Sscottl#endif 750196200Sscottl 751196200Sscottl /* Send the new config to the controller. */ 752196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 753196200Sscottl NULL, 0, NULL) < 0) { 754196200Sscottl warn("Failed to add volume"); 755196200Sscottl return (errno); 756196200Sscottl } 757196200Sscottl 758196200Sscottl /* Clean up. */ 759196200Sscottl free(config); 760196200Sscottl if (state.log_drv_count > 0) 761196200Sscottl free(state.volumes); 762196200Sscottl if (state.array_count > 0) 763196200Sscottl free(state.arrays); 764196200Sscottl for (i = 0; i < narrays; i++) 765196200Sscottl free(arrays[i].drives); 766196200Sscottl free(arrays); 767196200Sscottl close(fd); 768196200Sscottl 769196200Sscottl return (0); 770196200Sscottl} 771196200SscottlMFI_COMMAND(top, create, create_volume); 772196200Sscottl 773196200Sscottlstatic int 774196200Sscottldelete_volume(int ac, char **av) 775196200Sscottl{ 776196200Sscottl struct mfi_ld_info info; 777196200Sscottl int fd; 778196200Sscottl uint8_t target_id, mbox[4]; 779196200Sscottl 780196200Sscottl /* 781196200Sscottl * Backwards compat. Map 'delete volume' to 'delete' and 782196200Sscottl * 'delete spare' to 'remove'. 783196200Sscottl */ 784196200Sscottl if (ac > 1) { 785196200Sscottl if (strcmp(av[1], "volume") == 0) { 786196200Sscottl av++; 787196200Sscottl ac--; 788196200Sscottl } else if (strcmp(av[1], "spare") == 0) { 789196200Sscottl av++; 790196200Sscottl ac--; 791196200Sscottl return (remove_spare(ac, av)); 792196200Sscottl } 793196200Sscottl } 794196200Sscottl 795196200Sscottl if (ac != 2) { 796196200Sscottl warnx("delete volume: volume required"); 797196200Sscottl return (EINVAL); 798196200Sscottl } 799196200Sscottl 800196200Sscottl fd = mfi_open(mfi_unit); 801196200Sscottl if (fd < 0) { 802196200Sscottl warn("mfi_open"); 803196200Sscottl return (errno); 804196200Sscottl } 805196200Sscottl 806196200Sscottl if (!mfi_reconfig_supported()) { 807196200Sscottl warnx("The current mfi(4) driver does not support " 808196200Sscottl "configuration changes."); 809196200Sscottl return (EOPNOTSUPP); 810196200Sscottl } 811196200Sscottl 812196200Sscottl if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 813196200Sscottl warn("Invalid volume %s", av[1]); 814196200Sscottl return (errno); 815196200Sscottl } 816196200Sscottl 817196200Sscottl if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 818196200Sscottl warn("Failed to get info for volume %d", target_id); 819196200Sscottl return (errno); 820196200Sscottl } 821196200Sscottl 822196200Sscottl if (mfi_volume_busy(fd, target_id)) { 823196200Sscottl warnx("Volume %s is busy and cannot be deleted", 824196200Sscottl mfi_volume_name(fd, target_id)); 825196200Sscottl return (EBUSY); 826196200Sscottl } 827196200Sscottl 828196200Sscottl mbox_store_ldref(mbox, &info.ld_config.properties.ld); 829196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 830196200Sscottl sizeof(mbox), NULL) < 0) { 831196200Sscottl warn("Failed to delete volume"); 832196200Sscottl return (errno); 833196200Sscottl } 834196200Sscottl 835196200Sscottl close(fd); 836196200Sscottl 837196200Sscottl return (0); 838196200Sscottl} 839196200SscottlMFI_COMMAND(top, delete, delete_volume); 840196200Sscottl 841196200Sscottlstatic int 842196200Sscottladd_spare(int ac, char **av) 843196200Sscottl{ 844196200Sscottl struct mfi_pd_info info; 845196200Sscottl struct mfi_config_data *config; 846196200Sscottl struct mfi_array *ar; 847196200Sscottl struct mfi_ld_config *ld; 848196200Sscottl struct mfi_spare *spare; 849196200Sscottl uint16_t device_id; 850196200Sscottl uint8_t target_id; 851196200Sscottl char *p; 852196200Sscottl int error, fd, i; 853196200Sscottl 854196200Sscottl if (ac < 2) { 855196200Sscottl warnx("add spare: drive required"); 856196200Sscottl return (EINVAL); 857196200Sscottl } 858196200Sscottl 859196200Sscottl fd = mfi_open(mfi_unit); 860196200Sscottl if (fd < 0) { 861196200Sscottl warn("mfi_open"); 862196200Sscottl return (errno); 863196200Sscottl } 864196200Sscottl 865196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 866196200Sscottl if (error) 867196200Sscottl return (error); 868196200Sscottl 869196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 870196200Sscottl warn("Failed to fetch drive info"); 871196200Sscottl return (errno); 872196200Sscottl } 873196200Sscottl 874196200Sscottl if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 875196200Sscottl warnx("Drive %u is not available", device_id); 876196200Sscottl return (EINVAL); 877196200Sscottl } 878196200Sscottl 879196200Sscottl if (ac > 2) { 880196200Sscottl if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 881196200Sscottl warn("Invalid volume %s", av[2]); 882196200Sscottl return (errno); 883196200Sscottl } 884196200Sscottl } 885196200Sscottl 886196200Sscottl if (mfi_config_read(fd, &config) < 0) { 887196200Sscottl warn("Failed to read configuration"); 888196200Sscottl return (errno); 889196200Sscottl } 890196200Sscottl 891196200Sscottl spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 892196200Sscottl config->array_count); 893196200Sscottl bzero(spare, sizeof(struct mfi_spare)); 894196200Sscottl spare->ref = info.ref; 895196200Sscottl 896196200Sscottl if (ac == 2) { 897196200Sscottl /* Global spare backs all arrays. */ 898196200Sscottl p = (char *)config->array; 899196200Sscottl for (i = 0; i < config->array_count; i++) { 900196200Sscottl ar = (struct mfi_array *)p; 901196200Sscottl if (ar->size > info.coerced_size) { 902196200Sscottl warnx("Spare isn't large enough for array %u", 903196200Sscottl ar->array_ref); 904196200Sscottl return (EINVAL); 905196200Sscottl } 906196200Sscottl p += config->array_size; 907196200Sscottl } 908196200Sscottl spare->array_count = 0; 909196200Sscottl } else { 910196200Sscottl /* 911196200Sscottl * Dedicated spares only back the arrays for a 912196200Sscottl * specific volume. 913196200Sscottl */ 914196200Sscottl ld = mfi_config_lookup_volume(config, target_id); 915196200Sscottl if (ld == NULL) { 916196200Sscottl warnx("Did not find volume %d", target_id); 917196200Sscottl return (EINVAL); 918196200Sscottl } 919196200Sscottl 920196200Sscottl spare->spare_type |= MFI_SPARE_DEDICATED; 921196200Sscottl spare->array_count = ld->params.span_depth; 922196200Sscottl for (i = 0; i < ld->params.span_depth; i++) { 923196200Sscottl ar = mfi_config_lookup_array(config, 924196200Sscottl ld->span[i].array_ref); 925196200Sscottl if (ar == NULL) { 926196200Sscottl warnx("Missing array; inconsistent config?"); 927196200Sscottl return (ENXIO); 928196200Sscottl } 929196200Sscottl if (ar->size > info.coerced_size) { 930196200Sscottl warnx("Spare isn't large enough for array %u", 931196200Sscottl ar->array_ref); 932196200Sscottl return (EINVAL); 933196200Sscottl } 934196200Sscottl spare->array_ref[i] = ar->array_ref; 935196200Sscottl } 936196200Sscottl } 937196200Sscottl free(config); 938196200Sscottl 939196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 940196200Sscottl sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 941196200Sscottl NULL, 0, NULL) < 0) { 942196200Sscottl warn("Failed to assign spare"); 943196200Sscottl return (errno); 944196200Sscottl } 945196200Sscottl 946196200Sscottl close(fd); 947196200Sscottl 948196200Sscottl return (0); 949196200Sscottl} 950196200SscottlMFI_COMMAND(top, add, add_spare); 951196200Sscottl 952196200Sscottlstatic int 953196200Sscottlremove_spare(int ac, char **av) 954196200Sscottl{ 955196200Sscottl struct mfi_pd_info info; 956196200Sscottl int error, fd; 957196200Sscottl uint16_t device_id; 958196200Sscottl uint8_t mbox[4]; 959196200Sscottl 960196200Sscottl if (ac != 2) { 961196200Sscottl warnx("remove spare: drive required"); 962196200Sscottl return (EINVAL); 963196200Sscottl } 964196200Sscottl 965196200Sscottl fd = mfi_open(mfi_unit); 966196200Sscottl if (fd < 0) { 967196200Sscottl warn("mfi_open"); 968196200Sscottl return (errno); 969196200Sscottl } 970196200Sscottl 971196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 972196200Sscottl if (error) 973196200Sscottl return (error); 974196200Sscottl 975196200Sscottl /* Get the info for this drive. */ 976196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 977196200Sscottl warn("Failed to fetch info for drive %u", device_id); 978196200Sscottl return (errno); 979196200Sscottl } 980196200Sscottl 981196200Sscottl if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 982196200Sscottl warnx("Drive %u is not a hot spare", device_id); 983196200Sscottl return (EINVAL); 984196200Sscottl } 985196200Sscottl 986196200Sscottl mbox_store_pdref(mbox, &info.ref); 987196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 988196200Sscottl sizeof(mbox), NULL) < 0) { 989196200Sscottl warn("Failed to delete spare"); 990196200Sscottl return (errno); 991196200Sscottl } 992196200Sscottl 993196200Sscottl close(fd); 994196200Sscottl 995196200Sscottl return (0); 996196200Sscottl} 997196200SscottlMFI_COMMAND(top, remove, remove_spare); 998196200Sscottl 999196200Sscottl#ifdef DEBUG 1000196200Sscottl/* Display raw data about a config. */ 1001196200Sscottlstatic void 1002196200Sscottldump_config(int fd, struct mfi_config_data *config) 1003196200Sscottl{ 1004196200Sscottl struct mfi_array *ar; 1005196200Sscottl struct mfi_ld_config *ld; 1006196200Sscottl struct mfi_spare *sp; 1007196200Sscottl struct mfi_pd_info pinfo; 1008196200Sscottl uint16_t device_id; 1009196200Sscottl char *p; 1010196200Sscottl int i, j; 1011196200Sscottl 1012196200Sscottl printf( 1013196200Sscottl "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n", 1014196200Sscottl mfi_unit, config->array_count, config->log_drv_count, 1015196200Sscottl config->spares_count); 1016196200Sscottl printf(" array size: %u\n", config->array_size); 1017196200Sscottl printf(" volume size: %u\n", config->log_drv_size); 1018196200Sscottl printf(" spare size: %u\n", config->spares_size); 1019196200Sscottl p = (char *)config->array; 1020196200Sscottl 1021196200Sscottl for (i = 0; i < config->array_count; i++) { 1022196200Sscottl ar = (struct mfi_array *)p; 1023196200Sscottl printf(" array %u of %u drives:\n", ar->array_ref, 1024196200Sscottl ar->num_drives); 1025196200Sscottl printf(" size = %ju\n", (uintmax_t)ar->size); 1026196200Sscottl for (j = 0; j < ar->num_drives; j++) { 1027196200Sscottl device_id = ar->pd[j].ref.device_id; 1028196200Sscottl if (device_id == 0xffff) 1029196200Sscottl printf(" drive MISSING\n"); 1030196200Sscottl else { 1031196200Sscottl printf(" drive %u %s\n", device_id, 1032196200Sscottl mfi_pdstate(ar->pd[j].fw_state)); 1033196200Sscottl if (mfi_pd_get_info(fd, device_id, &pinfo, 1034196200Sscottl NULL) >= 0) { 1035196200Sscottl printf(" raw size: %ju\n", 1036196200Sscottl (uintmax_t)pinfo.raw_size); 1037196200Sscottl printf(" non-coerced size: %ju\n", 1038196200Sscottl (uintmax_t)pinfo.non_coerced_size); 1039196200Sscottl printf(" coerced size: %ju\n", 1040196200Sscottl (uintmax_t)pinfo.coerced_size); 1041196200Sscottl } 1042196200Sscottl } 1043196200Sscottl } 1044196200Sscottl p += config->array_size; 1045196200Sscottl } 1046196200Sscottl 1047196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 1048196200Sscottl ld = (struct mfi_ld_config *)p; 1049196200Sscottl printf(" volume %s ", 1050196200Sscottl mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1051196200Sscottl printf("%s %s", 1052196200Sscottl mfi_raid_level(ld->params.primary_raid_level, 1053196200Sscottl ld->params.secondary_raid_level), 1054196200Sscottl mfi_ldstate(ld->params.state)); 1055196200Sscottl if (ld->properties.name[0] != '\0') 1056196200Sscottl printf(" <%s>", ld->properties.name); 1057196200Sscottl printf("\n"); 1058196200Sscottl printf(" primary raid level: %u\n", 1059196200Sscottl ld->params.primary_raid_level); 1060196200Sscottl printf(" raid level qualifier: %u\n", 1061196200Sscottl ld->params.raid_level_qualifier); 1062196200Sscottl printf(" secondary raid level: %u\n", 1063196200Sscottl ld->params.secondary_raid_level); 1064196200Sscottl printf(" stripe size: %u\n", ld->params.stripe_size); 1065196200Sscottl printf(" num drives: %u\n", ld->params.num_drives); 1066196200Sscottl printf(" init state: %u\n", ld->params.init_state); 1067196200Sscottl printf(" consistent: %u\n", ld->params.is_consistent); 1068196200Sscottl printf(" no bgi: %u\n", ld->properties.no_bgi); 1069196200Sscottl printf(" spans:\n"); 1070196200Sscottl for (j = 0; j < ld->params.span_depth; j++) { 1071196200Sscottl printf(" array %u @ ", ld->span[j].array_ref); 1072196200Sscottl printf("%ju : %ju\n", 1073196200Sscottl (uintmax_t)ld->span[j].start_block, 1074196200Sscottl (uintmax_t)ld->span[j].num_blocks); 1075196200Sscottl } 1076196200Sscottl p += config->log_drv_size; 1077196200Sscottl } 1078196200Sscottl 1079196200Sscottl for (i = 0; i < config->spares_count; i++) { 1080196200Sscottl sp = (struct mfi_spare *)p; 1081196200Sscottl printf(" %s spare %u ", 1082196200Sscottl sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1083196200Sscottl "global", sp->ref.device_id); 1084196200Sscottl printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1085196200Sscottl printf(" backs:\n"); 1086196200Sscottl for (j = 0; j < sp->array_count; j++) 1087196200Sscottl printf(" array %u\n", sp->array_ref[j]); 1088196200Sscottl p += config->spares_size; 1089196200Sscottl } 1090196200Sscottl} 1091196200Sscottl 1092196200Sscottlstatic int 1093196200Sscottldebug_config(int ac, char **av) 1094196200Sscottl{ 1095196200Sscottl struct mfi_config_data *config; 1096196200Sscottl int fd; 1097196200Sscottl 1098196200Sscottl if (ac != 1) { 1099196200Sscottl warnx("debug: extra arguments"); 1100196200Sscottl return (EINVAL); 1101196200Sscottl } 1102196200Sscottl 1103196200Sscottl fd = mfi_open(mfi_unit); 1104196200Sscottl if (fd < 0) { 1105196200Sscottl warn("mfi_open"); 1106196200Sscottl return (errno); 1107196200Sscottl } 1108196200Sscottl 1109196200Sscottl /* Get the config from the controller. */ 1110196200Sscottl if (mfi_config_read(fd, &config) < 0) { 1111196200Sscottl warn("Failed to get config"); 1112196200Sscottl return (errno); 1113196200Sscottl } 1114196200Sscottl 1115196200Sscottl /* Dump out the configuration. */ 1116196200Sscottl dump_config(fd, config); 1117196200Sscottl free(config); 1118196200Sscottl close(fd); 1119196200Sscottl 1120196200Sscottl return (0); 1121196200Sscottl} 1122196200SscottlMFI_COMMAND(top, debug, debug_config); 1123196200Sscottl 1124196200Sscottlstatic int 1125196200Sscottldump(int ac, char **av) 1126196200Sscottl{ 1127196200Sscottl struct mfi_config_data *config; 1128196200Sscottl char buf[64]; 1129196200Sscottl size_t len; 1130196200Sscottl int fd; 1131196200Sscottl 1132196200Sscottl if (ac != 1) { 1133196200Sscottl warnx("dump: extra arguments"); 1134196200Sscottl return (EINVAL); 1135196200Sscottl } 1136196200Sscottl 1137196200Sscottl fd = mfi_open(mfi_unit); 1138196200Sscottl if (fd < 0) { 1139196200Sscottl warn("mfi_open"); 1140196200Sscottl return (errno); 1141196200Sscottl } 1142196200Sscottl 1143196200Sscottl /* Get the stashed copy of the last dcmd from the driver. */ 1144196200Sscottl snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1145196200Sscottl if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1146196200Sscottl warn("Failed to read debug command"); 1147196200Sscottl if (errno == ENOENT) 1148196200Sscottl errno = EOPNOTSUPP; 1149196200Sscottl return (errno); 1150196200Sscottl } 1151196200Sscottl 1152196200Sscottl config = malloc(len); 1153196200Sscottl if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1154196200Sscottl warn("Failed to read debug command"); 1155196200Sscottl return (errno); 1156196200Sscottl } 1157196200Sscottl dump_config(fd, config); 1158196200Sscottl free(config); 1159196200Sscottl close(fd); 1160196200Sscottl 1161196200Sscottl return (0); 1162196200Sscottl} 1163196200SscottlMFI_COMMAND(top, dump, dump); 1164196200Sscottl#endif 1165