diff options
Diffstat (limited to 'usr.sbin/mfiutil/mfi_cmd.c')
-rw-r--r-- | usr.sbin/mfiutil/mfi_cmd.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/usr.sbin/mfiutil/mfi_cmd.c b/usr.sbin/mfiutil/mfi_cmd.c new file mode 100644 index 0000000..abc8312 --- /dev/null +++ b/usr.sbin/mfiutil/mfi_cmd.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2008, 2009 Yahoo!, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mfiutil.h" +#include <dev/mfi/mfi_ioctl.h> + +static const char *mfi_status_codes[] = { + "Command completed succesfully", + "Invalid command", + "Invalid DMCD opcode", + "Invalid parameter", + "Invalid Sequence Number", + "Abort isn't possible for the requested command", + "Application 'host' code not found", + "Application in use", + "Application not initialized", + "Array index invalid", + "Array row not empty", + "Configuration resource conflict", + "Device not found", + "Drive too small", + "Flash memory allocation failed", + "Flash download already in progress", + "Flash operation failed", + "Bad flash image", + "Incomplete flash image", + "Flash not open", + "Flash not started", + "Flush failed", + "Specified application doesn't have host-resident code", + "Volume consistency check in progress", + "Volume initialization in progress", + "Volume LBA out of range", + "Maximum number of volumes are already configured", + "Volume is not OPTIMAL", + "Volume rebuild in progress", + "Volume reconstruction in progress", + "Volume RAID level is wrong for requested operation", + "Too many spares assigned", + "Scratch memory not available", + "Error writing MFC data to SEEPROM", + "Required hardware is missing", + "Item not found", + "Volume drives are not within an enclosure", + "Drive clear in progress", + "Drive type mismatch (SATA vs SAS)", + "Patrol read disabled", + "Invalid row index", + "SAS Config - Invalid action", + "SAS Config - Invalid data", + "SAS Config - Invalid page", + "SAS Config - Invalid type", + "SCSI command completed with error", + "SCSI I/O request failed", + "SCSI RESERVATION_CONFLICT", + "One or more flush operations during shutdown failed", + "Firmware time is not set", + "Wrong firmware or drive state", + "Volume is offline", + "Peer controller rejected request", + "Unable to inform peer of communication changes", + "Volume reservation already in progress", + "I2C errors were detected", + "PCI errors occurred during XOR/DMA operation", + "Diagnostics failed", + "Unable to process command as boot messages are pending", + "Foreign configuration is incomplete" +}; + +const char * +mfi_status(u_int status_code) +{ + static char buffer[16]; + + if (status_code == MFI_STAT_INVALID_STATUS) + return ("Invalid status"); + if (status_code < sizeof(mfi_status_codes) / sizeof(char *)) + return (mfi_status_codes[status_code]); + snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code); + return (buffer); +} + +const char * +mfi_raid_level(uint8_t primary_level, uint8_t secondary_level) +{ + static char buf[16]; + + switch (primary_level) { + case DDF_RAID0: + return ("RAID-0"); + case DDF_RAID1: + if (secondary_level != 0) + return ("RAID-10"); + else + return ("RAID-1"); + case DDF_RAID1E: + return ("RAID-1E"); + case DDF_RAID3: + return ("RAID-3"); + case DDF_RAID5: + if (secondary_level != 0) + return ("RAID-50"); + else + return ("RAID-5"); + case DDF_RAID5E: + return ("RAID-5E"); + case DDF_RAID5EE: + return ("RAID-5EE"); + case DDF_RAID6: + if (secondary_level != 0) + return ("RAID-60"); + else + return ("RAID-6"); + case DDF_JBOD: + return ("JBOD"); + case DDF_CONCAT: + return ("CONCAT"); + default: + sprintf(buf, "LVL 0x%02x", primary_level); + return (buf); + } +} + +static int +mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info) +{ + + bzero(info, sizeof(*info)); + info->array_id = target_id; + if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) + return (-1); + if (!info->present) { + errno = ENXIO; + return (-1); + } + return (0); +} + +const char * +mfi_volume_name(int fd, uint8_t target_id) +{ + static struct mfi_query_disk info; + static char buf[4]; + + if (mfi_query_disk(fd, target_id, &info) < 0) { + snprintf(buf, sizeof(buf), "%d", target_id); + return (buf); + } + return (info.devname); +} + +int +mfi_volume_busy(int fd, uint8_t target_id) +{ + struct mfi_query_disk info; + + /* Assume it isn't mounted if we can't get information. */ + if (mfi_query_disk(fd, target_id, &info) < 0) + return (0); + return (info.open != 0); +} + +/* + * Check if the running kernel supports changing the RAID + * configuration of the mfi controller. + */ +int +mfi_reconfig_supported(void) +{ + char mibname[64]; + size_t len; + int dummy; + + len = sizeof(dummy); + snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes", + mfi_unit); + return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0); +} + +int +mfi_lookup_volume(int fd, const char *name, uint8_t *target_id) +{ + struct mfi_query_disk info; + struct mfi_ld_list list; + char *cp; + long val; + u_int i; + + /* If it's a valid number, treat it as a raw target ID. */ + val = strtol(name, &cp, 0); + if (*cp == '\0') { + *target_id = val; + return (0); + } + + if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), + NULL, 0, NULL) < 0) + return (-1); + + for (i = 0; i < list.ld_count; i++) { + if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, + &info) < 0) + continue; + if (strcmp(name, info.devname) == 0) { + *target_id = list.ld_list[i].ld.v.target_id; + return (0); + } + } + errno = EINVAL; + return (-1); +} + +int +mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize, + uint8_t *mbox, size_t mboxlen, uint8_t *statusp) +{ + struct mfi_ioc_passthru ioc; + struct mfi_dcmd_frame *dcmd; + int r; + + if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || + (mbox == NULL && mboxlen != 0)) { + errno = EINVAL; + return (-1); + } + + bzero(&ioc, sizeof(ioc)); + dcmd = &ioc.ioc_frame; + if (mbox) + bcopy(mbox, dcmd->mbox, mboxlen); + dcmd->header.cmd = MFI_CMD_DCMD; + dcmd->header.timeout = 0; + dcmd->header.flags = 0; + dcmd->header.data_len = bufsize; + dcmd->opcode = opcode; + + ioc.buf = buf; + ioc.buf_size = bufsize; + r = ioctl(fd, MFIIO_PASSTHRU, &ioc); + if (r < 0) + return (r); + + if (statusp != NULL) + *statusp = dcmd->header.cmd_status; + else if (dcmd->header.cmd_status != MFI_STAT_OK) { + warnx("Command failed: %s", + mfi_status(dcmd->header.cmd_status)); + errno = EIO; + return (-1); + } + return (0); +} + +int +mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) +{ + + return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, + sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); +} + +int +mfi_open(int unit) +{ + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "/dev/mfi%d", unit); + return (open(path, O_RDWR)); +} + +void +mfi_display_progress(const char *label, struct mfi_progress *prog) +{ + uint seconds; + + printf("%s: %.2f%% complete, after %ds", label, + (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds); + if (prog->elapsed_seconds > 10) { + printf(" finished in "); + seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) / + prog->progress - prog->elapsed_seconds; + if (seconds > 3600) + printf("%u:", seconds / 3600); + if (seconds > 60) { + seconds %= 3600; + printf("%02u:%02u", seconds / 60, seconds % 60); + } else + printf("%us", seconds); + } + printf("\n"); +} + +int +mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end, + int ac, char **av) +{ + struct mfiutil_command **cmd; + + if (ac < 2) { + warnx("The %s command requires a sub-command.", av[0]); + return (EINVAL); + } + for (cmd = start; cmd < end; cmd++) { + if (strcmp((*cmd)->name, av[1]) == 0) + return ((*cmd)->handler(ac - 1, av + 1)); + } + + warnx("%s is not a valid sub-command of %s.", av[1], av[0]); + return (ENOENT); +} |