mfi_cmd.c revision 237259
155714Skris/*- 255714Skris * Copyright (c) 2008, 2009 Yahoo!, Inc. 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8280304Sjkim * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 3. The names of the authors may not be used to endorse or promote 1455714Skris * products derived from this software without specific prior written 15280304Sjkim * permission. 1655714Skris * 1755714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1855714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1955714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2055714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2155714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22280304Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2355714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2455714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2555714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2655714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2755714Skris * SUCH DAMAGE. 2855714Skris * 2955714Skris * $FreeBSD: head/usr.sbin/mfiutil/mfi_cmd.c 237259 2012-06-19 06:18:37Z eadler $ 3055714Skris */ 3155714Skris 3255714Skris#include <sys/errno.h> 3355714Skris#include <sys/ioctl.h> 3455714Skris#include <sys/param.h> 3555714Skris#include <sys/sysctl.h> 3655714Skris#include <sys/uio.h> 37280304Sjkim 3855714Skris#include <err.h> 3955714Skris#include <fcntl.h> 40280304Sjkim#include <stdio.h> 4155714Skris#include <stdlib.h> 4255714Skris#include <string.h> 4355714Skris#include <unistd.h> 4455714Skris 4555714Skris#include "mfiutil.h" 4655714Skris#include <dev/mfi/mfi_ioctl.h> 4755714Skris 4855714Skrisstatic const char *mfi_status_codes[] = { 4955714Skris "Command completed successfully", 5055714Skris "Invalid command", 5155714Skris "Invalid DMCD opcode", 52280304Sjkim "Invalid parameter", 5355714Skris "Invalid Sequence Number", 5455714Skris "Abort isn't possible for the requested command", 5555714Skris "Application 'host' code not found", 5655714Skris "Application in use", 5755714Skris "Application not initialized", 58160814Ssimon "Array index invalid", 59238405Sjkim "Array row not empty", 60238405Sjkim "Configuration resource conflict", 61238405Sjkim "Device not found", 62238405Sjkim "Drive too small", 63238405Sjkim "Flash memory allocation failed", 64238405Sjkim "Flash download already in progress", 65238405Sjkim "Flash operation failed", 66280304Sjkim "Bad flash image", 67238405Sjkim "Incomplete flash image", 68238405Sjkim "Flash not open", 69238405Sjkim "Flash not started", 70238405Sjkim "Flush failed", 71238405Sjkim "Specified application doesn't have host-resident code", 72238405Sjkim "Volume consistency check in progress", 73238405Sjkim "Volume initialization in progress", 74238405Sjkim "Volume LBA out of range", 75238405Sjkim "Maximum number of volumes are already configured", 76238405Sjkim "Volume is not OPTIMAL", 77238405Sjkim "Volume rebuild in progress", 78238405Sjkim "Volume reconstruction in progress", 79238405Sjkim "Volume RAID level is wrong for requested operation", 80238405Sjkim "Too many spares assigned", 81238405Sjkim "Scratch memory not available", 82238405Sjkim "Error writing MFC data to SEEPROM", 83238405Sjkim "Required hardware is missing", 84238405Sjkim "Item not found", 85238405Sjkim "Volume drives are not within an enclosure", 86238405Sjkim "Drive clear in progress", 87238405Sjkim "Drive type mismatch (SATA vs SAS)", 88238405Sjkim "Patrol read disabled", 89238405Sjkim "Invalid row index", 90238405Sjkim "SAS Config - Invalid action", 91238405Sjkim "SAS Config - Invalid data", 92238405Sjkim "SAS Config - Invalid page", 93238405Sjkim "SAS Config - Invalid type", 94238405Sjkim "SCSI command completed with error", 95238405Sjkim "SCSI I/O request failed", 96238405Sjkim "SCSI RESERVATION_CONFLICT", 97238405Sjkim "One or more flush operations during shutdown failed", 98238405Sjkim "Firmware time is not set", 99238405Sjkim "Wrong firmware or drive state", 100238405Sjkim "Volume is offline", 101238405Sjkim "Peer controller rejected request", 102238405Sjkim "Unable to inform peer of communication changes", 103238405Sjkim "Volume reservation already in progress", 104238405Sjkim "I2C errors were detected", 105238405Sjkim "PCI errors occurred during XOR/DMA operation", 106238405Sjkim "Diagnostics failed", 107238405Sjkim "Unable to process command as boot messages are pending", 108238405Sjkim "Foreign configuration is incomplete" 109238405Sjkim}; 110238405Sjkim 111238405Sjkimconst char * 112160814Ssimonmfi_status(u_int status_code) 113160814Ssimon{ 114280304Sjkim static char buffer[16]; 115160814Ssimon 116160814Ssimon if (status_code == MFI_STAT_INVALID_STATUS) 117160814Ssimon return ("Invalid status"); 118160814Ssimon if (status_code < sizeof(mfi_status_codes) / sizeof(char *)) 119160814Ssimon return (mfi_status_codes[status_code]); 120280304Sjkim snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code); 121160814Ssimon return (buffer); 122160814Ssimon} 123160814Ssimon 12455714Skrisconst char * 12555714Skrismfi_raid_level(uint8_t primary_level, uint8_t secondary_level) 126280304Sjkim{ 12755714Skris static char buf[16]; 128296317Sdelphij 129280304Sjkim switch (primary_level) { 130280304Sjkim case DDF_RAID0: 131280304Sjkim return ("RAID-0"); 132280304Sjkim case DDF_RAID1: 133280304Sjkim if (secondary_level != 0) 134280304Sjkim return ("RAID-10"); 13555714Skris else 13655714Skris return ("RAID-1"); 13755714Skris case DDF_RAID1E: 13855714Skris return ("RAID-1E"); 13955714Skris case DDF_RAID3: 140280304Sjkim return ("RAID-3"); 141280304Sjkim case DDF_RAID5: 142280304Sjkim if (secondary_level != 0) 143280304Sjkim return ("RAID-50"); 144280304Sjkim else 145280304Sjkim return ("RAID-5"); 146280304Sjkim case DDF_RAID5E: 147160814Ssimon return ("RAID-5E"); 148160814Ssimon case DDF_RAID5EE: 149160814Ssimon return ("RAID-5EE"); 150160814Ssimon case DDF_RAID6: 15155714Skris if (secondary_level != 0) 152280304Sjkim return ("RAID-60"); 153280304Sjkim else 154280304Sjkim return ("RAID-6"); 155280304Sjkim case DDF_JBOD: 156280304Sjkim return ("JBOD"); 15755714Skris case DDF_CONCAT: 158280304Sjkim return ("CONCAT"); 159280304Sjkim default: 160280304Sjkim sprintf(buf, "LVL 0x%02x", primary_level); 161280304Sjkim return (buf); 162280304Sjkim } 163280304Sjkim} 164280304Sjkim 165280304Sjkimstatic int 166280304Sjkimmfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info) 167280304Sjkim{ 168280304Sjkim 169280304Sjkim bzero(info, sizeof(*info)); 170280304Sjkim info->array_id = target_id; 171109998Smarkm if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) 172280304Sjkim return (-1); 173280304Sjkim if (!info->present) { 174280304Sjkim errno = ENXIO; 17589837Skris return (-1); 17655714Skris } 177280304Sjkim return (0); 178280304Sjkim} 179280304Sjkim 180280304Sjkimconst char * 181280304Sjkimmfi_volume_name(int fd, uint8_t target_id) 182280304Sjkim{ 183280304Sjkim static struct mfi_query_disk info; 184280304Sjkim static char buf[4]; 185280304Sjkim 186280304Sjkim if (mfi_query_disk(fd, target_id, &info) < 0) { 187280304Sjkim snprintf(buf, sizeof(buf), "%d", target_id); 188280304Sjkim return (buf); 189280304Sjkim } 190280304Sjkim return (info.devname); 191280304Sjkim} 192280304Sjkim 193280304Sjkimint 194280304Sjkimmfi_volume_busy(int fd, uint8_t target_id) 195280304Sjkim{ 196280304Sjkim struct mfi_query_disk info; 197280304Sjkim 198280304Sjkim /* Assume it isn't mounted if we can't get information. */ 199280304Sjkim if (mfi_query_disk(fd, target_id, &info) < 0) 200280304Sjkim return (0); 201280304Sjkim return (info.open != 0); 20255714Skris} 203280304Sjkim 204280304Sjkim/* 205280304Sjkim * Check if the running kernel supports changing the RAID 206280304Sjkim * configuration of the mfi controller. 207280304Sjkim */ 20855714Skrisint 209280304Sjkimmfi_reconfig_supported(void) 210280304Sjkim{ 211280304Sjkim char mibname[64]; 212280304Sjkim size_t len; 213280304Sjkim int dummy; 214280304Sjkim 215280304Sjkim len = sizeof(dummy); 216280304Sjkim snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes", 217280304Sjkim mfi_unit); 218280304Sjkim return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0); 219280304Sjkim} 220280304Sjkim 221280304Sjkimint 222280304Sjkimmfi_lookup_volume(int fd, const char *name, uint8_t *target_id) 223280304Sjkim{ 224280304Sjkim struct mfi_query_disk info; 225280304Sjkim struct mfi_ld_list list; 226280304Sjkim char *cp; 227280304Sjkim long val; 228280304Sjkim u_int i; 229280304Sjkim 23055714Skris /* If it's a valid number, treat it as a raw target ID. */ 231280304Sjkim val = strtol(name, &cp, 0); 232280304Sjkim if (*cp == '\0') { 233280304Sjkim *target_id = val; 234280304Sjkim return (0); 235280304Sjkim } 236280304Sjkim 237280304Sjkim if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), 238280304Sjkim NULL, 0, NULL) < 0) 239280304Sjkim return (-1); 240280304Sjkim 241280304Sjkim for (i = 0; i < list.ld_count; i++) { 242280304Sjkim if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, 243280304Sjkim &info) < 0) 244280304Sjkim continue; 245280304Sjkim if (strcmp(name, info.devname) == 0) { 246280304Sjkim *target_id = list.ld_list[i].ld.v.target_id; 247280304Sjkim return (0); 248280304Sjkim } 249280304Sjkim } 250280304Sjkim errno = EINVAL; 251280304Sjkim return (-1); 252280304Sjkim} 253280304Sjkim 254280304Sjkimint 255280304Sjkimmfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize, 256280304Sjkim uint8_t *mbox, size_t mboxlen, uint8_t *statusp) 257280304Sjkim{ 258160814Ssimon struct mfi_ioc_passthru ioc; 25955714Skris struct mfi_dcmd_frame *dcmd; 260280304Sjkim int r; 261280304Sjkim 262280304Sjkim if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || 263280304Sjkim (mbox == NULL && mboxlen != 0)) { 264280304Sjkim errno = EINVAL; 265280304Sjkim return (-1); 266280304Sjkim } 267238405Sjkim 268280304Sjkim bzero(&ioc, sizeof(ioc)); 269280304Sjkim dcmd = &ioc.ioc_frame; 270280304Sjkim if (mbox) 271280304Sjkim bcopy(mbox, dcmd->mbox, mboxlen); 272280304Sjkim dcmd->header.cmd = MFI_CMD_DCMD; 273280304Sjkim dcmd->header.timeout = 0; 274280304Sjkim dcmd->header.flags = 0; 275280304Sjkim dcmd->header.data_len = bufsize; 276280304Sjkim dcmd->opcode = opcode; 27755714Skris 278280304Sjkim ioc.buf = buf; 27955714Skris ioc.buf_size = bufsize; 280280304Sjkim r = ioctl(fd, MFIIO_PASSTHRU, &ioc); 281280304Sjkim if (r < 0) 282194206Ssimon return (r); 283280304Sjkim 284280304Sjkim if (statusp != NULL) 285280304Sjkim *statusp = dcmd->header.cmd_status; 286280304Sjkim else if (dcmd->header.cmd_status != MFI_STAT_OK) { 287280304Sjkim warnx("Command failed: %s", 288280304Sjkim mfi_status(dcmd->header.cmd_status)); 289280304Sjkim errno = EIO; 290194206Ssimon return (-1); 291280304Sjkim } 292280304Sjkim return (0); 293280304Sjkim} 294280304Sjkim 295280304Sjkimint 296280304Sjkimmfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) 297280304Sjkim{ 298280304Sjkim 29955714Skris return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, 300280304Sjkim sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); 301280304Sjkim} 302280304Sjkim 303280304Sjkimint 304280304Sjkimmfi_open(int unit, int acs) 305280304Sjkim{ 306280304Sjkim char path[MAXPATHLEN]; 307280304Sjkim 308280304Sjkim snprintf(path, sizeof(path), "/dev/mfi%d", unit); 309280304Sjkim return (open(path, acs)); 310280304Sjkim} 311280304Sjkim 312160814Ssimonvoid 313160814Ssimonmfi_display_progress(const char *label, struct mfi_progress *prog) 314160814Ssimon{ 315160814Ssimon uint seconds; 316160814Ssimon 317160814Ssimon printf("%s: %.2f%% complete, after %ds", label, 318160814Ssimon (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds); 319160814Ssimon if (prog->progress != 0 && prog->elapsed_seconds > 10) { 320160814Ssimon printf(" finished in "); 321280304Sjkim seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) / 322160814Ssimon prog->progress - prog->elapsed_seconds; 323160814Ssimon if (seconds > 3600) 324160814Ssimon printf("%u:", seconds / 3600); 325160814Ssimon if (seconds > 60) { 326160814Ssimon seconds %= 3600; 327160814Ssimon printf("%02u:%02u", seconds / 60, seconds % 60); 328160814Ssimon } else 329280304Sjkim printf("%us", seconds); 330160814Ssimon } 331280304Sjkim printf("\n"); 332280304Sjkim} 333280304Sjkim 334280304Sjkimint 335280304Sjkimmfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end, 336280304Sjkim int ac, char **av) 337280304Sjkim{ 338280304Sjkim struct mfiutil_command **cmd; 339280304Sjkim 34055714Skris if (ac < 2) { 34155714Skris warnx("The %s command requires a sub-command.", av[0]); 342280304Sjkim return (EINVAL); 343280304Sjkim } 344280304Sjkim for (cmd = start; cmd < end; cmd++) { 345280304Sjkim if (strcmp((*cmd)->name, av[1]) == 0) 346280304Sjkim return ((*cmd)->handler(ac - 1, av + 1)); 347280304Sjkim } 348280304Sjkim 349280304Sjkim warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 350280304Sjkim return (ENOENT); 351280304Sjkim} 352280304Sjkim