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$ 30196200Sscottl */ 31196200Sscottl 32196200Sscottl#include <sys/types.h> 33196200Sscottl#include <sys/errno.h> 34196200Sscottl#include <ctype.h> 35196200Sscottl#include <err.h> 36237259Seadler#include <fcntl.h> 37196200Sscottl#include <libutil.h> 38196200Sscottl#include <limits.h> 39196200Sscottl#include <stdio.h> 40196200Sscottl#include <stdlib.h> 41196200Sscottl#include <string.h> 42196200Sscottl#include <strings.h> 43196200Sscottl#include <unistd.h> 44196200Sscottl#include <cam/scsi/scsi_all.h> 45196200Sscottl#include "mfiutil.h" 46196200Sscottl 47196200SscottlMFI_TABLE(top, drive); 48196200Sscottl 49223345Sbz/* 50223345Sbz * Print the name of a drive either by drive number as %2u or by enclosure:slot 51223345Sbz * as Exx:Sxx (or both). Use default unless command line options override it 52223345Sbz * and the command allows this (which we usually do unless we already print 53223345Sbz * both). We prefer pinfo if given, otherwise try to look it up by device_id. 54223345Sbz */ 55196200Sscottlconst char * 56223345Sbzmfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def) 57223345Sbz{ 58223345Sbz struct mfi_pd_info info; 59223345Sbz static char buf[16]; 60223345Sbz char *p; 61223345Sbz int error, fd, len; 62223345Sbz 63223345Sbz if ((def & MFI_DNAME_HONOR_OPTS) != 0 && 64223345Sbz (mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) != 0) 65223345Sbz def = mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID); 66223345Sbz 67223345Sbz buf[0] = '\0'; 68223345Sbz if (pinfo == NULL && def & MFI_DNAME_ES) { 69223345Sbz /* Fallback in case of error, just ignore flags. */ 70223345Sbz if (device_id == 0xffff) 71223345Sbz snprintf(buf, sizeof(buf), "MISSING"); 72223345Sbz else 73223345Sbz snprintf(buf, sizeof(buf), "%2u", device_id); 74223345Sbz 75237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 76223345Sbz if (fd < 0) { 77223345Sbz warn("mfi_open"); 78223345Sbz return (buf); 79223345Sbz } 80223345Sbz 81223345Sbz /* Get the info for this drive. */ 82223345Sbz if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 83223345Sbz warn("Failed to fetch info for drive %2u", device_id); 84223345Sbz close(fd); 85223345Sbz return (buf); 86223345Sbz } 87223345Sbz 88223345Sbz close(fd); 89223345Sbz pinfo = &info; 90223345Sbz } 91223345Sbz 92223345Sbz p = buf; 93223345Sbz len = sizeof(buf); 94223345Sbz if (def & MFI_DNAME_DEVICE_ID) { 95223345Sbz if (device_id == 0xffff) 96223345Sbz error = snprintf(p, len, "MISSING"); 97223345Sbz else 98223345Sbz error = snprintf(p, len, "%2u", device_id); 99223345Sbz if (error >= 0) { 100223345Sbz p += error; 101223345Sbz len -= error; 102223345Sbz } 103223345Sbz } 104223345Sbz if ((def & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) == 105223345Sbz (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID) && len >= 2) { 106223345Sbz *p++ = ' '; 107223345Sbz len--; 108223345Sbz *p = '\0'; 109223345Sbz len--; 110223345Sbz } 111223345Sbz if (def & MFI_DNAME_ES) { 112223345Sbz if (pinfo->encl_device_id == 0xffff) 113223345Sbz error = snprintf(p, len, "S%u", 114223345Sbz pinfo->slot_number); 115223345Sbz else if (pinfo->encl_device_id == pinfo->ref.v.device_id) 116223345Sbz error = snprintf(p, len, "E%u", 117223345Sbz pinfo->encl_index); 118223345Sbz else 119223345Sbz error = snprintf(p, len, "E%u:S%u", 120223345Sbz pinfo->encl_index, pinfo->slot_number); 121223345Sbz if (error >= 0) { 122223345Sbz p += error; 123223345Sbz len -= error; 124223345Sbz } 125223345Sbz } 126223345Sbz 127223345Sbz return (buf); 128223345Sbz} 129223345Sbz 130223345Sbzconst char * 131196200Sscottlmfi_pdstate(enum mfi_pd_state state) 132196200Sscottl{ 133196200Sscottl static char buf[16]; 134196200Sscottl 135196200Sscottl switch (state) { 136196200Sscottl case MFI_PD_STATE_UNCONFIGURED_GOOD: 137196200Sscottl return ("UNCONFIGURED GOOD"); 138196200Sscottl case MFI_PD_STATE_UNCONFIGURED_BAD: 139196200Sscottl return ("UNCONFIGURED BAD"); 140196200Sscottl case MFI_PD_STATE_HOT_SPARE: 141196200Sscottl return ("HOT SPARE"); 142196200Sscottl case MFI_PD_STATE_OFFLINE: 143196200Sscottl return ("OFFLINE"); 144196200Sscottl case MFI_PD_STATE_FAILED: 145196200Sscottl return ("FAILED"); 146196200Sscottl case MFI_PD_STATE_REBUILD: 147196200Sscottl return ("REBUILD"); 148196200Sscottl case MFI_PD_STATE_ONLINE: 149196200Sscottl return ("ONLINE"); 150214131Spluknet case MFI_PD_STATE_COPYBACK: 151214131Spluknet return ("COPYBACK"); 152214131Spluknet case MFI_PD_STATE_SYSTEM: 153233713Sambrisko return ("JBOD"); 154196200Sscottl default: 155196200Sscottl sprintf(buf, "PSTATE 0x%04x", state); 156196200Sscottl return (buf); 157196200Sscottl } 158196200Sscottl} 159196200Sscottl 160196200Sscottlint 161196200Sscottlmfi_lookup_drive(int fd, char *drive, uint16_t *device_id) 162196200Sscottl{ 163196200Sscottl struct mfi_pd_list *list; 164196200Sscottl long val; 165214396Sjhb int error; 166196211Sscottl u_int i; 167196200Sscottl char *cp; 168214396Sjhb uint8_t encl, slot; 169196200Sscottl 170196200Sscottl /* Look for a raw device id first. */ 171196200Sscottl val = strtol(drive, &cp, 0); 172196200Sscottl if (*cp == '\0') { 173196200Sscottl if (val < 0 || val >= 0xffff) 174196200Sscottl goto bad; 175196200Sscottl *device_id = val; 176196200Sscottl return (0); 177196200Sscottl } 178196200Sscottl 179196200Sscottl /* Support for MegaCli style [Exx]:Syy notation. */ 180196200Sscottl if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') { 181196200Sscottl if (drive[1] == '\0') 182196200Sscottl goto bad; 183196200Sscottl cp = drive; 184196200Sscottl if (toupper(drive[0]) == 'E') { 185196200Sscottl cp++; /* Eat 'E' */ 186196200Sscottl val = strtol(cp, &cp, 0); 187196200Sscottl if (val < 0 || val > 0xff || *cp != ':') 188196200Sscottl goto bad; 189196200Sscottl encl = val; 190196200Sscottl cp++; /* Eat ':' */ 191196200Sscottl if (toupper(*cp) != 'S') 192196200Sscottl goto bad; 193196200Sscottl } else 194196200Sscottl encl = 0xff; 195196200Sscottl cp++; /* Eat 'S' */ 196196200Sscottl if (*cp == '\0') 197196200Sscottl goto bad; 198196200Sscottl val = strtol(cp, &cp, 0); 199196200Sscottl if (val < 0 || val > 0xff || *cp != '\0') 200196200Sscottl goto bad; 201196200Sscottl slot = val; 202196200Sscottl 203196200Sscottl if (mfi_pd_get_list(fd, &list, NULL) < 0) { 204214396Sjhb error = errno; 205196200Sscottl warn("Failed to fetch drive list"); 206214396Sjhb return (error); 207196200Sscottl } 208196200Sscottl 209196211Sscottl for (i = 0; i < list->count; i++) { 210196211Sscottl if (list->addr[i].scsi_dev_type != 0) 211196200Sscottl continue; 212196200Sscottl 213196200Sscottl if (((encl == 0xff && 214196211Sscottl list->addr[i].encl_device_id == 0xffff) || 215196211Sscottl list->addr[i].encl_index == encl) && 216196211Sscottl list->addr[i].slot_number == slot) { 217196211Sscottl *device_id = list->addr[i].device_id; 218196200Sscottl free(list); 219196200Sscottl return (0); 220196200Sscottl } 221196200Sscottl } 222196200Sscottl free(list); 223196200Sscottl warnx("Unknown drive %s", drive); 224196200Sscottl return (EINVAL); 225196200Sscottl } 226196200Sscottl 227196200Sscottlbad: 228196200Sscottl warnx("Invalid drive number %s", drive); 229196200Sscottl return (EINVAL); 230196200Sscottl} 231196200Sscottl 232196200Sscottlstatic void 233196200Sscottlmbox_store_device_id(uint8_t *mbox, uint16_t device_id) 234196200Sscottl{ 235196200Sscottl 236196200Sscottl mbox[0] = device_id & 0xff; 237196200Sscottl mbox[1] = device_id >> 8; 238196200Sscottl} 239196200Sscottl 240196200Sscottlvoid 241196200Sscottlmbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref) 242196200Sscottl{ 243196200Sscottl 244196200Sscottl mbox[0] = ref->v.device_id & 0xff; 245196200Sscottl mbox[1] = ref->v.device_id >> 8; 246196200Sscottl mbox[2] = ref->v.seq_num & 0xff; 247196200Sscottl mbox[3] = ref->v.seq_num >> 8; 248196200Sscottl} 249196200Sscottl 250196200Sscottlint 251196200Sscottlmfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp) 252196200Sscottl{ 253196200Sscottl struct mfi_pd_list *list; 254196200Sscottl uint32_t list_size; 255196200Sscottl 256196200Sscottl /* 257196200Sscottl * Keep fetching the list in a loop until we have a large enough 258196200Sscottl * buffer to hold the entire list. 259196200Sscottl */ 260196200Sscottl list = NULL; 261196200Sscottl list_size = 1024; 262196200Sscottlfetch: 263196200Sscottl list = reallocf(list, list_size); 264196200Sscottl if (list == NULL) 265196200Sscottl return (-1); 266196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 267196200Sscottl 0, statusp) < 0) { 268196200Sscottl free(list); 269196200Sscottl return (-1); 270196200Sscottl } 271196200Sscottl 272196200Sscottl if (list->size > list_size) { 273196200Sscottl list_size = list->size; 274196200Sscottl goto fetch; 275196200Sscottl } 276196200Sscottl 277196200Sscottl *listp = list; 278196200Sscottl return (0); 279196200Sscottl} 280196200Sscottl 281196200Sscottlint 282196200Sscottlmfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info, 283196200Sscottl uint8_t *statusp) 284196200Sscottl{ 285196200Sscottl uint8_t mbox[2]; 286196200Sscottl 287196200Sscottl mbox_store_device_id(&mbox[0], device_id); 288196200Sscottl return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info, 289196200Sscottl sizeof(struct mfi_pd_info), mbox, 2, statusp)); 290196200Sscottl} 291196200Sscottl 292196200Sscottlstatic void 293196200Sscottlcam_strvis(char *dst, const char *src, int srclen, int dstlen) 294196200Sscottl{ 295196200Sscottl 296196200Sscottl /* Trim leading/trailing spaces, nulls. */ 297196200Sscottl while (srclen > 0 && src[0] == ' ') 298196200Sscottl src++, srclen--; 299196200Sscottl while (srclen > 0 300196200Sscottl && (src[srclen-1] == ' ' || src[srclen-1] == '\0')) 301196200Sscottl srclen--; 302196200Sscottl 303196200Sscottl while (srclen > 0 && dstlen > 1) { 304196200Sscottl char *cur_pos = dst; 305196200Sscottl 306196200Sscottl if (*src < 0x20) { 307196200Sscottl /* SCSI-II Specifies that these should never occur. */ 308196200Sscottl /* non-printable character */ 309196200Sscottl if (dstlen > 4) { 310196200Sscottl *cur_pos++ = '\\'; 311196200Sscottl *cur_pos++ = ((*src & 0300) >> 6) + '0'; 312196200Sscottl *cur_pos++ = ((*src & 0070) >> 3) + '0'; 313196200Sscottl *cur_pos++ = ((*src & 0007) >> 0) + '0'; 314196200Sscottl } else { 315196200Sscottl *cur_pos++ = '?'; 316196200Sscottl } 317196200Sscottl } else { 318196200Sscottl /* normal character */ 319196200Sscottl *cur_pos++ = *src; 320196200Sscottl } 321196200Sscottl src++; 322196200Sscottl srclen--; 323196200Sscottl dstlen -= cur_pos - dst; 324196200Sscottl dst = cur_pos; 325196200Sscottl } 326196200Sscottl *dst = '\0'; 327196200Sscottl} 328196200Sscottl 329196200Sscottl/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 330196200Sscottlconst char * 331196200Sscottlmfi_pd_inq_string(struct mfi_pd_info *info) 332196200Sscottl{ 333237329Smjacob struct scsi_inquiry_data iqd, *inq_data = &iqd; 334196200Sscottl char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE]; 335196200Sscottl static char inq_string[64]; 336196200Sscottl 337237329Smjacob memcpy(inq_data, info->inquiry_data, 338237329Smjacob (sizeof (iqd) < sizeof (info->inquiry_data))? 339237329Smjacob sizeof (iqd) : sizeof (info->inquiry_data)); 340196200Sscottl if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) 341196200Sscottl return (NULL); 342196200Sscottl if (SID_TYPE(inq_data) != T_DIRECT) 343196200Sscottl return (NULL); 344196200Sscottl if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) 345196200Sscottl return (NULL); 346196200Sscottl 347196200Sscottl cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), 348196200Sscottl sizeof(vendor)); 349196200Sscottl cam_strvis(product, inq_data->product, sizeof(inq_data->product), 350196200Sscottl sizeof(product)); 351196200Sscottl cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), 352196200Sscottl sizeof(revision)); 353196200Sscottl cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0), 354196200Sscottl sizeof(serial)); 355196200Sscottl 356196200Sscottl /* Hack for SATA disks, no idea how to tell speed. */ 357196200Sscottl if (strcmp(vendor, "ATA") == 0) { 358196200Sscottl snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA", 359196200Sscottl product, revision, serial); 360196200Sscottl return (inq_string); 361196200Sscottl } 362196200Sscottl 363196200Sscottl switch (SID_ANSI_REV(inq_data)) { 364196200Sscottl case SCSI_REV_CCS: 365196200Sscottl strcpy(rstr, "SCSI-CCS"); 366196200Sscottl break; 367196200Sscottl case 5: 368196200Sscottl strcpy(rstr, "SAS"); 369196200Sscottl break; 370196200Sscottl default: 371196200Sscottl snprintf(rstr, sizeof (rstr), "SCSI-%d", 372196200Sscottl SID_ANSI_REV(inq_data)); 373196200Sscottl break; 374196200Sscottl } 375196200Sscottl snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor, 376196200Sscottl product, revision, serial, rstr); 377196200Sscottl return (inq_string); 378196200Sscottl} 379196200Sscottl 380196200Sscottl/* Helper function to set a drive to a given state. */ 381196200Sscottlstatic int 382196200Sscottldrive_set_state(char *drive, uint16_t new_state) 383196200Sscottl{ 384196200Sscottl struct mfi_pd_info info; 385196200Sscottl uint16_t device_id; 386196200Sscottl uint8_t mbox[6]; 387196200Sscottl int error, fd; 388196200Sscottl 389237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 390196200Sscottl if (fd < 0) { 391214396Sjhb error = errno; 392196200Sscottl warn("mfi_open"); 393214396Sjhb return (error); 394196200Sscottl } 395196200Sscottl 396196200Sscottl error = mfi_lookup_drive(fd, drive, &device_id); 397222899Sbz if (error) { 398222899Sbz close(fd); 399196200Sscottl return (error); 400222899Sbz } 401196200Sscottl 402196200Sscottl /* Get the info for this drive. */ 403196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 404214396Sjhb error = errno; 405196200Sscottl warn("Failed to fetch info for drive %u", device_id); 406222899Sbz close(fd); 407214396Sjhb return (error); 408196200Sscottl } 409196200Sscottl 410196200Sscottl /* Try to change the state. */ 411196200Sscottl if (info.fw_state == new_state) { 412196200Sscottl warnx("Drive %u is already in the desired state", device_id); 413222899Sbz close(fd); 414196200Sscottl return (EINVAL); 415196200Sscottl } 416196200Sscottl 417196200Sscottl mbox_store_pdref(&mbox[0], &info.ref); 418196200Sscottl mbox[4] = new_state & 0xff; 419196200Sscottl mbox[5] = new_state >> 8; 420196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6, 421196200Sscottl NULL) < 0) { 422214396Sjhb error = errno; 423196200Sscottl warn("Failed to set drive %u to %s", device_id, 424196200Sscottl mfi_pdstate(new_state)); 425222899Sbz close(fd); 426214396Sjhb return (error); 427196200Sscottl } 428196200Sscottl 429196200Sscottl close(fd); 430196200Sscottl 431196200Sscottl return (0); 432196200Sscottl} 433196200Sscottl 434196200Sscottlstatic int 435196200Sscottlfail_drive(int ac, char **av) 436196200Sscottl{ 437196200Sscottl 438196200Sscottl if (ac != 2) { 439196200Sscottl warnx("fail: %s", ac > 2 ? "extra arguments" : 440196200Sscottl "drive required"); 441196200Sscottl return (EINVAL); 442196200Sscottl } 443196200Sscottl 444196200Sscottl return (drive_set_state(av[1], MFI_PD_STATE_FAILED)); 445196200Sscottl} 446196200SscottlMFI_COMMAND(top, fail, fail_drive); 447196200Sscottl 448196200Sscottlstatic int 449196200Sscottlgood_drive(int ac, char **av) 450196200Sscottl{ 451196200Sscottl 452196200Sscottl if (ac != 2) { 453196200Sscottl warnx("good: %s", ac > 2 ? "extra arguments" : 454196200Sscottl "drive required"); 455196200Sscottl return (EINVAL); 456196200Sscottl } 457196200Sscottl 458196200Sscottl return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD)); 459196200Sscottl} 460196200SscottlMFI_COMMAND(top, good, good_drive); 461196200Sscottl 462196200Sscottlstatic int 463196200Sscottlrebuild_drive(int ac, char **av) 464196200Sscottl{ 465196200Sscottl 466196200Sscottl if (ac != 2) { 467196200Sscottl warnx("rebuild: %s", ac > 2 ? "extra arguments" : 468196200Sscottl "drive required"); 469196200Sscottl return (EINVAL); 470196200Sscottl } 471196200Sscottl 472196200Sscottl return (drive_set_state(av[1], MFI_PD_STATE_REBUILD)); 473196200Sscottl} 474196200SscottlMFI_COMMAND(top, rebuild, rebuild_drive); 475196200Sscottl 476196200Sscottlstatic int 477254906Ssbrunosyspd_drive(int ac, char **av) 478254906Ssbruno{ 479254906Ssbruno 480254906Ssbruno if (ac != 2) { 481254906Ssbruno warnx("syspd: %s", ac > 2 ? "extra arguments" : 482254906Ssbruno "drive required"); 483254906Ssbruno return (EINVAL); 484254906Ssbruno } 485254906Ssbruno 486254906Ssbruno return (drive_set_state(av[1], MFI_PD_STATE_SYSTEM)); 487254906Ssbruno} 488254906SsbrunoMFI_COMMAND(top, syspd, syspd_drive); 489254906Ssbruno 490254906Ssbrunostatic int 491196200Sscottlstart_rebuild(int ac, char **av) 492196200Sscottl{ 493196200Sscottl struct mfi_pd_info info; 494196200Sscottl uint16_t device_id; 495196200Sscottl uint8_t mbox[4]; 496196200Sscottl int error, fd; 497196200Sscottl 498196200Sscottl if (ac != 2) { 499196200Sscottl warnx("start rebuild: %s", ac > 2 ? "extra arguments" : 500196200Sscottl "drive required"); 501196200Sscottl return (EINVAL); 502196200Sscottl } 503196200Sscottl 504237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 505196200Sscottl if (fd < 0) { 506214396Sjhb error = errno; 507196200Sscottl warn("mfi_open"); 508214396Sjhb return (error); 509196200Sscottl } 510196200Sscottl 511196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 512222899Sbz if (error) { 513222899Sbz close(fd); 514196200Sscottl return (error); 515222899Sbz } 516196200Sscottl 517196200Sscottl /* Get the info for this drive. */ 518196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 519214396Sjhb error = errno; 520196200Sscottl warn("Failed to fetch info for drive %u", device_id); 521222899Sbz close(fd); 522214396Sjhb return (error); 523196200Sscottl } 524196200Sscottl 525196200Sscottl /* Check the state, must be REBUILD. */ 526196200Sscottl if (info.fw_state != MFI_PD_STATE_REBUILD) { 527214396Sjhb warnx("Drive %d is not in the REBUILD state", device_id); 528222899Sbz close(fd); 529196200Sscottl return (EINVAL); 530196200Sscottl } 531196200Sscottl 532196200Sscottl /* Start the rebuild. */ 533196200Sscottl mbox_store_pdref(&mbox[0], &info.ref); 534196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4, 535196200Sscottl NULL) < 0) { 536214396Sjhb error = errno; 537196200Sscottl warn("Failed to start rebuild on drive %u", device_id); 538222899Sbz close(fd); 539214396Sjhb return (error); 540196200Sscottl } 541196200Sscottl close(fd); 542196200Sscottl 543196200Sscottl return (0); 544196200Sscottl} 545196200SscottlMFI_COMMAND(start, rebuild, start_rebuild); 546196200Sscottl 547196200Sscottlstatic int 548196200Sscottlabort_rebuild(int ac, char **av) 549196200Sscottl{ 550196200Sscottl struct mfi_pd_info info; 551196200Sscottl uint16_t device_id; 552196200Sscottl uint8_t mbox[4]; 553196200Sscottl int error, fd; 554196200Sscottl 555196200Sscottl if (ac != 2) { 556196200Sscottl warnx("abort rebuild: %s", ac > 2 ? "extra arguments" : 557196200Sscottl "drive required"); 558196200Sscottl return (EINVAL); 559196200Sscottl } 560196200Sscottl 561237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 562196200Sscottl if (fd < 0) { 563214396Sjhb error = errno; 564196200Sscottl warn("mfi_open"); 565214396Sjhb return (error); 566196200Sscottl } 567196200Sscottl 568196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 569222899Sbz if (error) { 570222899Sbz close(fd); 571196200Sscottl return (error); 572222899Sbz } 573196200Sscottl 574196200Sscottl /* Get the info for this drive. */ 575196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 576214396Sjhb error = errno; 577196200Sscottl warn("Failed to fetch info for drive %u", device_id); 578222899Sbz close(fd); 579214396Sjhb return (error); 580196200Sscottl } 581196200Sscottl 582196200Sscottl /* Check the state, must be REBUILD. */ 583196200Sscottl if (info.fw_state != MFI_PD_STATE_REBUILD) { 584196200Sscottl warn("Drive %d is not in the REBUILD state", device_id); 585222899Sbz close(fd); 586196200Sscottl return (EINVAL); 587196200Sscottl } 588196200Sscottl 589196200Sscottl /* Abort the rebuild. */ 590196200Sscottl mbox_store_pdref(&mbox[0], &info.ref); 591196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4, 592196200Sscottl NULL) < 0) { 593214396Sjhb error = errno; 594196200Sscottl warn("Failed to abort rebuild on drive %u", device_id); 595222899Sbz close(fd); 596214396Sjhb return (error); 597196200Sscottl } 598196200Sscottl close(fd); 599196200Sscottl 600196200Sscottl return (0); 601196200Sscottl} 602196200SscottlMFI_COMMAND(abort, rebuild, abort_rebuild); 603196200Sscottl 604196200Sscottlstatic int 605196200Sscottldrive_progress(int ac, char **av) 606196200Sscottl{ 607196200Sscottl struct mfi_pd_info info; 608196200Sscottl uint16_t device_id; 609196200Sscottl int error, fd; 610196200Sscottl 611196200Sscottl if (ac != 2) { 612196200Sscottl warnx("drive progress: %s", ac > 2 ? "extra arguments" : 613196200Sscottl "drive required"); 614196200Sscottl return (EINVAL); 615196200Sscottl } 616196200Sscottl 617237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 618196200Sscottl if (fd < 0) { 619214396Sjhb error = errno; 620196200Sscottl warn("mfi_open"); 621214396Sjhb return (error); 622196200Sscottl } 623196200Sscottl 624196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 625222899Sbz if (error) { 626222899Sbz close(fd); 627196200Sscottl return (error); 628222899Sbz } 629196200Sscottl 630196200Sscottl /* Get the info for this drive. */ 631196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 632214396Sjhb error = errno; 633196200Sscottl warn("Failed to fetch info for drive %u", device_id); 634222899Sbz close(fd); 635214396Sjhb return (error); 636196200Sscottl } 637196200Sscottl close(fd); 638196200Sscottl 639196200Sscottl /* Display any of the active events. */ 640196200Sscottl if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD) 641196200Sscottl mfi_display_progress("Rebuild", &info.prog_info.rbld); 642196200Sscottl if (info.prog_info.active & MFI_PD_PROGRESS_PATROL) 643196200Sscottl mfi_display_progress("Patrol Read", &info.prog_info.patrol); 644196200Sscottl if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR) 645196200Sscottl mfi_display_progress("Clear", &info.prog_info.clear); 646196200Sscottl if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD | 647196200Sscottl MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0) 648223345Sbz printf("No activity in progress for drive %s.\n", 649223345Sbz mfi_drive_name(NULL, device_id, 650223345Sbz MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS)); 651196200Sscottl 652196200Sscottl return (0); 653196200Sscottl} 654196200SscottlMFI_COMMAND(drive, progress, drive_progress); 655196200Sscottl 656196200Sscottlstatic int 657196200Sscottldrive_clear(int ac, char **av) 658196200Sscottl{ 659196200Sscottl struct mfi_pd_info info; 660196200Sscottl uint32_t opcode; 661196200Sscottl uint16_t device_id; 662196200Sscottl uint8_t mbox[4]; 663196200Sscottl char *s1; 664196200Sscottl int error, fd; 665196200Sscottl 666196200Sscottl if (ac != 3) { 667196200Sscottl warnx("drive clear: %s", ac > 3 ? "extra arguments" : 668196200Sscottl "drive and action requires"); 669196200Sscottl return (EINVAL); 670196200Sscottl } 671196200Sscottl 672196200Sscottl for (s1 = av[2]; *s1 != '\0'; s1++) 673196200Sscottl *s1 = tolower(*s1); 674196200Sscottl if (strcmp(av[2], "start") == 0) 675196200Sscottl opcode = MFI_DCMD_PD_CLEAR_START; 676196200Sscottl else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0)) 677196200Sscottl opcode = MFI_DCMD_PD_CLEAR_ABORT; 678196200Sscottl else { 679196200Sscottl warnx("drive clear: invalid action, must be 'start' or 'stop'\n"); 680196200Sscottl return (EINVAL); 681196200Sscottl } 682196200Sscottl 683237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 684196200Sscottl if (fd < 0) { 685214396Sjhb error = errno; 686196200Sscottl warn("mfi_open"); 687214396Sjhb return (error); 688196200Sscottl } 689196200Sscottl 690196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 691222899Sbz if (error) { 692222899Sbz close(fd); 693196200Sscottl return (error); 694222899Sbz } 695196200Sscottl 696196200Sscottl /* Get the info for this drive. */ 697196200Sscottl if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) { 698214396Sjhb error = errno; 699196200Sscottl warn("Failed to fetch info for drive %u", device_id); 700222899Sbz close(fd); 701214396Sjhb return (error); 702196200Sscottl } 703196200Sscottl 704196200Sscottl mbox_store_pdref(&mbox[0], &info.ref); 705196200Sscottl if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) { 706214396Sjhb error = errno; 707196200Sscottl warn("Failed to %s clear on drive %u", 708196200Sscottl opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop", 709196200Sscottl device_id); 710222899Sbz close(fd); 711214396Sjhb return (error); 712196200Sscottl } 713196200Sscottl 714196200Sscottl close(fd); 715196200Sscottl return (0); 716196200Sscottl} 717196200SscottlMFI_COMMAND(drive, clear, drive_clear); 718196200Sscottl 719196200Sscottlstatic int 720196200Sscottldrive_locate(int ac, char **av) 721196200Sscottl{ 722196200Sscottl uint16_t device_id; 723196200Sscottl uint32_t opcode; 724196200Sscottl int error, fd; 725196200Sscottl uint8_t mbox[4]; 726196200Sscottl 727196200Sscottl if (ac != 3) { 728196200Sscottl warnx("locate: %s", ac > 3 ? "extra arguments" : 729196200Sscottl "drive and state required"); 730196200Sscottl return (EINVAL); 731196200Sscottl } 732196200Sscottl 733196200Sscottl if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0) 734196200Sscottl opcode = MFI_DCMD_PD_LOCATE_START; 735196200Sscottl else if (strcasecmp(av[2], "off") == 0 || 736196200Sscottl strcasecmp(av[2], "stop") == 0) 737196200Sscottl opcode = MFI_DCMD_PD_LOCATE_STOP; 738196200Sscottl else { 739196200Sscottl warnx("locate: invalid state %s", av[2]); 740196200Sscottl return (EINVAL); 741196200Sscottl } 742196200Sscottl 743237259Seadler fd = mfi_open(mfi_unit, O_RDWR); 744196200Sscottl if (fd < 0) { 745214396Sjhb error = errno; 746196200Sscottl warn("mfi_open"); 747214396Sjhb return (error); 748196200Sscottl } 749196200Sscottl 750196200Sscottl error = mfi_lookup_drive(fd, av[1], &device_id); 751222899Sbz if (error) { 752222899Sbz close(fd); 753196200Sscottl return (error); 754222899Sbz } 755196200Sscottl 756196200Sscottl 757196200Sscottl mbox_store_device_id(&mbox[0], device_id); 758196200Sscottl mbox[2] = 0; 759196200Sscottl mbox[3] = 0; 760196200Sscottl if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) { 761214396Sjhb error = errno; 762196200Sscottl warn("Failed to %s locate on drive %u", 763196200Sscottl opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop", 764196200Sscottl device_id); 765222899Sbz close(fd); 766214396Sjhb return (error); 767196200Sscottl } 768196200Sscottl close(fd); 769196200Sscottl 770196200Sscottl return (0); 771196200Sscottl} 772196200SscottlMFI_COMMAND(top, locate, drive_locate); 773