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: releng/11.0/usr.sbin/mfiutil/mfi_config.c 251516 2013-06-08 02:54:59Z sbruno $ 30196200Sscottl */ 31196200Sscottl 32214396Sjhb#include <sys/param.h> 33196200Sscottl#ifdef DEBUG 34196200Sscottl#include <sys/sysctl.h> 35196200Sscottl#endif 36196200Sscottl#include <err.h> 37214396Sjhb#include <errno.h> 38237259Seadler#include <fcntl.h> 39196200Sscottl#include <libutil.h> 40196200Sscottl#include <stdint.h> 41196200Sscottl#include <stdio.h> 42196200Sscottl#include <stdlib.h> 43196200Sscottl#include <string.h> 44196200Sscottl#include <unistd.h> 45196200Sscottl#include "mfiutil.h" 46196200Sscottl 47196200Sscottlstatic int add_spare(int ac, char **av); 48196200Sscottlstatic int remove_spare(int ac, char **av); 49196200Sscottl 50196200Sscottlstatic long 51196200Sscottldehumanize(const char *value) 52196200Sscottl{ 53196200Sscottl char *vtp; 54196200Sscottl long iv; 55196200Sscottl 56196200Sscottl if (value == NULL) 57196200Sscottl return (0); 58196200Sscottl iv = strtoq(value, &vtp, 0); 59196200Sscottl if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 60196200Sscottl return (0); 61196200Sscottl } 62196200Sscottl switch (vtp[0]) { 63196200Sscottl case 't': case 'T': 64196200Sscottl iv *= 1024; 65196200Sscottl case 'g': case 'G': 66196200Sscottl iv *= 1024; 67196200Sscottl case 'm': case 'M': 68196200Sscottl iv *= 1024; 69196200Sscottl case 'k': case 'K': 70196200Sscottl iv *= 1024; 71196200Sscottl case '\0': 72196200Sscottl break; 73196200Sscottl default: 74196200Sscottl return (0); 75196200Sscottl } 76196200Sscottl return (iv); 77196200Sscottl} 78251516Ssbruno 79196200Sscottlint 80196200Sscottlmfi_config_read(int fd, struct mfi_config_data **configp) 81196200Sscottl{ 82251516Ssbruno return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0); 83251516Ssbruno} 84251516Ssbruno 85251516Ssbrunoint 86251516Ssbrunomfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp, 87251516Ssbruno uint8_t *mbox, size_t mboxlen) 88251516Ssbruno{ 89196200Sscottl struct mfi_config_data *config; 90196200Sscottl uint32_t config_size; 91222899Sbz int error; 92196200Sscottl 93196200Sscottl /* 94196200Sscottl * Keep fetching the config in a loop until we have a large enough 95196200Sscottl * buffer to hold the entire configuration. 96196200Sscottl */ 97196200Sscottl config = NULL; 98196200Sscottl config_size = 1024; 99196200Sscottlfetch: 100196200Sscottl config = reallocf(config, config_size); 101196200Sscottl if (config == NULL) 102196200Sscottl return (-1); 103251516Ssbruno if (mfi_dcmd_command(fd, opcode, config, 104251516Ssbruno config_size, mbox, mboxlen, NULL) < 0) { 105222899Sbz error = errno; 106222899Sbz free(config); 107222899Sbz errno = error; 108196200Sscottl return (-1); 109222899Sbz } 110196200Sscottl 111196200Sscottl if (config->size > config_size) { 112196200Sscottl config_size = config->size; 113196200Sscottl goto fetch; 114196200Sscottl } 115196200Sscottl 116196200Sscottl *configp = config; 117196200Sscottl return (0); 118196200Sscottl} 119196200Sscottl 120196200Sscottlstatic struct mfi_array * 121196200Sscottlmfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 122196200Sscottl{ 123196200Sscottl struct mfi_array *ar; 124196200Sscottl char *p; 125196200Sscottl int i; 126196200Sscottl 127196200Sscottl p = (char *)config->array; 128196200Sscottl for (i = 0; i < config->array_count; i++) { 129196200Sscottl ar = (struct mfi_array *)p; 130196200Sscottl if (ar->array_ref == array_ref) 131196200Sscottl return (ar); 132196200Sscottl p += config->array_size; 133196200Sscottl } 134196200Sscottl 135196200Sscottl return (NULL); 136196200Sscottl} 137196200Sscottl 138196200Sscottlstatic struct mfi_ld_config * 139196200Sscottlmfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 140196200Sscottl{ 141196200Sscottl struct mfi_ld_config *ld; 142196200Sscottl char *p; 143196200Sscottl int i; 144196200Sscottl 145196200Sscottl p = (char *)config->array + config->array_count * config->array_size; 146196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 147196200Sscottl ld = (struct mfi_ld_config *)p; 148196200Sscottl if (ld->properties.ld.v.target_id == target_id) 149196200Sscottl return (ld); 150196200Sscottl p += config->log_drv_size; 151196200Sscottl } 152196200Sscottl 153196200Sscottl return (NULL); 154196200Sscottl} 155196200Sscottl 156196200Sscottlstatic int 157237260Seadlerclear_config(int ac __unused, char **av __unused) 158196200Sscottl{ 159196200Sscottl struct mfi_ld_list list; 160214396Sjhb int ch, error, fd; 161196200Sscottl u_int i; 162196200Sscottl 163237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 164196200Sscottl if (fd < 0) { 165214396Sjhb error = errno; 166196200Sscottl warn("mfi_open"); 167214396Sjhb return (error); 168196200Sscottl } 169196200Sscottl 170196200Sscottl if (!mfi_reconfig_supported()) { 171196200Sscottl warnx("The current mfi(4) driver does not support " 172196200Sscottl "configuration changes."); 173222899Sbz close(fd); 174196200Sscottl return (EOPNOTSUPP); 175196200Sscottl } 176196200Sscottl 177196200Sscottl if (mfi_ld_get_list(fd, &list, NULL) < 0) { 178214396Sjhb error = errno; 179196200Sscottl warn("Failed to get volume list"); 180222899Sbz close(fd); 181214396Sjhb return (error); 182196200Sscottl } 183196200Sscottl 184196200Sscottl for (i = 0; i < list.ld_count; i++) { 185196200Sscottl if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 186196200Sscottl warnx("Volume %s is busy and cannot be deleted", 187196200Sscottl mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 188222899Sbz close(fd); 189196200Sscottl return (EBUSY); 190196200Sscottl } 191196200Sscottl } 192196200Sscottl 193196200Sscottl printf( 194196200Sscottl "Are you sure you wish to clear the configuration on mfi%u? [y/N] ", 195196200Sscottl mfi_unit); 196196200Sscottl ch = getchar(); 197196200Sscottl if (ch != 'y' && ch != 'Y') { 198196200Sscottl printf("\nAborting\n"); 199222899Sbz close(fd); 200196200Sscottl return (0); 201196200Sscottl } 202196200Sscottl 203196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 204214396Sjhb error = errno; 205196200Sscottl warn("Failed to clear configuration"); 206222899Sbz close(fd); 207214396Sjhb return (error); 208196200Sscottl } 209196200Sscottl 210196200Sscottl printf("mfi%d: Configuration cleared\n", mfi_unit); 211196200Sscottl close(fd); 212196200Sscottl 213196200Sscottl return (0); 214196200Sscottl} 215196200SscottlMFI_COMMAND(top, clear, clear_config); 216196200Sscottl 217233713Sambrisko#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE 218233713Sambrisko#define MFI_ARRAY_SIZE sizeof(struct mfi_array) 219196200Sscottl 220196200Sscottl#define RT_RAID0 0 221196200Sscottl#define RT_RAID1 1 222196200Sscottl#define RT_RAID5 2 223196200Sscottl#define RT_RAID6 3 224196200Sscottl#define RT_JBOD 4 225196200Sscottl#define RT_CONCAT 5 226196200Sscottl#define RT_RAID10 6 227196200Sscottl#define RT_RAID50 7 228196200Sscottl#define RT_RAID60 8 229196200Sscottl 230196200Sscottlstatic int 231196200Sscottlcompare_int(const void *one, const void *two) 232196200Sscottl{ 233196200Sscottl int first, second; 234196200Sscottl 235196200Sscottl first = *(const int *)one; 236196200Sscottl second = *(const int *)two; 237196200Sscottl 238196200Sscottl return (first - second); 239196200Sscottl} 240196200Sscottl 241196200Sscottlstatic struct raid_type_entry { 242196200Sscottl const char *name; 243196200Sscottl int raid_type; 244196200Sscottl} raid_type_table[] = { 245196200Sscottl { "raid0", RT_RAID0 }, 246196200Sscottl { "raid-0", RT_RAID0 }, 247196200Sscottl { "raid1", RT_RAID1 }, 248196200Sscottl { "raid-1", RT_RAID1 }, 249196200Sscottl { "mirror", RT_RAID1 }, 250196200Sscottl { "raid5", RT_RAID5 }, 251196200Sscottl { "raid-5", RT_RAID5 }, 252196200Sscottl { "raid6", RT_RAID6 }, 253196200Sscottl { "raid-6", RT_RAID6 }, 254196200Sscottl { "jbod", RT_JBOD }, 255196200Sscottl { "concat", RT_CONCAT }, 256196200Sscottl { "raid10", RT_RAID10 }, 257196200Sscottl { "raid1+0", RT_RAID10 }, 258196200Sscottl { "raid-10", RT_RAID10 }, 259196200Sscottl { "raid-1+0", RT_RAID10 }, 260196200Sscottl { "raid50", RT_RAID50 }, 261196200Sscottl { "raid5+0", RT_RAID50 }, 262196200Sscottl { "raid-50", RT_RAID50 }, 263196200Sscottl { "raid-5+0", RT_RAID50 }, 264196200Sscottl { "raid60", RT_RAID60 }, 265196200Sscottl { "raid6+0", RT_RAID60 }, 266196200Sscottl { "raid-60", RT_RAID60 }, 267196200Sscottl { "raid-6+0", RT_RAID60 }, 268196200Sscottl { NULL, 0 }, 269196200Sscottl}; 270196200Sscottl 271196200Sscottlstruct config_id_state { 272196200Sscottl int array_count; 273196200Sscottl int log_drv_count; 274196200Sscottl int *arrays; 275196200Sscottl int *volumes; 276196200Sscottl uint16_t array_ref; 277196200Sscottl uint8_t target_id; 278196200Sscottl}; 279196200Sscottl 280196200Sscottlstruct array_info { 281196200Sscottl int drive_count; 282196200Sscottl struct mfi_pd_info *drives; 283196200Sscottl struct mfi_array *array; 284196200Sscottl}; 285196200Sscottl 286196200Sscottl/* Parse a comma-separated list of drives for an array. */ 287196200Sscottlstatic int 288196200Sscottlparse_array(int fd, int raid_type, char *array_str, struct array_info *info) 289196200Sscottl{ 290196200Sscottl struct mfi_pd_info *pinfo; 291196200Sscottl uint16_t device_id; 292196200Sscottl char *cp; 293196200Sscottl u_int count; 294196200Sscottl int error; 295196200Sscottl 296196200Sscottl cp = array_str; 297196200Sscottl for (count = 0; cp != NULL; count++) { 298196200Sscottl cp = strchr(cp, ','); 299196200Sscottl if (cp != NULL) { 300196200Sscottl cp++; 301196200Sscottl if (*cp == ',') { 302196200Sscottl warnx("Invalid drive list '%s'", array_str); 303196200Sscottl return (EINVAL); 304196200Sscottl } 305196200Sscottl } 306196200Sscottl } 307196200Sscottl 308196200Sscottl /* Validate the number of drives for this array. */ 309196200Sscottl if (count >= MAX_DRIVES_PER_ARRAY) { 310233713Sambrisko warnx("Too many drives for a single array: max is %d", 311196200Sscottl MAX_DRIVES_PER_ARRAY); 312196200Sscottl return (EINVAL); 313196200Sscottl } 314196200Sscottl switch (raid_type) { 315196200Sscottl case RT_RAID1: 316196200Sscottl case RT_RAID10: 317196200Sscottl if (count % 2 != 0) { 318196200Sscottl warnx("RAID1 and RAID10 require an even number of " 319196200Sscottl "drives in each array"); 320196200Sscottl return (EINVAL); 321196200Sscottl } 322196200Sscottl break; 323196200Sscottl case RT_RAID5: 324196200Sscottl case RT_RAID50: 325196200Sscottl if (count < 3) { 326196200Sscottl warnx("RAID5 and RAID50 require at least 3 drives in " 327196200Sscottl "each array"); 328196200Sscottl return (EINVAL); 329196200Sscottl } 330196200Sscottl break; 331196200Sscottl case RT_RAID6: 332196200Sscottl case RT_RAID60: 333196200Sscottl if (count < 4) { 334196200Sscottl warnx("RAID6 and RAID60 require at least 4 drives in " 335196200Sscottl "each array"); 336196200Sscottl return (EINVAL); 337196200Sscottl } 338196200Sscottl break; 339196200Sscottl } 340196200Sscottl 341196200Sscottl /* Validate each drive. */ 342196200Sscottl info->drives = calloc(count, sizeof(struct mfi_pd_info)); 343215526Sjhb if (info->drives == NULL) { 344215526Sjhb warnx("malloc failed"); 345215526Sjhb return (ENOMEM); 346215526Sjhb } 347196200Sscottl info->drive_count = count; 348196200Sscottl for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 349196200Sscottl pinfo++) { 350196200Sscottl error = mfi_lookup_drive(fd, cp, &device_id); 351222899Sbz if (error) { 352222899Sbz free(info->drives); 353227893Semaste info->drives = NULL; 354196200Sscottl return (error); 355222899Sbz } 356196200Sscottl 357196200Sscottl if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 358214396Sjhb error = errno; 359196200Sscottl warn("Failed to fetch drive info for drive %s", cp); 360222899Sbz free(info->drives); 361227893Semaste info->drives = NULL; 362214396Sjhb return (error); 363196200Sscottl } 364196200Sscottl 365196200Sscottl if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 366196200Sscottl warnx("Drive %u is not available", device_id); 367222899Sbz free(info->drives); 368227893Semaste info->drives = NULL; 369196200Sscottl return (EINVAL); 370196200Sscottl } 371251516Ssbruno 372251516Ssbruno if (pinfo->state.ddf.v.pd_type.is_foreign) { 373251516Ssbruno warnx("Drive %u is foreign", device_id); 374251516Ssbruno free(info->drives); 375251516Ssbruno info->drives = NULL; 376251516Ssbruno return (EINVAL); 377251516Ssbruno } 378196200Sscottl } 379196200Sscottl 380196200Sscottl return (0); 381196200Sscottl} 382196200Sscottl 383196200Sscottl/* 384196200Sscottl * Find the next free array ref assuming that 'array_ref' is the last 385196200Sscottl * one used. 'array_ref' should be 0xffff for the initial test. 386196200Sscottl */ 387196200Sscottlstatic uint16_t 388196200Sscottlfind_next_array(struct config_id_state *state) 389196200Sscottl{ 390196200Sscottl int i; 391196200Sscottl 392196200Sscottl /* Assume the current one is used. */ 393196200Sscottl state->array_ref++; 394196200Sscottl 395196200Sscottl /* Find the next free one. */ 396196200Sscottl for (i = 0; i < state->array_count; i++) 397196200Sscottl if (state->arrays[i] == state->array_ref) 398196200Sscottl state->array_ref++; 399196200Sscottl return (state->array_ref); 400196200Sscottl} 401196200Sscottl 402196200Sscottl/* 403196200Sscottl * Find the next free volume ID assuming that 'target_id' is the last 404196200Sscottl * one used. 'target_id' should be 0xff for the initial test. 405196200Sscottl */ 406196200Sscottlstatic uint8_t 407196200Sscottlfind_next_volume(struct config_id_state *state) 408196200Sscottl{ 409196200Sscottl int i; 410196200Sscottl 411196200Sscottl /* Assume the current one is used. */ 412196200Sscottl state->target_id++; 413196200Sscottl 414196200Sscottl /* Find the next free one. */ 415196200Sscottl for (i = 0; i < state->log_drv_count; i++) 416196200Sscottl if (state->volumes[i] == state->target_id) 417196200Sscottl state->target_id++; 418196200Sscottl return (state->target_id); 419196200Sscottl} 420196200Sscottl 421196200Sscottl/* Populate an array with drives. */ 422196200Sscottlstatic void 423237260Seadlerbuild_array(int fd __unused, char *arrayp, struct array_info *array_info, 424196200Sscottl struct config_id_state *state, int verbose) 425196200Sscottl{ 426196200Sscottl struct mfi_array *ar = (struct mfi_array *)arrayp; 427196200Sscottl int i; 428196200Sscottl 429196200Sscottl ar->size = array_info->drives[0].coerced_size; 430196200Sscottl ar->num_drives = array_info->drive_count; 431196200Sscottl ar->array_ref = find_next_array(state); 432196200Sscottl for (i = 0; i < array_info->drive_count; i++) { 433196200Sscottl if (verbose) 434223345Sbz printf("Adding drive %s to array %u\n", 435223345Sbz mfi_drive_name(NULL, 436196200Sscottl array_info->drives[i].ref.v.device_id, 437223345Sbz MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS), 438196200Sscottl ar->array_ref); 439196200Sscottl if (ar->size > array_info->drives[i].coerced_size) 440196200Sscottl ar->size = array_info->drives[i].coerced_size; 441196200Sscottl ar->pd[i].ref = array_info->drives[i].ref; 442196200Sscottl ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 443196200Sscottl } 444196200Sscottl array_info->array = ar; 445196200Sscottl} 446196200Sscottl 447196200Sscottl/* 448196200Sscottl * Create a volume that spans one or more arrays. 449196200Sscottl */ 450196200Sscottlstatic void 451196200Sscottlbuild_volume(char *volumep, int narrays, struct array_info *arrays, 452196200Sscottl int raid_type, long stripe_size, struct config_id_state *state, int verbose) 453196200Sscottl{ 454196200Sscottl struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 455196200Sscottl struct mfi_array *ar; 456196200Sscottl int i; 457196200Sscottl 458196200Sscottl /* properties */ 459196200Sscottl ld->properties.ld.v.target_id = find_next_volume(state); 460196200Sscottl ld->properties.ld.v.seq = 0; 461196200Sscottl ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 462196200Sscottl MR_LD_CACHE_WRITE_BACK; 463196200Sscottl ld->properties.access_policy = MFI_LD_ACCESS_RW; 464196200Sscottl ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 465196200Sscottl ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 466196200Sscottl MR_LD_CACHE_WRITE_BACK; 467196200Sscottl ld->properties.no_bgi = 0; 468196200Sscottl 469196200Sscottl /* params */ 470196200Sscottl switch (raid_type) { 471196200Sscottl case RT_RAID0: 472196200Sscottl case RT_JBOD: 473196200Sscottl ld->params.primary_raid_level = DDF_RAID0; 474196200Sscottl ld->params.raid_level_qualifier = 0; 475196200Sscottl ld->params.secondary_raid_level = 0; 476196200Sscottl break; 477196200Sscottl case RT_RAID1: 478196200Sscottl ld->params.primary_raid_level = DDF_RAID1; 479196200Sscottl ld->params.raid_level_qualifier = 0; 480196200Sscottl ld->params.secondary_raid_level = 0; 481196200Sscottl break; 482196200Sscottl case RT_RAID5: 483196200Sscottl ld->params.primary_raid_level = DDF_RAID5; 484196200Sscottl ld->params.raid_level_qualifier = 3; 485196200Sscottl ld->params.secondary_raid_level = 0; 486196200Sscottl break; 487196200Sscottl case RT_RAID6: 488196200Sscottl ld->params.primary_raid_level = DDF_RAID6; 489196200Sscottl ld->params.raid_level_qualifier = 3; 490196200Sscottl ld->params.secondary_raid_level = 0; 491196200Sscottl break; 492196200Sscottl case RT_CONCAT: 493196200Sscottl ld->params.primary_raid_level = DDF_CONCAT; 494196200Sscottl ld->params.raid_level_qualifier = 0; 495196200Sscottl ld->params.secondary_raid_level = 0; 496196200Sscottl break; 497196200Sscottl case RT_RAID10: 498196200Sscottl ld->params.primary_raid_level = DDF_RAID1; 499196200Sscottl ld->params.raid_level_qualifier = 0; 500196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 501196200Sscottl break; 502196200Sscottl case RT_RAID50: 503196200Sscottl /* 504196200Sscottl * XXX: This appears to work though the card's BIOS 505196200Sscottl * complains that the configuration is foreign. The 506196200Sscottl * BIOS setup does not allow for creation of RAID-50 507196200Sscottl * or RAID-60 arrays. The only nested array 508196200Sscottl * configuration it allows for is RAID-10. 509196200Sscottl */ 510196200Sscottl ld->params.primary_raid_level = DDF_RAID5; 511196200Sscottl ld->params.raid_level_qualifier = 3; 512196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 513196200Sscottl break; 514196200Sscottl case RT_RAID60: 515196200Sscottl ld->params.primary_raid_level = DDF_RAID6; 516196200Sscottl ld->params.raid_level_qualifier = 3; 517196200Sscottl ld->params.secondary_raid_level = 3; /* XXX? */ 518196200Sscottl break; 519196200Sscottl } 520196200Sscottl 521196200Sscottl /* 522196200Sscottl * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 523196200Sscottl * ffs() to simulate log2(stripe_size). 524196200Sscottl */ 525196200Sscottl ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 526196200Sscottl ld->params.num_drives = arrays[0].array->num_drives; 527196200Sscottl ld->params.span_depth = narrays; 528196200Sscottl ld->params.state = MFI_LD_STATE_OPTIMAL; 529196200Sscottl ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 530196200Sscottl ld->params.is_consistent = 0; 531196200Sscottl 532196200Sscottl /* spans */ 533196200Sscottl for (i = 0; i < narrays; i++) { 534196200Sscottl ar = arrays[i].array; 535196200Sscottl if (verbose) 536196200Sscottl printf("Adding array %u to volume %u\n", ar->array_ref, 537196200Sscottl ld->properties.ld.v.target_id); 538196200Sscottl ld->span[i].start_block = 0; 539196200Sscottl ld->span[i].num_blocks = ar->size; 540196200Sscottl ld->span[i].array_ref = ar->array_ref; 541196200Sscottl } 542196200Sscottl} 543196200Sscottl 544196200Sscottlstatic int 545196200Sscottlcreate_volume(int ac, char **av) 546196200Sscottl{ 547196200Sscottl struct mfi_config_data *config; 548196200Sscottl struct mfi_array *ar; 549196200Sscottl struct mfi_ld_config *ld; 550196200Sscottl struct config_id_state state; 551196200Sscottl size_t config_size; 552196200Sscottl char *p, *cfg_arrays, *cfg_volumes; 553196200Sscottl int error, fd, i, raid_type; 554196200Sscottl int narrays, nvolumes, arrays_per_volume; 555196200Sscottl struct array_info *arrays; 556196200Sscottl long stripe_size; 557196200Sscottl#ifdef DEBUG 558196200Sscottl int dump; 559196200Sscottl#endif 560196200Sscottl int ch, verbose; 561196200Sscottl 562196200Sscottl /* 563196200Sscottl * Backwards compat. Map 'create volume' to 'create' and 564196200Sscottl * 'create spare' to 'add'. 565196200Sscottl */ 566196200Sscottl if (ac > 1) { 567196200Sscottl if (strcmp(av[1], "volume") == 0) { 568196200Sscottl av++; 569196200Sscottl ac--; 570196200Sscottl } else if (strcmp(av[1], "spare") == 0) { 571196200Sscottl av++; 572196200Sscottl ac--; 573196200Sscottl return (add_spare(ac, av)); 574196200Sscottl } 575196200Sscottl } 576196200Sscottl 577196200Sscottl if (ac < 2) { 578196200Sscottl warnx("create volume: volume type required"); 579196200Sscottl return (EINVAL); 580196200Sscottl } 581196200Sscottl 582222899Sbz bzero(&state, sizeof(state)); 583222899Sbz config = NULL; 584222899Sbz arrays = NULL; 585222899Sbz narrays = 0; 586222899Sbz error = 0; 587222899Sbz 588237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 589196200Sscottl if (fd < 0) { 590214396Sjhb error = errno; 591196200Sscottl warn("mfi_open"); 592214396Sjhb return (error); 593196200Sscottl } 594196200Sscottl 595196200Sscottl if (!mfi_reconfig_supported()) { 596196200Sscottl warnx("The current mfi(4) driver does not support " 597196200Sscottl "configuration changes."); 598222899Sbz error = EOPNOTSUPP; 599222899Sbz goto error; 600196200Sscottl } 601196200Sscottl 602196200Sscottl /* Lookup the RAID type first. */ 603196200Sscottl raid_type = -1; 604196200Sscottl for (i = 0; raid_type_table[i].name != NULL; i++) 605196200Sscottl if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 606196200Sscottl raid_type = raid_type_table[i].raid_type; 607196200Sscottl break; 608196200Sscottl } 609196200Sscottl 610196200Sscottl if (raid_type == -1) { 611196200Sscottl warnx("Unknown or unsupported volume type %s", av[1]); 612222899Sbz error = EINVAL; 613222899Sbz goto error; 614196200Sscottl } 615196200Sscottl 616196200Sscottl /* Parse any options. */ 617196200Sscottl optind = 2; 618196200Sscottl#ifdef DEBUG 619196200Sscottl dump = 0; 620196200Sscottl#endif 621196200Sscottl verbose = 0; 622196200Sscottl stripe_size = 64 * 1024; 623196200Sscottl 624196200Sscottl while ((ch = getopt(ac, av, "ds:v")) != -1) { 625196200Sscottl switch (ch) { 626196200Sscottl#ifdef DEBUG 627196200Sscottl case 'd': 628196200Sscottl dump = 1; 629196200Sscottl break; 630196200Sscottl#endif 631196200Sscottl case 's': 632196200Sscottl stripe_size = dehumanize(optarg); 633196200Sscottl if ((stripe_size < 512) || (!powerof2(stripe_size))) 634196200Sscottl stripe_size = 64 * 1024; 635196200Sscottl break; 636196200Sscottl case 'v': 637196200Sscottl verbose = 1; 638196200Sscottl break; 639196200Sscottl case '?': 640196200Sscottl default: 641222899Sbz error = EINVAL; 642222899Sbz goto error; 643196200Sscottl } 644196200Sscottl } 645196200Sscottl ac -= optind; 646196200Sscottl av += optind; 647196200Sscottl 648196200Sscottl /* Parse all the arrays. */ 649196200Sscottl narrays = ac; 650196200Sscottl if (narrays == 0) { 651196200Sscottl warnx("At least one drive list is required"); 652222899Sbz error = EINVAL; 653222899Sbz goto error; 654196200Sscottl } 655196200Sscottl switch (raid_type) { 656196200Sscottl case RT_RAID0: 657196200Sscottl case RT_RAID1: 658196200Sscottl case RT_RAID5: 659196200Sscottl case RT_RAID6: 660196200Sscottl case RT_CONCAT: 661196200Sscottl if (narrays != 1) { 662196200Sscottl warnx("Only one drive list can be specified"); 663222899Sbz error = EINVAL; 664222899Sbz goto error; 665196200Sscottl } 666196200Sscottl break; 667196200Sscottl case RT_RAID10: 668196200Sscottl case RT_RAID50: 669196200Sscottl case RT_RAID60: 670196200Sscottl if (narrays < 1) { 671196200Sscottl warnx("RAID10, RAID50, and RAID60 require at least " 672196200Sscottl "two drive lists"); 673222899Sbz error = EINVAL; 674222899Sbz goto error; 675196200Sscottl } 676196200Sscottl if (narrays > MFI_MAX_SPAN_DEPTH) { 677196200Sscottl warnx("Volume spans more than %d arrays", 678196200Sscottl MFI_MAX_SPAN_DEPTH); 679222899Sbz error = EINVAL; 680222899Sbz goto error; 681196200Sscottl } 682196200Sscottl break; 683196200Sscottl } 684196200Sscottl arrays = calloc(narrays, sizeof(*arrays)); 685215526Sjhb if (arrays == NULL) { 686215526Sjhb warnx("malloc failed"); 687222899Sbz error = ENOMEM; 688222899Sbz goto error; 689215526Sjhb } 690196200Sscottl for (i = 0; i < narrays; i++) { 691196200Sscottl error = parse_array(fd, raid_type, av[i], &arrays[i]); 692196200Sscottl if (error) 693222899Sbz goto error; 694196200Sscottl } 695196200Sscottl 696196200Sscottl switch (raid_type) { 697196200Sscottl case RT_RAID10: 698196200Sscottl case RT_RAID50: 699196200Sscottl case RT_RAID60: 700196200Sscottl for (i = 1; i < narrays; i++) { 701196200Sscottl if (arrays[i].drive_count != arrays[0].drive_count) { 702196200Sscottl warnx("All arrays must contain the same " 703196200Sscottl "number of drives"); 704222899Sbz error = EINVAL; 705222899Sbz goto error; 706196200Sscottl } 707196200Sscottl } 708196200Sscottl break; 709196200Sscottl } 710196200Sscottl 711196200Sscottl /* 712196200Sscottl * Fetch the current config and build sorted lists of existing 713196200Sscottl * array and volume identifiers. 714196200Sscottl */ 715196200Sscottl if (mfi_config_read(fd, &config) < 0) { 716214396Sjhb error = errno; 717196200Sscottl warn("Failed to read configuration"); 718222899Sbz goto error; 719196200Sscottl } 720196200Sscottl p = (char *)config->array; 721196200Sscottl state.array_ref = 0xffff; 722196200Sscottl state.target_id = 0xff; 723196200Sscottl state.array_count = config->array_count; 724196200Sscottl if (config->array_count > 0) { 725196200Sscottl state.arrays = calloc(config->array_count, sizeof(int)); 726215526Sjhb if (state.arrays == NULL) { 727215526Sjhb warnx("malloc failed"); 728222899Sbz error = ENOMEM; 729222899Sbz goto error; 730215526Sjhb } 731196200Sscottl for (i = 0; i < config->array_count; i++) { 732196200Sscottl ar = (struct mfi_array *)p; 733196200Sscottl state.arrays[i] = ar->array_ref; 734196200Sscottl p += config->array_size; 735196200Sscottl } 736196200Sscottl qsort(state.arrays, config->array_count, sizeof(int), 737196200Sscottl compare_int); 738196200Sscottl } else 739196200Sscottl state.arrays = NULL; 740196200Sscottl state.log_drv_count = config->log_drv_count; 741196200Sscottl if (config->log_drv_count) { 742196200Sscottl state.volumes = calloc(config->log_drv_count, sizeof(int)); 743215526Sjhb if (state.volumes == NULL) { 744215526Sjhb warnx("malloc failed"); 745222899Sbz error = ENOMEM; 746222899Sbz goto error; 747215526Sjhb } 748196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 749196200Sscottl ld = (struct mfi_ld_config *)p; 750196200Sscottl state.volumes[i] = ld->properties.ld.v.target_id; 751196200Sscottl p += config->log_drv_size; 752196200Sscottl } 753196200Sscottl qsort(state.volumes, config->log_drv_count, sizeof(int), 754196200Sscottl compare_int); 755196200Sscottl } else 756196200Sscottl state.volumes = NULL; 757196200Sscottl free(config); 758196200Sscottl 759196200Sscottl /* Determine the size of the configuration we will build. */ 760196200Sscottl switch (raid_type) { 761196200Sscottl case RT_RAID0: 762196200Sscottl case RT_RAID1: 763196200Sscottl case RT_RAID5: 764196200Sscottl case RT_RAID6: 765196200Sscottl case RT_CONCAT: 766196200Sscottl case RT_JBOD: 767196200Sscottl /* Each volume spans a single array. */ 768196200Sscottl nvolumes = narrays; 769196200Sscottl break; 770196200Sscottl case RT_RAID10: 771196200Sscottl case RT_RAID50: 772196200Sscottl case RT_RAID60: 773196200Sscottl /* A single volume spans multiple arrays. */ 774196200Sscottl nvolumes = 1; 775196200Sscottl break; 776196200Sscottl default: 777196200Sscottl /* Pacify gcc. */ 778196200Sscottl abort(); 779196200Sscottl } 780196200Sscottl 781196200Sscottl config_size = sizeof(struct mfi_config_data) + 782196200Sscottl sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 783196200Sscottl config = calloc(1, config_size); 784215526Sjhb if (config == NULL) { 785215526Sjhb warnx("malloc failed"); 786222899Sbz error = ENOMEM; 787222899Sbz goto error; 788215526Sjhb } 789196200Sscottl config->size = config_size; 790196200Sscottl config->array_count = narrays; 791196200Sscottl config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 792196200Sscottl config->log_drv_count = nvolumes; 793196200Sscottl config->log_drv_size = sizeof(struct mfi_ld_config); 794196200Sscottl config->spares_count = 0; 795196200Sscottl config->spares_size = 40; /* XXX: Firmware hardcode */ 796196200Sscottl cfg_arrays = (char *)config->array; 797196200Sscottl cfg_volumes = cfg_arrays + config->array_size * narrays; 798196200Sscottl 799196200Sscottl /* Build the arrays. */ 800196200Sscottl for (i = 0; i < narrays; i++) { 801196200Sscottl build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 802196200Sscottl cfg_arrays += config->array_size; 803196200Sscottl } 804196200Sscottl 805196200Sscottl /* Now build the volume(s). */ 806196200Sscottl arrays_per_volume = narrays / nvolumes; 807196200Sscottl for (i = 0; i < nvolumes; i++) { 808196200Sscottl build_volume(cfg_volumes, arrays_per_volume, 809196200Sscottl &arrays[i * arrays_per_volume], raid_type, stripe_size, 810196200Sscottl &state, verbose); 811196200Sscottl cfg_volumes += config->log_drv_size; 812196200Sscottl } 813196200Sscottl 814196200Sscottl#ifdef DEBUG 815196200Sscottl if (dump) 816251516Ssbruno dump_config(fd, config, NULL); 817196200Sscottl#endif 818196200Sscottl 819196200Sscottl /* Send the new config to the controller. */ 820196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 821196200Sscottl NULL, 0, NULL) < 0) { 822214396Sjhb error = errno; 823196200Sscottl warn("Failed to add volume"); 824222899Sbz /* FALLTHROUGH */ 825196200Sscottl } 826196200Sscottl 827222899Sbzerror: 828196200Sscottl /* Clean up. */ 829196200Sscottl free(config); 830222899Sbz free(state.volumes); 831222899Sbz free(state.arrays); 832228119Sdelphij if (arrays != NULL) { 833228119Sdelphij for (i = 0; i < narrays; i++) 834228119Sdelphij free(arrays[i].drives); 835228119Sdelphij free(arrays); 836228119Sdelphij } 837196200Sscottl close(fd); 838196200Sscottl 839222899Sbz return (error); 840196200Sscottl} 841196200SscottlMFI_COMMAND(top, create, create_volume); 842196200Sscottl 843196200Sscottlstatic int 844196200Sscottldelete_volume(int ac, char **av) 845196200Sscottl{ 846196200Sscottl struct mfi_ld_info info; 847214396Sjhb int error, fd; 848196200Sscottl uint8_t target_id, mbox[4]; 849196200Sscottl 850196200Sscottl /* 851196200Sscottl * Backwards compat. Map 'delete volume' to 'delete' and 852196200Sscottl * 'delete spare' to 'remove'. 853196200Sscottl */ 854196200Sscottl if (ac > 1) { 855196200Sscottl if (strcmp(av[1], "volume") == 0) { 856196200Sscottl av++; 857196200Sscottl ac--; 858196200Sscottl } else if (strcmp(av[1], "spare") == 0) { 859196200Sscottl av++; 860196200Sscottl ac--; 861196200Sscottl return (remove_spare(ac, av)); 862196200Sscottl } 863196200Sscottl } 864196200Sscottl 865196200Sscottl if (ac != 2) { 866196200Sscottl warnx("delete volume: volume required"); 867196200Sscottl return (EINVAL); 868196200Sscottl } 869196200Sscottl 870237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 871196200Sscottl if (fd < 0) { 872214396Sjhb error = errno; 873196200Sscottl warn("mfi_open"); 874214396Sjhb return (error); 875196200Sscottl } 876196200Sscottl 877196200Sscottl if (!mfi_reconfig_supported()) { 878196200Sscottl warnx("The current mfi(4) driver does not support " 879196200Sscottl "configuration changes."); 880222899Sbz close(fd); 881196200Sscottl return (EOPNOTSUPP); 882196200Sscottl } 883196200Sscottl 884196200Sscottl if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 885214396Sjhb error = errno; 886196200Sscottl warn("Invalid volume %s", av[1]); 887222899Sbz close(fd); 888214396Sjhb return (error); 889196200Sscottl } 890196200Sscottl 891196200Sscottl if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 892214396Sjhb error = errno; 893196200Sscottl warn("Failed to get info for volume %d", target_id); 894222899Sbz close(fd); 895214396Sjhb return (error); 896196200Sscottl } 897196200Sscottl 898196200Sscottl if (mfi_volume_busy(fd, target_id)) { 899196200Sscottl warnx("Volume %s is busy and cannot be deleted", 900196200Sscottl mfi_volume_name(fd, target_id)); 901222899Sbz close(fd); 902196200Sscottl return (EBUSY); 903196200Sscottl } 904196200Sscottl 905196200Sscottl mbox_store_ldref(mbox, &info.ld_config.properties.ld); 906196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 907196200Sscottl sizeof(mbox), NULL) < 0) { 908214396Sjhb error = errno; 909196200Sscottl warn("Failed to delete volume"); 910222899Sbz close(fd); 911214396Sjhb return (error); 912196200Sscottl } 913196200Sscottl 914196200Sscottl close(fd); 915196200Sscottl 916196200Sscottl return (0); 917196200Sscottl} 918196200SscottlMFI_COMMAND(top, delete, delete_volume); 919196200Sscottl 920196200Sscottlstatic int 921196200Sscottladd_spare(int ac, char **av) 922196200Sscottl{ 923196200Sscottl struct mfi_pd_info info; 924196200Sscottl struct mfi_config_data *config; 925196200Sscottl struct mfi_array *ar; 926196200Sscottl struct mfi_ld_config *ld; 927196200Sscottl struct mfi_spare *spare; 928196200Sscottl uint16_t device_id; 929196200Sscottl uint8_t target_id; 930196200Sscottl char *p; 931196200Sscottl int error, fd, i; 932196200Sscottl 933196200Sscottl if (ac < 2) { 934196200Sscottl warnx("add spare: drive required"); 935196200Sscottl return (EINVAL); 936196200Sscottl } 937196200Sscottl 938237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 939196200Sscottl if (fd < 0) { 940214396Sjhb error = errno; 941196200Sscottl warn("mfi_open"); 942214396Sjhb return (error); 943196200Sscottl } 944196200Sscottl 945222899Sbz config = NULL; 946222899Sbz spare = NULL; 947196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 948196200Sscottl if (error) 949222899Sbz goto error; 950196200Sscottl 951196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 952214396Sjhb error = errno; 953196200Sscottl warn("Failed to fetch drive info"); 954222899Sbz goto error; 955196200Sscottl } 956196200Sscottl 957196200Sscottl if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 958196200Sscottl warnx("Drive %u is not available", device_id); 959222899Sbz error = EINVAL; 960222899Sbz goto error; 961196200Sscottl } 962196200Sscottl 963196200Sscottl if (ac > 2) { 964196200Sscottl if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 965214396Sjhb error = errno; 966196200Sscottl warn("Invalid volume %s", av[2]); 967222899Sbz goto error; 968196200Sscottl } 969196200Sscottl } 970196200Sscottl 971196200Sscottl if (mfi_config_read(fd, &config) < 0) { 972214396Sjhb error = errno; 973196200Sscottl warn("Failed to read configuration"); 974222899Sbz goto error; 975196200Sscottl } 976196200Sscottl 977196200Sscottl spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 978196200Sscottl config->array_count); 979215526Sjhb if (spare == NULL) { 980215526Sjhb warnx("malloc failed"); 981222899Sbz error = ENOMEM; 982222899Sbz goto error; 983215526Sjhb } 984196200Sscottl bzero(spare, sizeof(struct mfi_spare)); 985196200Sscottl spare->ref = info.ref; 986196200Sscottl 987196200Sscottl if (ac == 2) { 988196200Sscottl /* Global spare backs all arrays. */ 989196200Sscottl p = (char *)config->array; 990196200Sscottl for (i = 0; i < config->array_count; i++) { 991196200Sscottl ar = (struct mfi_array *)p; 992196200Sscottl if (ar->size > info.coerced_size) { 993196200Sscottl warnx("Spare isn't large enough for array %u", 994196200Sscottl ar->array_ref); 995222899Sbz error = EINVAL; 996222899Sbz goto error; 997196200Sscottl } 998196200Sscottl p += config->array_size; 999196200Sscottl } 1000196200Sscottl spare->array_count = 0; 1001196200Sscottl } else { 1002196200Sscottl /* 1003196200Sscottl * Dedicated spares only back the arrays for a 1004196200Sscottl * specific volume. 1005196200Sscottl */ 1006196200Sscottl ld = mfi_config_lookup_volume(config, target_id); 1007196200Sscottl if (ld == NULL) { 1008196200Sscottl warnx("Did not find volume %d", target_id); 1009222899Sbz error = EINVAL; 1010222899Sbz goto error; 1011196200Sscottl } 1012196200Sscottl 1013196200Sscottl spare->spare_type |= MFI_SPARE_DEDICATED; 1014196200Sscottl spare->array_count = ld->params.span_depth; 1015196200Sscottl for (i = 0; i < ld->params.span_depth; i++) { 1016196200Sscottl ar = mfi_config_lookup_array(config, 1017196200Sscottl ld->span[i].array_ref); 1018196200Sscottl if (ar == NULL) { 1019196200Sscottl warnx("Missing array; inconsistent config?"); 1020222899Sbz error = ENXIO; 1021222899Sbz goto error; 1022196200Sscottl } 1023196200Sscottl if (ar->size > info.coerced_size) { 1024196200Sscottl warnx("Spare isn't large enough for array %u", 1025196200Sscottl ar->array_ref); 1026222899Sbz error = EINVAL; 1027222899Sbz goto error; 1028196200Sscottl } 1029196200Sscottl spare->array_ref[i] = ar->array_ref; 1030196200Sscottl } 1031196200Sscottl } 1032196200Sscottl 1033196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 1034196200Sscottl sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 1035196200Sscottl NULL, 0, NULL) < 0) { 1036214396Sjhb error = errno; 1037196200Sscottl warn("Failed to assign spare"); 1038222899Sbz /* FALLTHROUGH. */ 1039196200Sscottl } 1040196200Sscottl 1041222899Sbzerror: 1042222899Sbz free(spare); 1043222899Sbz free(config); 1044196200Sscottl close(fd); 1045196200Sscottl 1046222899Sbz return (error); 1047196200Sscottl} 1048196200SscottlMFI_COMMAND(top, add, add_spare); 1049196200Sscottl 1050196200Sscottlstatic int 1051196200Sscottlremove_spare(int ac, char **av) 1052196200Sscottl{ 1053196200Sscottl struct mfi_pd_info info; 1054196200Sscottl int error, fd; 1055196200Sscottl uint16_t device_id; 1056196200Sscottl uint8_t mbox[4]; 1057196200Sscottl 1058196200Sscottl if (ac != 2) { 1059196200Sscottl warnx("remove spare: drive required"); 1060196200Sscottl return (EINVAL); 1061196200Sscottl } 1062196200Sscottl 1063237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 1064196200Sscottl if (fd < 0) { 1065214396Sjhb error = errno; 1066196200Sscottl warn("mfi_open"); 1067214396Sjhb return (error); 1068196200Sscottl } 1069196200Sscottl 1070196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 1071222899Sbz if (error) { 1072222899Sbz close(fd); 1073196200Sscottl return (error); 1074222899Sbz } 1075196200Sscottl 1076196200Sscottl /* Get the info for this drive. */ 1077196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 1078214396Sjhb error = errno; 1079196200Sscottl warn("Failed to fetch info for drive %u", device_id); 1080222899Sbz close(fd); 1081214396Sjhb return (error); 1082196200Sscottl } 1083196200Sscottl 1084196200Sscottl if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 1085196200Sscottl warnx("Drive %u is not a hot spare", device_id); 1086222899Sbz close(fd); 1087196200Sscottl return (EINVAL); 1088196200Sscottl } 1089196200Sscottl 1090196200Sscottl mbox_store_pdref(mbox, &info.ref); 1091196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 1092196200Sscottl sizeof(mbox), NULL) < 0) { 1093214396Sjhb error = errno; 1094196200Sscottl warn("Failed to delete spare"); 1095222899Sbz close(fd); 1096214396Sjhb return (error); 1097196200Sscottl } 1098196200Sscottl 1099196200Sscottl close(fd); 1100196200Sscottl 1101196200Sscottl return (0); 1102196200Sscottl} 1103196200SscottlMFI_COMMAND(top, remove, remove_spare); 1104196200Sscottl 1105196200Sscottl/* Display raw data about a config. */ 1106251516Ssbrunovoid 1107251516Ssbrunodump_config(int fd, struct mfi_config_data *config, const char *msg_prefix) 1108196200Sscottl{ 1109196200Sscottl struct mfi_array *ar; 1110196200Sscottl struct mfi_ld_config *ld; 1111196200Sscottl struct mfi_spare *sp; 1112196200Sscottl struct mfi_pd_info pinfo; 1113196200Sscottl uint16_t device_id; 1114196200Sscottl char *p; 1115196200Sscottl int i, j; 1116196200Sscottl 1117251516Ssbruno if (NULL == msg_prefix) 1118251516Ssbruno msg_prefix = "Configuration (Debug)"; 1119251516Ssbruno 1120196200Sscottl printf( 1121251516Ssbruno "mfi%d %s: %d arrays, %d volumes, %d spares\n", mfi_unit, 1122251516Ssbruno msg_prefix, config->array_count, config->log_drv_count, 1123196200Sscottl config->spares_count); 1124196200Sscottl printf(" array size: %u\n", config->array_size); 1125196200Sscottl printf(" volume size: %u\n", config->log_drv_size); 1126196200Sscottl printf(" spare size: %u\n", config->spares_size); 1127196200Sscottl p = (char *)config->array; 1128196200Sscottl 1129196200Sscottl for (i = 0; i < config->array_count; i++) { 1130196200Sscottl ar = (struct mfi_array *)p; 1131196200Sscottl printf(" array %u of %u drives:\n", ar->array_ref, 1132196200Sscottl ar->num_drives); 1133196200Sscottl printf(" size = %ju\n", (uintmax_t)ar->size); 1134196200Sscottl for (j = 0; j < ar->num_drives; j++) { 1135213674Srandi device_id = ar->pd[j].ref.v.device_id; 1136196200Sscottl if (device_id == 0xffff) 1137196200Sscottl printf(" drive MISSING\n"); 1138196200Sscottl else { 1139196200Sscottl printf(" drive %u %s\n", device_id, 1140196200Sscottl mfi_pdstate(ar->pd[j].fw_state)); 1141196200Sscottl if (mfi_pd_get_info(fd, device_id, &pinfo, 1142196200Sscottl NULL) >= 0) { 1143196200Sscottl printf(" raw size: %ju\n", 1144196200Sscottl (uintmax_t)pinfo.raw_size); 1145196200Sscottl printf(" non-coerced size: %ju\n", 1146196200Sscottl (uintmax_t)pinfo.non_coerced_size); 1147196200Sscottl printf(" coerced size: %ju\n", 1148196200Sscottl (uintmax_t)pinfo.coerced_size); 1149196200Sscottl } 1150196200Sscottl } 1151196200Sscottl } 1152196200Sscottl p += config->array_size; 1153196200Sscottl } 1154196200Sscottl 1155196200Sscottl for (i = 0; i < config->log_drv_count; i++) { 1156196200Sscottl ld = (struct mfi_ld_config *)p; 1157196200Sscottl printf(" volume %s ", 1158196200Sscottl mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1159196200Sscottl printf("%s %s", 1160196200Sscottl mfi_raid_level(ld->params.primary_raid_level, 1161196200Sscottl ld->params.secondary_raid_level), 1162196200Sscottl mfi_ldstate(ld->params.state)); 1163196200Sscottl if (ld->properties.name[0] != '\0') 1164196200Sscottl printf(" <%s>", ld->properties.name); 1165196200Sscottl printf("\n"); 1166196200Sscottl printf(" primary raid level: %u\n", 1167196200Sscottl ld->params.primary_raid_level); 1168196200Sscottl printf(" raid level qualifier: %u\n", 1169196200Sscottl ld->params.raid_level_qualifier); 1170196200Sscottl printf(" secondary raid level: %u\n", 1171196200Sscottl ld->params.secondary_raid_level); 1172196200Sscottl printf(" stripe size: %u\n", ld->params.stripe_size); 1173196200Sscottl printf(" num drives: %u\n", ld->params.num_drives); 1174196200Sscottl printf(" init state: %u\n", ld->params.init_state); 1175196200Sscottl printf(" consistent: %u\n", ld->params.is_consistent); 1176196200Sscottl printf(" no bgi: %u\n", ld->properties.no_bgi); 1177196200Sscottl printf(" spans:\n"); 1178196200Sscottl for (j = 0; j < ld->params.span_depth; j++) { 1179196200Sscottl printf(" array %u @ ", ld->span[j].array_ref); 1180196200Sscottl printf("%ju : %ju\n", 1181196200Sscottl (uintmax_t)ld->span[j].start_block, 1182196200Sscottl (uintmax_t)ld->span[j].num_blocks); 1183196200Sscottl } 1184196200Sscottl p += config->log_drv_size; 1185196200Sscottl } 1186196200Sscottl 1187196200Sscottl for (i = 0; i < config->spares_count; i++) { 1188196200Sscottl sp = (struct mfi_spare *)p; 1189196200Sscottl printf(" %s spare %u ", 1190196200Sscottl sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1191213674Srandi "global", sp->ref.v.device_id); 1192196200Sscottl printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1193196200Sscottl printf(" backs:\n"); 1194196200Sscottl for (j = 0; j < sp->array_count; j++) 1195196200Sscottl printf(" array %u\n", sp->array_ref[j]); 1196196200Sscottl p += config->spares_size; 1197196200Sscottl } 1198196200Sscottl} 1199196200Sscottl 1200251516Ssbruno#ifdef DEBUG 1201196200Sscottlstatic int 1202196200Sscottldebug_config(int ac, char **av) 1203196200Sscottl{ 1204196200Sscottl struct mfi_config_data *config; 1205214396Sjhb int error, fd; 1206196200Sscottl 1207196200Sscottl if (ac != 1) { 1208196200Sscottl warnx("debug: extra arguments"); 1209196200Sscottl return (EINVAL); 1210196200Sscottl } 1211196200Sscottl 1212237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 1213196200Sscottl if (fd < 0) { 1214214396Sjhb error = errno; 1215196200Sscottl warn("mfi_open"); 1216214396Sjhb return (error); 1217196200Sscottl } 1218196200Sscottl 1219196200Sscottl /* Get the config from the controller. */ 1220196200Sscottl if (mfi_config_read(fd, &config) < 0) { 1221214396Sjhb error = errno; 1222196200Sscottl warn("Failed to get config"); 1223222899Sbz close(fd); 1224214396Sjhb return (error); 1225196200Sscottl } 1226196200Sscottl 1227196200Sscottl /* Dump out the configuration. */ 1228251516Ssbruno dump_config(fd, config, NULL); 1229196200Sscottl free(config); 1230196200Sscottl close(fd); 1231196200Sscottl 1232196200Sscottl return (0); 1233196200Sscottl} 1234196200SscottlMFI_COMMAND(top, debug, debug_config); 1235196200Sscottl 1236196200Sscottlstatic int 1237196200Sscottldump(int ac, char **av) 1238196200Sscottl{ 1239196200Sscottl struct mfi_config_data *config; 1240196200Sscottl char buf[64]; 1241196200Sscottl size_t len; 1242214396Sjhb int error, fd; 1243196200Sscottl 1244196200Sscottl if (ac != 1) { 1245196200Sscottl warnx("dump: extra arguments"); 1246196200Sscottl return (EINVAL); 1247196200Sscottl } 1248196200Sscottl 1249237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 1250196200Sscottl if (fd < 0) { 1251214396Sjhb error = errno; 1252196200Sscottl warn("mfi_open"); 1253214396Sjhb return (error); 1254196200Sscottl } 1255196200Sscottl 1256196200Sscottl /* Get the stashed copy of the last dcmd from the driver. */ 1257196200Sscottl snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1258196200Sscottl if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1259214396Sjhb error = errno; 1260196200Sscottl warn("Failed to read debug command"); 1261214396Sjhb if (error == ENOENT) 1262214396Sjhb error = EOPNOTSUPP; 1263222899Sbz close(fd); 1264214396Sjhb return (error); 1265196200Sscottl } 1266196200Sscottl 1267196200Sscottl config = malloc(len); 1268215526Sjhb if (config == NULL) { 1269215526Sjhb warnx("malloc failed"); 1270222899Sbz close(fd); 1271215526Sjhb return (ENOMEM); 1272215526Sjhb } 1273196200Sscottl if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1274214396Sjhb error = errno; 1275196200Sscottl warn("Failed to read debug command"); 1276222899Sbz free(config); 1277222899Sbz close(fd); 1278214396Sjhb return (error); 1279196200Sscottl } 1280251516Ssbruno dump_config(fd, config, NULL); 1281196200Sscottl free(config); 1282196200Sscottl close(fd); 1283196200Sscottl 1284196200Sscottl return (0); 1285196200Sscottl} 1286196200SscottlMFI_COMMAND(top, dump, dump); 1287196200Sscottl#endif 1288