1116743Ssam/*- 2186904Ssam * SPDX-License-Identifier: BSD-3-Clause 3116743Ssam * 4116743Ssam * Copyright (c) 2008, 2009 Yahoo!, Inc. 5116743Ssam * All rights reserved. 6116743Ssam * 7116743Ssam * Redistribution and use in source and binary forms, with or without 8116743Ssam * modification, are permitted provided that the following conditions 9116743Ssam * are met: 10116743Ssam * 1. Redistributions of source code must retain the above copyright 11116743Ssam * notice, this list of conditions and the following disclaimer. 12116743Ssam * 2. Redistributions in binary form must reproduce the above copyright 13116743Ssam * notice, this list of conditions and the following disclaimer in the 14116743Ssam * documentation and/or other materials provided with the distribution. 15116743Ssam * 3. The names of the authors may not be used to endorse or promote 16116743Ssam * products derived from this software without specific prior written 17116743Ssam * permission. 18116743Ssam * 19116743Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20116743Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21116743Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22116743Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23116743Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24116743Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25116743Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26116743Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27116743Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28116743Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29116743Ssam * SUCH DAMAGE. 30116743Ssam */ 31116743Ssam 32116743Ssam#include <sys/param.h> 33116743Ssam#ifdef DEBUG 34116743Ssam#include <sys/sysctl.h> 35116743Ssam#endif 36116743Ssam#include <ctype.h> 37116743Ssam#include <err.h> 38185522Ssam#include <errno.h> 39185522Ssam#include <fcntl.h> 40119783Ssam#include <libutil.h> 41116743Ssam#include <stdint.h> 42138570Ssam#include <stdio.h> 43116743Ssam#include <stdlib.h> 44116743Ssam#include <string.h> 45116743Ssam#include <unistd.h> 46155481Ssam#include "mfiutil.h" 47116743Ssam 48155481Ssamstatic int add_spare(int ac, char **av); 49155481Ssamstatic int remove_spare(int ac, char **av); 50170530Ssam 51155481Ssamstatic long 52178354Ssamdehumanize(const char *value) 53178354Ssam{ 54140438Ssam char *vtp; 55138570Ssam long iv; 56155480Ssam 57138570Ssam if (value == NULL) 58116743Ssam return (0); 59147067Ssam iv = strtoq(value, &vtp, 0); 60147067Ssam if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) { 61147067Ssam return (0); 62147067Ssam } 63147057Ssam switch (vtp[0]) { 64147057Ssam case 't': case 'T': 65147057Ssam iv *= 1024; 66147057Ssam case 'g': case 'G': 67147057Ssam iv *= 1024; 68147057Ssam case 'm': case 'M': 69147057Ssam iv *= 1024; 70147057Ssam case 'k': case 'K': 71147057Ssam iv *= 1024; 72147057Ssam case '\0': 73147057Ssam break; 74170530Ssam default: 75170530Ssam return (0); 76170530Ssam } 77170530Ssam return (iv); 78170530Ssam} 79170530Ssam 80170530Ssamint 81170530Ssammfi_config_read(int fd, struct mfi_config_data **configp) 82138570Ssam{ 83116743Ssam return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0); 84119150Ssam} 85178354Ssam 86178354Ssamint 87170530Ssammfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp, 88138570Ssam uint8_t *mbox, size_t mboxlen) 89116743Ssam{ 90138570Ssam struct mfi_config_data *config; 91138570Ssam uint32_t config_size; 92116743Ssam int error; 93138570Ssam 94138570Ssam /* 95138570Ssam * Keep fetching the config in a loop until we have a large enough 96138570Ssam * buffer to hold the entire configuration. 97138570Ssam */ 98138570Ssam config = NULL; 99138570Ssam config_size = 1024; 100138570Ssamfetch: 101138570Ssam config = reallocf(config, config_size); 102138570Ssam if (config == NULL) 103184358Ssam return (-1); 104184358Ssam if (mfi_dcmd_command(fd, opcode, config, 105184358Ssam config_size, mbox, mboxlen, NULL) < 0) { 106138570Ssam error = errno; 107116743Ssam free(config); 108138570Ssam errno = error; 109170530Ssam return (-1); 110170530Ssam } 111116743Ssam 112186904Ssam if (config->size > config_size) { 113186904Ssam config_size = config->size; 114116743Ssam goto fetch; 115165185Ssam } 116116743Ssam 117138570Ssam *configp = config; 118116743Ssam return (0); 119116743Ssam} 120116743Ssam 121140438Ssamstatic struct mfi_array * 122116743Ssammfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref) 123116743Ssam{ 124138570Ssam struct mfi_array *ar; 125116743Ssam char *p; 126186904Ssam int i; 127186904Ssam 128138570Ssam p = (char *)config->array; 129138570Ssam for (i = 0; i < config->array_count; i++) { 130138570Ssam ar = (struct mfi_array *)p; 131138570Ssam if (ar->array_ref == array_ref) 132138570Ssam return (ar); 133138570Ssam p += config->array_size; 134138570Ssam } 135158298Ssam 136138570Ssam return (NULL); 137138570Ssam} 138138570Ssam 139138570Ssamstatic struct mfi_ld_config * 140138570Ssammfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id) 141138570Ssam{ 142138570Ssam struct mfi_ld_config *ld; 143138570Ssam char *p; 144138570Ssam int i; 145138570Ssam 146138570Ssam p = (char *)config->array + config->array_count * config->array_size; 147138570Ssam for (i = 0; i < config->log_drv_count; i++) { 148138570Ssam ld = (struct mfi_ld_config *)p; 149138570Ssam if (ld->properties.ld.v.target_id == target_id) 150138570Ssam return (ld); 151138570Ssam p += config->log_drv_size; 152138570Ssam } 153178354Ssam 154186904Ssam return (NULL); 155186904Ssam} 156156073Ssam 157138570Ssamstatic int 158138570Ssamclear_config(int ac __unused, char **av __unused) 159138570Ssam{ 160138570Ssam struct mfi_ld_list list; 161155482Ssam int ch, error, fd; 162170530Ssam u_int i; 163170530Ssam 164170530Ssam fd = mfi_open(mfi_device, O_RDWR); 165170530Ssam if (fd < 0) { 166170530Ssam error = errno; 167170530Ssam warn("mfi_open"); 168170530Ssam return (error); 169138570Ssam } 170138570Ssam 171155482Ssam if (!mfi_reconfig_supported(mfi_device)) { 172155482Ssam warnx("The current %s driver does not support " 173155482Ssam "configuration changes.", mfi_device); 174167252Ssam close(fd); 175161425Simp return (EOPNOTSUPP); 176138570Ssam } 177138570Ssam 178138570Ssam if (mfi_ld_get_list(fd, &list, NULL) < 0) { 179138570Ssam error = errno; 180138570Ssam warn("Failed to get volume list"); 181138570Ssam close(fd); 182138570Ssam return (error); 183138570Ssam } 184170530Ssam 185138570Ssam for (i = 0; i < list.ld_count; i++) { 186138570Ssam if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) { 187138570Ssam warnx("Volume %s is busy and cannot be deleted", 188138570Ssam mfi_volume_name(fd, list.ld_list[i].ld.v.target_id)); 189138570Ssam close(fd); 190178354Ssam return (EBUSY); 191178354Ssam } 192178354Ssam } 193178354Ssam 194138570Ssam printf( 195178354Ssam "Are you sure you wish to clear the configuration on %s? [y/N] ", 196178354Ssam mfi_device); 197178354Ssam ch = getchar(); 198178354Ssam if (ch != 'y' && ch != 'Y') { 199178354Ssam printf("\nAborting\n"); 200178354Ssam close(fd); 201178354Ssam return (0); 202178354Ssam } 203178354Ssam 204178354Ssam if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) { 205178354Ssam error = errno; 206178354Ssam warn("Failed to clear configuration"); 207178354Ssam close(fd); 208178354Ssam return (error); 209178354Ssam } 210155491Ssam 211155486Ssam printf("%s: Configuration cleared\n", mfi_device); 212155486Ssam close(fd); 213116743Ssam 214147256Sbrooks return (0); 215138570Ssam} 216138570SsamMFI_COMMAND(top, clear, clear_config); 217178354Ssam 218178354Ssam#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE 219178354Ssam#define MFI_ARRAY_SIZE sizeof(struct mfi_array) 220178354Ssam 221178354Ssam#define RT_RAID0 0 222178354Ssam#define RT_RAID1 1 223138570Ssam#define RT_RAID5 2 224116743Ssam#define RT_RAID6 3 225159290Ssam#define RT_JBOD 4 226159290Ssam#define RT_CONCAT 5 227116743Ssam#define RT_RAID10 6 228116743Ssam#define RT_RAID50 7 229155491Ssam#define RT_RAID60 8 230116743Ssam 231138570Ssamstatic int 232155486Ssamcompare_int(const void *one, const void *two) 233138570Ssam{ 234178354Ssam int first, second; 235178354Ssam 236178354Ssam first = *(const int *)one; 237178354Ssam second = *(const int *)two; 238178354Ssam 239178354Ssam return (first - second); 240178354Ssam} 241178354Ssam 242178354Ssamstatic struct raid_type_entry { 243178354Ssam const char *name; 244178354Ssam int raid_type; 245155496Ssam} raid_type_table[] = { 246178354Ssam { "raid0", RT_RAID0 }, 247165571Ssam { "raid-0", RT_RAID0 }, 248170530Ssam { "raid1", RT_RAID1 }, 249178354Ssam { "raid-1", RT_RAID1 }, 250178354Ssam { "mirror", RT_RAID1 }, 251178354Ssam { "raid5", RT_RAID5 }, 252178354Ssam { "raid-5", RT_RAID5 }, 253178354Ssam { "raid6", RT_RAID6 }, 254178354Ssam { "raid-6", RT_RAID6 }, 255179401Ssam { "jbod", RT_JBOD }, 256185744Ssam { "concat", RT_CONCAT }, 257186904Ssam { "raid10", RT_RAID10 }, 258189380Ssam { "raid1+0", RT_RAID10 }, 259185744Ssam { "raid-10", RT_RAID10 }, 260178751Ssam { "raid-1+0", RT_RAID10 }, 261178751Ssam { "raid50", RT_RAID50 }, 262116743Ssam { "raid5+0", RT_RAID50 }, 263188783Ssam { "raid-50", RT_RAID50 }, 264116743Ssam { "raid-5+0", RT_RAID50 }, 265116743Ssam { "raid60", RT_RAID60 }, 266155490Ssam { "raid6+0", RT_RAID60 }, 267138570Ssam { "raid-60", RT_RAID60 }, 268170530Ssam { "raid-6+0", RT_RAID60 }, 269187831Ssam { NULL, 0 }, 270170530Ssam}; 271116743Ssam 272140432Ssamstruct config_id_state { 273140432Ssam int array_count; 274140761Ssam int log_drv_count; 275140761Ssam int *arrays; 276140432Ssam int *volumes; 277140432Ssam uint16_t array_ref; 278140432Ssam uint8_t target_id; 279138570Ssam}; 280170530Ssam 281155483Ssamstruct array_info { 282170530Ssam int drive_count; 283170530Ssam struct mfi_pd_info *drives; 284138570Ssam struct mfi_array *array; 285116743Ssam}; 286138570Ssam 287147057Ssam/* Parse a comma-separated list of drives for an array. */ 288116743Ssamstatic int 289140432Ssamparse_array(int fd, int raid_type, char *array_str, struct array_info *info) 290140432Ssam{ 291140432Ssam struct mfi_pd_info *pinfo; 292140432Ssam uint16_t device_id; 293184368Ssam char *cp; 294140432Ssam u_int count; 295140432Ssam int error; 296138570Ssam 297155515Ssam cp = array_str; 298155515Ssam for (count = 0; cp != NULL; count++) { 299155515Ssam cp = strchr(cp, ','); 300178354Ssam if (cp != NULL) { 301127698Ssam cp++; 302178354Ssam if (*cp == ',') { 303140761Ssam warnx("Invalid drive list '%s'", array_str); 304154140Ssam return (EINVAL); 305119783Ssam } 306178354Ssam } 307138570Ssam } 308170530Ssam 309116743Ssam /* Validate the number of drives for this array. */ 310116743Ssam if (count >= MAX_DRIVES_PER_ARRAY) { 311138570Ssam warnx("Too many drives for a single array: max is %d", 312138570Ssam MAX_DRIVES_PER_ARRAY); 313155492Ssam return (EINVAL); 314116743Ssam } 315138570Ssam switch (raid_type) { 316138570Ssam case RT_RAID1: 317138570Ssam case RT_RAID10: 318155482Ssam if (count % 2 != 0) { 319138570Ssam warnx("RAID1 and RAID10 require an even number of " 320138570Ssam "drives in each array"); 321138570Ssam return (EINVAL); 322138570Ssam } 323116743Ssam break; 324116743Ssam case RT_RAID5: 325138570Ssam case RT_RAID50: 326138570Ssam if (count < 3) { 327116743Ssam warnx("RAID5 and RAID50 require at least 3 drives in " 328138570Ssam "each array"); 329138570Ssam return (EINVAL); 330138570Ssam } 331116743Ssam break; 332138570Ssam case RT_RAID6: 333138570Ssam case RT_RAID60: 334138570Ssam if (count < 4) { 335138570Ssam warnx("RAID6 and RAID60 require at least 4 drives in " 336138570Ssam "each array"); 337138570Ssam return (EINVAL); 338178354Ssam } 339178354Ssam break; 340178354Ssam } 341116743Ssam 342116743Ssam /* Validate each drive. */ 343185744Ssam info->drives = calloc(count, sizeof(struct mfi_pd_info)); 344185744Ssam if (info->drives == NULL) { 345155485Ssam warnx("malloc failed"); 346186904Ssam return (ENOMEM); 347186904Ssam } 348186904Ssam info->drive_count = count; 349186904Ssam for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL; 350186904Ssam pinfo++) { 351186904Ssam error = mfi_lookup_drive(fd, cp, &device_id); 352186904Ssam if (error) { 353186904Ssam free(info->drives); 354186904Ssam info->drives = NULL; 355186904Ssam return (error); 356186904Ssam } 357186904Ssam 358116743Ssam if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) { 359116743Ssam error = errno; 360121100Ssam warn("Failed to fetch drive info for drive %s", cp); 361121100Ssam free(info->drives); 362167252Ssam info->drives = NULL; 363121100Ssam return (error); 364121100Ssam } 365121100Ssam 366121100Ssam if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 367121100Ssam warnx("Drive %u is not available", device_id); 368138570Ssam free(info->drives); 369138570Ssam info->drives = NULL; 370155482Ssam return (EINVAL); 371155482Ssam } 372155482Ssam 373167252Ssam if (pinfo->state.ddf.v.pd_type.is_foreign) { 374155482Ssam warnx("Drive %u is foreign", device_id); 375121100Ssam free(info->drives); 376121100Ssam info->drives = NULL; 377121100Ssam return (EINVAL); 378121100Ssam } 379121100Ssam } 380121100Ssam 381116743Ssam return (0); 382116743Ssam} 383116743Ssam 384116743Ssam/* 385116743Ssam * Find the next free array ref assuming that 'array_ref' is the last 386116743Ssam * one used. 'array_ref' should be 0xffff for the initial test. 387116743Ssam */ 388116743Ssamstatic uint16_t 389116743Ssamfind_next_array(struct config_id_state *state) 390116743Ssam{ 391138570Ssam int i; 392138570Ssam 393116743Ssam /* Assume the current one is used. */ 394116743Ssam state->array_ref++; 395186904Ssam 396186904Ssam /* Find the next free one. */ 397116743Ssam for (i = 0; i < state->array_count; i++) 398116743Ssam if (state->arrays[i] == state->array_ref) 399116743Ssam state->array_ref++; 400116743Ssam return (state->array_ref); 401138570Ssam} 402138570Ssam 403178354Ssam/* 404178354Ssam * Find the next free volume ID assuming that 'target_id' is the last 405178354Ssam * one used. 'target_id' should be 0xff for the initial test. 406178354Ssam */ 407116743Ssamstatic uint8_t 408116743Ssamfind_next_volume(struct config_id_state *state) 409116743Ssam{ 410116743Ssam int i; 411116743Ssam 412116743Ssam /* Assume the current one is used. */ 413116743Ssam state->target_id++; 414116743Ssam 415116743Ssam /* Find the next free one. */ 416116743Ssam for (i = 0; i < state->log_drv_count; i++) 417155515Ssam if (state->volumes[i] == state->target_id) 418155515Ssam state->target_id++; 419138570Ssam return (state->target_id); 420138570Ssam} 421116743Ssam 422116743Ssam/* Populate an array with drives. */ 423138570Ssamstatic void 424138570Ssambuild_array(int fd __unused, char *arrayp, struct array_info *array_info, 425116743Ssam struct config_id_state *state, int verbose) 426116743Ssam{ 427116743Ssam struct mfi_array *ar = (struct mfi_array *)arrayp; 428116743Ssam int i; 429116743Ssam 430116743Ssam ar->size = array_info->drives[0].coerced_size; 431116743Ssam ar->num_drives = array_info->drive_count; 432116743Ssam ar->array_ref = find_next_array(state); 433116743Ssam for (i = 0; i < array_info->drive_count; i++) { 434116743Ssam if (verbose) 435116743Ssam printf("Adding drive %s to array %u\n", 436116743Ssam mfi_drive_name(NULL, 437116743Ssam array_info->drives[i].ref.v.device_id, 438116743Ssam MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS), 439186904Ssam ar->array_ref); 440186904Ssam if (ar->size > array_info->drives[i].coerced_size) 441116743Ssam ar->size = array_info->drives[i].coerced_size; 442186904Ssam ar->pd[i].ref = array_info->drives[i].ref; 443116743Ssam ar->pd[i].fw_state = MFI_PD_STATE_ONLINE; 444116743Ssam } 445116743Ssam array_info->array = ar; 446116743Ssam} 447116743Ssam 448116743Ssam/* 449116743Ssam * Create a volume that spans one or more arrays. 450116743Ssam */ 451116743Ssamstatic void 452116743Ssambuild_volume(char *volumep, int narrays, struct array_info *arrays, 453138570Ssam int raid_type, long stripe_size, struct config_id_state *state, int verbose) 454138570Ssam{ 455116743Ssam struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep; 456116743Ssam struct mfi_array *ar; 457116743Ssam int i; 458116743Ssam 459116743Ssam /* properties */ 460116743Ssam ld->properties.ld.v.target_id = find_next_volume(state); 461155515Ssam ld->properties.ld.v.seq = 0; 462155515Ssam ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 463185744Ssam MR_LD_CACHE_WRITE_BACK; 464185744Ssam ld->properties.access_policy = MFI_LD_ACCESS_RW; 465185744Ssam ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED; 466185744Ssam ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE | 467185744Ssam MR_LD_CACHE_WRITE_BACK; 468185744Ssam ld->properties.no_bgi = 0; 469185744Ssam 470185744Ssam /* params */ 471185744Ssam switch (raid_type) { 472185744Ssam case RT_RAID0: 473116743Ssam case RT_JBOD: 474116743Ssam ld->params.primary_raid_level = DDF_RAID0; 475138570Ssam ld->params.raid_level_qualifier = 0; 476138570Ssam ld->params.secondary_raid_level = 0; 477116743Ssam break; 478116743Ssam case RT_RAID1: 479186904Ssam ld->params.primary_raid_level = DDF_RAID1; 480186904Ssam ld->params.raid_level_qualifier = 0; 481138570Ssam ld->params.secondary_raid_level = 0; 482138570Ssam break; 483116743Ssam case RT_RAID5: 484138570Ssam ld->params.primary_raid_level = DDF_RAID5; 485138570Ssam ld->params.raid_level_qualifier = 3; 486138570Ssam ld->params.secondary_raid_level = 0; 487138570Ssam break; 488138570Ssam case RT_RAID6: 489116743Ssam ld->params.primary_raid_level = DDF_RAID6; 490116743Ssam ld->params.raid_level_qualifier = 3; 491116743Ssam ld->params.secondary_raid_level = 0; 492116743Ssam break; 493116743Ssam case RT_CONCAT: 494116743Ssam ld->params.primary_raid_level = DDF_CONCAT; 495116743Ssam ld->params.raid_level_qualifier = 0; 496116743Ssam ld->params.secondary_raid_level = 0; 497138570Ssam break; 498138570Ssam case RT_RAID10: 499138570Ssam ld->params.primary_raid_level = DDF_RAID1; 500155732Ssam ld->params.raid_level_qualifier = 0; 501170530Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 502116743Ssam break; 503116743Ssam case RT_RAID50: 504116743Ssam /* 505116743Ssam * XXX: This appears to work though the card's BIOS 506116743Ssam * complains that the configuration is foreign. The 507116743Ssam * BIOS setup does not allow for creation of RAID-50 508138570Ssam * or RAID-60 arrays. The only nested array 509138570Ssam * configuration it allows for is RAID-10. 510138570Ssam */ 511138570Ssam ld->params.primary_raid_level = DDF_RAID5; 512186904Ssam ld->params.raid_level_qualifier = 3; 513186904Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 514186904Ssam break; 515186904Ssam case RT_RAID60: 516116743Ssam ld->params.primary_raid_level = DDF_RAID6; 517116743Ssam ld->params.raid_level_qualifier = 3; 518138570Ssam ld->params.secondary_raid_level = 3; /* XXX? */ 519138570Ssam break; 520138570Ssam } 521138570Ssam 522155515Ssam /* 523155515Ssam * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use 524138570Ssam * ffs() to simulate log2(stripe_size). 525138570Ssam */ 526138570Ssam ld->params.stripe_size = ffs(stripe_size) - 1 - 9; 527138570Ssam ld->params.num_drives = arrays[0].array->num_drives; 528138570Ssam ld->params.span_depth = narrays; 529138570Ssam ld->params.state = MFI_LD_STATE_OPTIMAL; 530138570Ssam ld->params.init_state = MFI_LD_PARAMS_INIT_NO; 531138570Ssam ld->params.is_consistent = 0; 532138570Ssam 533138570Ssam /* spans */ 534138570Ssam for (i = 0; i < narrays; i++) { 535138570Ssam ar = arrays[i].array; 536138570Ssam if (verbose) 537138570Ssam printf("Adding array %u to volume %u\n", ar->array_ref, 538138570Ssam ld->properties.ld.v.target_id); 539138570Ssam ld->span[i].start_block = 0; 540138570Ssam ld->span[i].num_blocks = ar->size; 541138570Ssam ld->span[i].array_ref = ar->array_ref; 542138570Ssam } 543138570Ssam} 544138570Ssam 545155515Ssamstatic int 546184369Ssamcreate_volume(int ac, char **av) 547184369Ssam{ 548183221Ssam struct mfi_config_data *config; 549155489Ssam struct mfi_array *ar; 550183221Ssam struct mfi_ld_config *ld; 551183221Ssam struct config_id_state state; 552183221Ssam size_t config_size; 553182893Srpaulo char *p, *cfg_arrays, *cfg_volumes; 554183221Ssam int error, fd, i, raid_type; 555184369Ssam int narrays, nvolumes, arrays_per_volume; 556184369Ssam struct array_info *arrays; 557184369Ssam long stripe_size; 558184369Ssam#ifdef DEBUG 559138570Ssam int dump; 560138570Ssam#endif 561178354Ssam int ch, verbose; 562178354Ssam 563178354Ssam /* 564178354Ssam * Backwards compat. Map 'create volume' to 'create' and 565162410Ssam * 'create spare' to 'add'. 566138570Ssam */ 567162410Ssam if (ac > 1) { 568162410Ssam if (strcmp(av[1], "volume") == 0) { 569162410Ssam av++; 570162410Ssam ac--; 571178354Ssam } else if (strcmp(av[1], "spare") == 0) { 572178354Ssam av++; 573138570Ssam ac--; 574138570Ssam return (add_spare(ac, av)); 575138570Ssam } 576138570Ssam } 577138570Ssam 578138570Ssam if (ac < 2) { 579138570Ssam warnx("create volume: volume type required"); 580138570Ssam return (EINVAL); 581166954Ssam } 582166954Ssam 583166954Ssam bzero(&state, sizeof(state)); 584166954Ssam config = NULL; 585138570Ssam arrays = NULL; 586138570Ssam narrays = 0; 587138570Ssam error = 0; 588138570Ssam 589138570Ssam fd = mfi_open(mfi_device, O_RDWR); 590138570Ssam if (fd < 0) { 591138570Ssam error = errno; 592138570Ssam warn("mfi_open"); 593138570Ssam return (error); 594138570Ssam } 595138570Ssam 596138570Ssam if (!mfi_reconfig_supported(mfi_device)) { 597138570Ssam warnx("The current %s(4) driver does not support " 598138570Ssam "configuration changes.", mfi_device); 599138570Ssam error = EOPNOTSUPP; 600138570Ssam goto error; 601138570Ssam } 602138570Ssam 603138570Ssam /* Lookup the RAID type first. */ 604138570Ssam raid_type = -1; 605138570Ssam for (i = 0; raid_type_table[i].name != NULL; i++) 606138570Ssam if (strcasecmp(raid_type_table[i].name, av[1]) == 0) { 607138570Ssam raid_type = raid_type_table[i].raid_type; 608138570Ssam break; 609138570Ssam } 610138570Ssam 611138570Ssam if (raid_type == -1) { 612138570Ssam warnx("Unknown or unsupported volume type %s", av[1]); 613147057Ssam error = EINVAL; 614147057Ssam goto error; 615147057Ssam } 616147057Ssam 617147057Ssam /* Parse any options. */ 618147057Ssam optind = 2; 619147057Ssam#ifdef DEBUG 620147057Ssam dump = 0; 621170530Ssam#endif 622170530Ssam verbose = 0; 623178354Ssam stripe_size = 64 * 1024; 624178354Ssam 625178354Ssam while ((ch = getopt(ac, av, "ds:v")) != -1) { 626178354Ssam switch (ch) { 627178354Ssam#ifdef DEBUG 628178354Ssam case 'd': 629178354Ssam dump = 1; 630178354Ssam break; 631155515Ssam#endif 632155515Ssam case 's': 633155515Ssam stripe_size = dehumanize(optarg); 634155515Ssam if ((stripe_size < 512) || (!powerof2(stripe_size))) 635155515Ssam stripe_size = 64 * 1024; 636155515Ssam break; 637155515Ssam case 'v': 638155515Ssam verbose = 1; 639155515Ssam break; 640155515Ssam case '?': 641155515Ssam default: 642155515Ssam error = EINVAL; 643155515Ssam goto error; 644155515Ssam } 645155515Ssam } 646155515Ssam ac -= optind; 647155515Ssam av += optind; 648155515Ssam 649184354Ssam /* Parse all the arrays. */ 650184354Ssam narrays = ac; 651184354Ssam if (narrays == 0) { 652184354Ssam warnx("At least one drive list is required"); 653184354Ssam error = EINVAL; 654184354Ssam goto error; 655154140Ssam } 656154140Ssam switch (raid_type) { 657155515Ssam case RT_RAID0: 658155515Ssam case RT_RAID1: 659155515Ssam case RT_RAID5: 660155515Ssam case RT_RAID6: 661155515Ssam case RT_CONCAT: 662155515Ssam if (narrays != 1) { 663155515Ssam warnx("Only one drive list can be specified"); 664165571Ssam error = EINVAL; 665165571Ssam goto error; 666165571Ssam } 667165571Ssam break; 668165571Ssam case RT_RAID10: 669166016Ssam case RT_RAID50: 670166016Ssam case RT_RAID60: 671166016Ssam if (narrays < 1) { 672166016Ssam warnx("RAID10, RAID50, and RAID60 require at least " 673166016Ssam "two drive lists"); 674170530Ssam error = EINVAL; 675170530Ssam goto error; 676170530Ssam } 677170530Ssam if (narrays > MFI_MAX_SPAN_DEPTH) { 678170530Ssam warnx("Volume spans more than %d arrays", 679170530Ssam MFI_MAX_SPAN_DEPTH); 680170530Ssam error = EINVAL; 681170530Ssam goto error; 682116743Ssam } 683116743Ssam break; 684116743Ssam } 685165185Ssam arrays = calloc(narrays, sizeof(*arrays)); 686165185Ssam if (arrays == NULL) { 687116743Ssam warnx("malloc failed"); 688116743Ssam error = ENOMEM; 689116743Ssam goto error; 690116743Ssam } 691116743Ssam for (i = 0; i < narrays; i++) { 692155515Ssam error = parse_array(fd, raid_type, av[i], &arrays[i]); 693138570Ssam if (error) 694116743Ssam goto error; 695138570Ssam } 696116743Ssam 697138570Ssam switch (raid_type) { 698138570Ssam case RT_RAID10: 699165185Ssam case RT_RAID50: 700165185Ssam case RT_RAID60: 701155515Ssam for (i = 1; i < narrays; i++) { 702155515Ssam if (arrays[i].drive_count != arrays[0].drive_count) { 703116743Ssam warnx("All arrays must contain the same " 704188974Ssam "number of drives"); 705188974Ssam error = EINVAL; 706138570Ssam goto error; 707138570Ssam } 708155515Ssam } 709155515Ssam break; 710155515Ssam } 711155515Ssam 712138570Ssam /* 713155515Ssam * Fetch the current config and build sorted lists of existing 714155515Ssam * array and volume identifiers. 715155515Ssam */ 716116743Ssam if (mfi_config_read(fd, &config) < 0) { 717 error = errno; 718 warn("Failed to read configuration"); 719 goto error; 720 } 721 p = (char *)config->array; 722 state.array_ref = 0xffff; 723 state.target_id = 0xff; 724 state.array_count = config->array_count; 725 if (config->array_count > 0) { 726 state.arrays = calloc(config->array_count, sizeof(int)); 727 if (state.arrays == NULL) { 728 warnx("malloc failed"); 729 error = ENOMEM; 730 goto error; 731 } 732 for (i = 0; i < config->array_count; i++) { 733 ar = (struct mfi_array *)p; 734 state.arrays[i] = ar->array_ref; 735 p += config->array_size; 736 } 737 qsort(state.arrays, config->array_count, sizeof(int), 738 compare_int); 739 } else 740 state.arrays = NULL; 741 state.log_drv_count = config->log_drv_count; 742 if (config->log_drv_count) { 743 state.volumes = calloc(config->log_drv_count, sizeof(int)); 744 if (state.volumes == NULL) { 745 warnx("malloc failed"); 746 error = ENOMEM; 747 goto error; 748 } 749 for (i = 0; i < config->log_drv_count; i++) { 750 ld = (struct mfi_ld_config *)p; 751 state.volumes[i] = ld->properties.ld.v.target_id; 752 p += config->log_drv_size; 753 } 754 qsort(state.volumes, config->log_drv_count, sizeof(int), 755 compare_int); 756 } else 757 state.volumes = NULL; 758 free(config); 759 760 /* Determine the size of the configuration we will build. */ 761 switch (raid_type) { 762 case RT_RAID0: 763 case RT_RAID1: 764 case RT_RAID5: 765 case RT_RAID6: 766 case RT_CONCAT: 767 case RT_JBOD: 768 /* Each volume spans a single array. */ 769 nvolumes = narrays; 770 break; 771 case RT_RAID10: 772 case RT_RAID50: 773 case RT_RAID60: 774 /* A single volume spans multiple arrays. */ 775 nvolumes = 1; 776 break; 777 default: 778 /* Pacify gcc. */ 779 abort(); 780 } 781 782 config_size = sizeof(struct mfi_config_data) + 783 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays; 784 config = calloc(1, config_size); 785 if (config == NULL) { 786 warnx("malloc failed"); 787 error = ENOMEM; 788 goto error; 789 } 790 config->size = config_size; 791 config->array_count = narrays; 792 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */ 793 config->log_drv_count = nvolumes; 794 config->log_drv_size = sizeof(struct mfi_ld_config); 795 config->spares_count = 0; 796 config->spares_size = 40; /* XXX: Firmware hardcode */ 797 cfg_arrays = (char *)config->array; 798 cfg_volumes = cfg_arrays + config->array_size * narrays; 799 800 /* Build the arrays. */ 801 for (i = 0; i < narrays; i++) { 802 build_array(fd, cfg_arrays, &arrays[i], &state, verbose); 803 cfg_arrays += config->array_size; 804 } 805 806 /* Now build the volume(s). */ 807 arrays_per_volume = narrays / nvolumes; 808 for (i = 0; i < nvolumes; i++) { 809 build_volume(cfg_volumes, arrays_per_volume, 810 &arrays[i * arrays_per_volume], raid_type, stripe_size, 811 &state, verbose); 812 cfg_volumes += config->log_drv_size; 813 } 814 815#ifdef DEBUG 816 if (dump) 817 dump_config(fd, config, NULL); 818#endif 819 820 /* Send the new config to the controller. */ 821 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size, 822 NULL, 0, NULL) < 0) { 823 error = errno; 824 warn("Failed to add volume"); 825 /* FALLTHROUGH */ 826 } 827 828error: 829 /* Clean up. */ 830 free(config); 831 free(state.volumes); 832 free(state.arrays); 833 if (arrays != NULL) { 834 for (i = 0; i < narrays; i++) 835 free(arrays[i].drives); 836 free(arrays); 837 } 838 close(fd); 839 840 return (error); 841} 842MFI_COMMAND(top, create, create_volume); 843 844static int 845delete_volume(int ac, char **av) 846{ 847 struct mfi_ld_info info; 848 int error, fd; 849 uint8_t target_id, mbox[4]; 850 851 /* 852 * Backwards compat. Map 'delete volume' to 'delete' and 853 * 'delete spare' to 'remove'. 854 */ 855 if (ac > 1) { 856 if (strcmp(av[1], "volume") == 0) { 857 av++; 858 ac--; 859 } else if (strcmp(av[1], "spare") == 0) { 860 av++; 861 ac--; 862 return (remove_spare(ac, av)); 863 } 864 } 865 866 if (ac != 2) { 867 warnx("delete volume: volume required"); 868 return (EINVAL); 869 } 870 871 fd = mfi_open(mfi_device, O_RDWR); 872 if (fd < 0) { 873 error = errno; 874 warn("mfi_open"); 875 return (error); 876 } 877 878 if (!mfi_reconfig_supported(mfi_device)) { 879 warnx("The current %s(4) driver does not support " 880 "configuration changes.", mfi_device); 881 close(fd); 882 return (EOPNOTSUPP); 883 } 884 885 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 886 error = errno; 887 warn("Invalid volume %s", av[1]); 888 close(fd); 889 return (error); 890 } 891 892 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 893 error = errno; 894 warn("Failed to get info for volume %d", target_id); 895 close(fd); 896 return (error); 897 } 898 899 if (mfi_volume_busy(fd, target_id)) { 900 warnx("Volume %s is busy and cannot be deleted", 901 mfi_volume_name(fd, target_id)); 902 close(fd); 903 return (EBUSY); 904 } 905 906 mbox_store_ldref(mbox, &info.ld_config.properties.ld); 907 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox, 908 sizeof(mbox), NULL) < 0) { 909 error = errno; 910 warn("Failed to delete volume"); 911 close(fd); 912 return (error); 913 } 914 915 close(fd); 916 917 return (0); 918} 919MFI_COMMAND(top, delete, delete_volume); 920 921static int 922add_spare(int ac, char **av) 923{ 924 struct mfi_pd_info info; 925 struct mfi_config_data *config; 926 struct mfi_array *ar; 927 struct mfi_ld_config *ld; 928 struct mfi_spare *spare; 929 uint16_t device_id; 930 uint8_t target_id; 931 char *p; 932 int error, fd, i; 933 934 if (ac < 2) { 935 warnx("add spare: drive required"); 936 return (EINVAL); 937 } 938 939 fd = mfi_open(mfi_device, O_RDWR); 940 if (fd < 0) { 941 error = errno; 942 warn("mfi_open"); 943 return (error); 944 } 945 946 config = NULL; 947 spare = NULL; 948 error = mfi_lookup_drive(fd, av[1], &device_id); 949 if (error) 950 goto error; 951 952 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 953 error = errno; 954 warn("Failed to fetch drive info"); 955 goto error; 956 } 957 958 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) { 959 warnx("Drive %u is not available", device_id); 960 error = EINVAL; 961 goto error; 962 } 963 964 if (ac > 2) { 965 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) { 966 error = errno; 967 warn("Invalid volume %s", av[2]); 968 goto error; 969 } 970 } 971 972 if (mfi_config_read(fd, &config) < 0) { 973 error = errno; 974 warn("Failed to read configuration"); 975 goto error; 976 } 977 978 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) * 979 config->array_count); 980 if (spare == NULL) { 981 warnx("malloc failed"); 982 error = ENOMEM; 983 goto error; 984 } 985 bzero(spare, sizeof(struct mfi_spare)); 986 spare->ref = info.ref; 987 988 if (ac == 2) { 989 /* Global spare backs all arrays. */ 990 p = (char *)config->array; 991 for (i = 0; i < config->array_count; i++) { 992 ar = (struct mfi_array *)p; 993 if (ar->size > info.coerced_size) { 994 warnx("Spare isn't large enough for array %u", 995 ar->array_ref); 996 error = EINVAL; 997 goto error; 998 } 999 p += config->array_size; 1000 } 1001 spare->array_count = 0; 1002 } else { 1003 /* 1004 * Dedicated spares only back the arrays for a 1005 * specific volume. 1006 */ 1007 ld = mfi_config_lookup_volume(config, target_id); 1008 if (ld == NULL) { 1009 warnx("Did not find volume %d", target_id); 1010 error = EINVAL; 1011 goto error; 1012 } 1013 1014 spare->spare_type |= MFI_SPARE_DEDICATED; 1015 spare->array_count = ld->params.span_depth; 1016 for (i = 0; i < ld->params.span_depth; i++) { 1017 ar = mfi_config_lookup_array(config, 1018 ld->span[i].array_ref); 1019 if (ar == NULL) { 1020 warnx("Missing array; inconsistent config?"); 1021 error = ENXIO; 1022 goto error; 1023 } 1024 if (ar->size > info.coerced_size) { 1025 warnx("Spare isn't large enough for array %u", 1026 ar->array_ref); 1027 error = EINVAL; 1028 goto error; 1029 } 1030 spare->array_ref[i] = ar->array_ref; 1031 } 1032 } 1033 1034 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare, 1035 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count, 1036 NULL, 0, NULL) < 0) { 1037 error = errno; 1038 warn("Failed to assign spare"); 1039 /* FALLTHROUGH. */ 1040 } 1041 1042error: 1043 free(spare); 1044 free(config); 1045 close(fd); 1046 1047 return (error); 1048} 1049MFI_COMMAND(top, add, add_spare); 1050 1051static int 1052remove_spare(int ac, char **av) 1053{ 1054 struct mfi_pd_info info; 1055 int error, fd; 1056 uint16_t device_id; 1057 uint8_t mbox[4]; 1058 1059 if (ac != 2) { 1060 warnx("remove spare: drive required"); 1061 return (EINVAL); 1062 } 1063 1064 fd = mfi_open(mfi_device, O_RDWR); 1065 if (fd < 0) { 1066 error = errno; 1067 warn("mfi_open"); 1068 return (error); 1069 } 1070 1071 error = mfi_lookup_drive(fd, av[1], &device_id); 1072 if (error) { 1073 close(fd); 1074 return (error); 1075 } 1076 1077 /* Get the info for this drive. */ 1078 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 1079 error = errno; 1080 warn("Failed to fetch info for drive %u", device_id); 1081 close(fd); 1082 return (error); 1083 } 1084 1085 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) { 1086 warnx("Drive %u is not a hot spare", device_id); 1087 close(fd); 1088 return (EINVAL); 1089 } 1090 1091 mbox_store_pdref(mbox, &info.ref); 1092 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox, 1093 sizeof(mbox), NULL) < 0) { 1094 error = errno; 1095 warn("Failed to delete spare"); 1096 close(fd); 1097 return (error); 1098 } 1099 1100 close(fd); 1101 1102 return (0); 1103} 1104MFI_COMMAND(top, remove, remove_spare); 1105 1106/* Display raw data about a config. */ 1107void 1108dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix) 1109{ 1110 struct mfi_array *ar; 1111 struct mfi_ld_config *ld; 1112 struct mfi_spare *sp; 1113 struct mfi_pd_info pinfo; 1114 uint16_t device_id; 1115 char *p; 1116 int i, j; 1117 1118 if (NULL == msg_prefix) 1119 msg_prefix = "Configuration (Debug)"; 1120 1121 printf( 1122 "%s %s: %d arrays, %d volumes, %d spares\n", mfi_device, 1123 msg_prefix, config->array_count, config->log_drv_count, 1124 config->spares_count); 1125 printf(" array size: %u\n", config->array_size); 1126 printf(" volume size: %u\n", config->log_drv_size); 1127 printf(" spare size: %u\n", config->spares_size); 1128 p = (char *)config->array; 1129 1130 for (i = 0; i < config->array_count; i++) { 1131 ar = (struct mfi_array *)p; 1132 printf(" array %u of %u drives:\n", ar->array_ref, 1133 ar->num_drives); 1134 printf(" size = %ju\n", (uintmax_t)ar->size); 1135 for (j = 0; j < ar->num_drives; j++) { 1136 device_id = ar->pd[j].ref.v.device_id; 1137 if (device_id == 0xffff) 1138 printf(" drive MISSING\n"); 1139 else { 1140 printf(" drive %u %s\n", device_id, 1141 mfi_pdstate(ar->pd[j].fw_state)); 1142 if (mfi_pd_get_info(fd, device_id, &pinfo, 1143 NULL) >= 0) { 1144 printf(" raw size: %ju\n", 1145 (uintmax_t)pinfo.raw_size); 1146 printf(" non-coerced size: %ju\n", 1147 (uintmax_t)pinfo.non_coerced_size); 1148 printf(" coerced size: %ju\n", 1149 (uintmax_t)pinfo.coerced_size); 1150 } 1151 } 1152 } 1153 p += config->array_size; 1154 } 1155 1156 for (i = 0; i < config->log_drv_count; i++) { 1157 ld = (struct mfi_ld_config *)p; 1158 printf(" volume %s ", 1159 mfi_volume_name(fd, ld->properties.ld.v.target_id)); 1160 printf("%s %s", 1161 mfi_raid_level(ld->params.primary_raid_level, 1162 ld->params.secondary_raid_level), 1163 mfi_ldstate(ld->params.state)); 1164 if (ld->properties.name[0] != '\0') 1165 printf(" <%s>", ld->properties.name); 1166 printf("\n"); 1167 printf(" primary raid level: %u\n", 1168 ld->params.primary_raid_level); 1169 printf(" raid level qualifier: %u\n", 1170 ld->params.raid_level_qualifier); 1171 printf(" secondary raid level: %u\n", 1172 ld->params.secondary_raid_level); 1173 printf(" stripe size: %u\n", ld->params.stripe_size); 1174 printf(" num drives: %u\n", ld->params.num_drives); 1175 printf(" init state: %u\n", ld->params.init_state); 1176 printf(" consistent: %u\n", ld->params.is_consistent); 1177 printf(" no bgi: %u\n", ld->properties.no_bgi); 1178 printf(" spans:\n"); 1179 for (j = 0; j < ld->params.span_depth; j++) { 1180 printf(" array %u @ ", ld->span[j].array_ref); 1181 printf("%ju : %ju\n", 1182 (uintmax_t)ld->span[j].start_block, 1183 (uintmax_t)ld->span[j].num_blocks); 1184 } 1185 p += config->log_drv_size; 1186 } 1187 1188 for (i = 0; i < config->spares_count; i++) { 1189 sp = (struct mfi_spare *)p; 1190 printf(" %s spare %u ", 1191 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" : 1192 "global", sp->ref.v.device_id); 1193 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE)); 1194 printf(" backs:\n"); 1195 for (j = 0; j < sp->array_count; j++) 1196 printf(" array %u\n", sp->array_ref[j]); 1197 p += config->spares_size; 1198 } 1199} 1200 1201#ifdef DEBUG 1202static int 1203debug_config(int ac, char **av) 1204{ 1205 struct mfi_config_data *config; 1206 int error, fd; 1207 1208 if (ac != 1) { 1209 warnx("debug: extra arguments"); 1210 return (EINVAL); 1211 } 1212 1213 fd = mfi_open(mfi_device, O_RDWR); 1214 if (fd < 0) { 1215 error = errno; 1216 warn("mfi_open"); 1217 return (error); 1218 } 1219 1220 /* Get the config from the controller. */ 1221 if (mfi_config_read(fd, &config) < 0) { 1222 error = errno; 1223 warn("Failed to get config"); 1224 close(fd); 1225 return (error); 1226 } 1227 1228 /* Dump out the configuration. */ 1229 dump_config(fd, config, NULL); 1230 free(config); 1231 close(fd); 1232 1233 return (0); 1234} 1235MFI_COMMAND(top, debug, debug_config); 1236 1237static int 1238dump(int ac, char **av) 1239{ 1240 struct mfi_config_data *config; 1241 char buf[64]; 1242 size_t len; 1243 int error, fd; 1244 1245 if (ac != 1) { 1246 warnx("dump: extra arguments"); 1247 return (EINVAL); 1248 } 1249 1250 fd = mfi_open(mfi_device, O_RDWR); 1251 if (fd < 0) { 1252 error = errno; 1253 warn("mfi_open"); 1254 return (error); 1255 } 1256 1257 /* Get the stashed copy of the last dcmd from the driver. */ 1258 snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit); 1259 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) { 1260 error = errno; 1261 warn("Failed to read debug command"); 1262 if (error == ENOENT) 1263 error = EOPNOTSUPP; 1264 close(fd); 1265 return (error); 1266 } 1267 1268 config = malloc(len); 1269 if (config == NULL) { 1270 warnx("malloc failed"); 1271 close(fd); 1272 return (ENOMEM); 1273 } 1274 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) { 1275 error = errno; 1276 warn("Failed to read debug command"); 1277 free(config); 1278 close(fd); 1279 return (error); 1280 } 1281 dump_config(fd, config, NULL); 1282 free(config); 1283 close(fd); 1284 1285 return (0); 1286} 1287MFI_COMMAND(top, dump, dump); 1288#endif 1289