1fc58801cSScott Long /*- 2fc58801cSScott Long * Copyright (c) 2008 Yahoo!, Inc. 3fc58801cSScott Long * All rights reserved. 4fc58801cSScott Long * Written by: John Baldwin <jhb@FreeBSD.org> 5fc58801cSScott Long * 6fc58801cSScott Long * Redistribution and use in source and binary forms, with or without 7fc58801cSScott Long * modification, are permitted provided that the following conditions 8fc58801cSScott Long * are met: 9fc58801cSScott Long * 1. Redistributions of source code must retain the above copyright 10fc58801cSScott Long * notice, this list of conditions and the following disclaimer. 11fc58801cSScott Long * 2. Redistributions in binary form must reproduce the above copyright 12fc58801cSScott Long * notice, this list of conditions and the following disclaimer in the 13fc58801cSScott Long * documentation and/or other materials provided with the distribution. 14fc58801cSScott Long * 3. Neither the name of the author nor the names of any co-contributors 15fc58801cSScott Long * may be used to endorse or promote products derived from this software 16fc58801cSScott Long * without specific prior written permission. 17fc58801cSScott Long * 18fc58801cSScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19fc58801cSScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20fc58801cSScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21fc58801cSScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22fc58801cSScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23fc58801cSScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24fc58801cSScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25fc58801cSScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26fc58801cSScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27fc58801cSScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28fc58801cSScott Long * SUCH DAMAGE. 29fc58801cSScott Long */ 30fc58801cSScott Long 31fc58801cSScott Long #include <sys/cdefs.h> 32fc58801cSScott Long __RCSID("$FreeBSD$"); 33fc58801cSScott Long 34fc58801cSScott Long #include <sys/param.h> 35fc58801cSScott Long #include <sys/errno.h> 36fc58801cSScott Long #include <ctype.h> 37fc58801cSScott Long #include <err.h> 38fc58801cSScott Long #include <libutil.h> 39fc58801cSScott Long #include <limits.h> 40fc58801cSScott Long #include <stdio.h> 41fc58801cSScott Long #include <stdlib.h> 42fc58801cSScott Long #include <string.h> 43fc58801cSScott Long #include <strings.h> 44fc58801cSScott Long #include <unistd.h> 45fc58801cSScott Long 46fc58801cSScott Long #include <camlib.h> 47fc58801cSScott Long #include <cam/scsi/scsi_all.h> 48fc58801cSScott Long 49fc58801cSScott Long #include "mptutil.h" 50fc58801cSScott Long 51fc58801cSScott Long const char * 52fc58801cSScott Long mpt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0 *info) 53fc58801cSScott Long { 54fc58801cSScott Long static char buf[16]; 55fc58801cSScott Long 56fc58801cSScott Long switch (info->PhysDiskStatus.State) { 57fc58801cSScott Long case MPI_PHYSDISK0_STATUS_ONLINE: 58fc58801cSScott Long if ((info->PhysDiskStatus.Flags & 59fc58801cSScott Long MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC) && 60fc58801cSScott Long info->PhysDiskSettings.HotSparePool == 0) 61fc58801cSScott Long return ("REBUILD"); 62fc58801cSScott Long else 63fc58801cSScott Long return ("ONLINE"); 64fc58801cSScott Long case MPI_PHYSDISK0_STATUS_MISSING: 65fc58801cSScott Long return ("MISSING"); 66fc58801cSScott Long case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE: 67fc58801cSScott Long return ("NOT COMPATIBLE"); 68fc58801cSScott Long case MPI_PHYSDISK0_STATUS_FAILED: 69fc58801cSScott Long return ("FAILED"); 70fc58801cSScott Long case MPI_PHYSDISK0_STATUS_INITIALIZING: 71fc58801cSScott Long return ("INITIALIZING"); 72fc58801cSScott Long case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED: 73fc58801cSScott Long return ("OFFLINE REQUESTED"); 74fc58801cSScott Long case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED: 75fc58801cSScott Long return ("FAILED REQUESTED"); 76fc58801cSScott Long case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE: 77fc58801cSScott Long return ("OTHER OFFLINE"); 78fc58801cSScott Long default: 79fc58801cSScott Long sprintf(buf, "PSTATE 0x%02x", info->PhysDiskStatus.State); 80fc58801cSScott Long return (buf); 81fc58801cSScott Long } 82fc58801cSScott Long } 83fc58801cSScott Long 84fc58801cSScott Long /* 85fc58801cSScott Long * There are several ways to enumerate physical disks. Unfortunately, 86fc58801cSScott Long * none of them are truly complete, so we have to build a union of all of 87fc58801cSScott Long * them. Specifically: 88fc58801cSScott Long * 89fc58801cSScott Long * - IOC2 : This gives us a list of volumes, and by walking the volumes we 90fc58801cSScott Long * can enumerate all of the drives attached to volumes including 91fc58801cSScott Long * online drives and failed drives. 92fc58801cSScott Long * - IOC3 : This gives us a list of all online physical drives including 93fc58801cSScott Long * drives that are not part of a volume nor a spare drive. It 94fc58801cSScott Long * does not include any failed drives. 95fc58801cSScott Long * - IOC5 : This gives us a list of all spare drives including failed 96fc58801cSScott Long * spares. 97fc58801cSScott Long * 98fc58801cSScott Long * The specific edge cases are that 1) a failed volume member can only be 99fc58801cSScott Long * found via IOC2, 2) a drive that is neither a volume member nor a spare 100fc58801cSScott Long * can only be found via IOC3, and 3) a failed spare can only be found via 101fc58801cSScott Long * IOC5. 102fc58801cSScott Long * 103fc58801cSScott Long * To handle this, walk all of the three lists and use the following 104fc58801cSScott Long * routine to add each drive encountered. It quietly succeeds if the 105fc58801cSScott Long * drive is already present in the list. It also sorts the list as it 106fc58801cSScott Long * inserts new drives. 107fc58801cSScott Long */ 108fc58801cSScott Long static int 109fc58801cSScott Long mpt_pd_insert(int fd, struct mpt_drive_list *list, U8 PhysDiskNum) 110fc58801cSScott Long { 111fc58801cSScott Long int i, j; 112fc58801cSScott Long 113fc58801cSScott Long /* 114fc58801cSScott Long * First, do a simple linear search to see if we have already 115fc58801cSScott Long * seen this drive. 116fc58801cSScott Long */ 117fc58801cSScott Long for (i = 0; i < list->ndrives; i++) { 118fc58801cSScott Long if (list->drives[i]->PhysDiskNum == PhysDiskNum) 119fc58801cSScott Long return (0); 120fc58801cSScott Long if (list->drives[i]->PhysDiskNum > PhysDiskNum) 121fc58801cSScott Long break; 122fc58801cSScott Long } 123fc58801cSScott Long 124fc58801cSScott Long /* 125fc58801cSScott Long * 'i' is our slot for the 'new' drive. Make room and then 126fc58801cSScott Long * read the drive info. 127fc58801cSScott Long */ 128fc58801cSScott Long for (j = list->ndrives - 1; j >= i; j--) 129fc58801cSScott Long list->drives[j + 1] = list->drives[j]; 130fc58801cSScott Long list->drives[i] = mpt_pd_info(fd, PhysDiskNum, NULL); 131fc58801cSScott Long if (list->drives[i] == NULL) 132fc58801cSScott Long return (-1); 133fc58801cSScott Long list->ndrives++; 134fc58801cSScott Long return (0); 135fc58801cSScott Long } 136fc58801cSScott Long 137fc58801cSScott Long struct mpt_drive_list * 138fc58801cSScott Long mpt_pd_list(int fd) 139fc58801cSScott Long { 140fc58801cSScott Long CONFIG_PAGE_IOC_2 *ioc2; 141fc58801cSScott Long CONFIG_PAGE_IOC_2_RAID_VOL *vol; 142fc58801cSScott Long CONFIG_PAGE_RAID_VOL_0 **volumes; 143fc58801cSScott Long RAID_VOL0_PHYS_DISK *rdisk; 144fc58801cSScott Long CONFIG_PAGE_IOC_3 *ioc3; 145fc58801cSScott Long IOC_3_PHYS_DISK *disk; 146fc58801cSScott Long CONFIG_PAGE_IOC_5 *ioc5; 147fc58801cSScott Long IOC_5_HOT_SPARE *spare; 148fc58801cSScott Long struct mpt_drive_list *list; 149fc58801cSScott Long int count, i, j; 150fc58801cSScott Long 151fc58801cSScott Long ioc2 = mpt_read_ioc_page(fd, 2, NULL); 152fc58801cSScott Long if (ioc2 == NULL) { 153fc58801cSScott Long warn("Failed to fetch volume list"); 154fc58801cSScott Long return (NULL); 155fc58801cSScott Long } 156fc58801cSScott Long 157fc58801cSScott Long ioc3 = mpt_read_ioc_page(fd, 3, NULL); 158fc58801cSScott Long if (ioc3 == NULL) { 159fc58801cSScott Long warn("Failed to fetch drive list"); 160fc58801cSScott Long free(ioc2); 161fc58801cSScott Long return (NULL); 162fc58801cSScott Long } 163fc58801cSScott Long 164fc58801cSScott Long ioc5 = mpt_read_ioc_page(fd, 5, NULL); 165fc58801cSScott Long if (ioc5 == NULL) { 166fc58801cSScott Long warn("Failed to fetch spare list"); 167fc58801cSScott Long free(ioc3); 168fc58801cSScott Long free(ioc2); 169fc58801cSScott Long return (NULL); 170fc58801cSScott Long } 171fc58801cSScott Long 172fc58801cSScott Long /* 173fc58801cSScott Long * Go ahead and read the info for all the volumes. For this 174fc58801cSScott Long * pass we figure out how many physical drives there are. 175fc58801cSScott Long */ 176fc58801cSScott Long volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 177fc58801cSScott Long count = 0; 178fc58801cSScott Long vol = ioc2->RaidVolume; 179fc58801cSScott Long for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 180fc58801cSScott Long volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 181fc58801cSScott Long NULL); 182fc58801cSScott Long if (volumes[i] == NULL) { 183fc58801cSScott Long warn("Failed to read volume info"); 184fc58801cSScott Long return (NULL); 185fc58801cSScott Long } 186fc58801cSScott Long count += volumes[i]->NumPhysDisks; 187fc58801cSScott Long } 188fc58801cSScott Long count += ioc3->NumPhysDisks; 189fc58801cSScott Long count += ioc5->NumHotSpares; 190fc58801cSScott Long 191fc58801cSScott Long /* Walk the various lists enumerating drives. */ 192fc58801cSScott Long list = malloc(sizeof(*list) + sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) * 193fc58801cSScott Long count); 194fc58801cSScott Long list->ndrives = 0; 195fc58801cSScott Long 196fc58801cSScott Long for (i = 0; i < ioc2->NumActiveVolumes; i++) { 197fc58801cSScott Long rdisk = volumes[i]->PhysDisk; 198fc58801cSScott Long for (j = 0; j < volumes[i]->NumPhysDisks; rdisk++, j++) 199fc58801cSScott Long if (mpt_pd_insert(fd, list, rdisk->PhysDiskNum) < 0) 200fc58801cSScott Long return (NULL); 201fc58801cSScott Long free(volumes[i]); 202fc58801cSScott Long } 203fc58801cSScott Long free(ioc2); 204fc58801cSScott Long free(volumes); 205fc58801cSScott Long 206fc58801cSScott Long spare = ioc5->HotSpare; 207fc58801cSScott Long for (i = 0; i < ioc5->NumHotSpares; spare++, i++) 208fc58801cSScott Long if (mpt_pd_insert(fd, list, spare->PhysDiskNum) < 0) 209fc58801cSScott Long return (NULL); 210fc58801cSScott Long free(ioc5); 211fc58801cSScott Long 212fc58801cSScott Long disk = ioc3->PhysDisk; 213fc58801cSScott Long for (i = 0; i < ioc3->NumPhysDisks; disk++, i++) 214fc58801cSScott Long if (mpt_pd_insert(fd, list, disk->PhysDiskNum) < 0) 215fc58801cSScott Long return (NULL); 216fc58801cSScott Long free(ioc3); 217fc58801cSScott Long 218fc58801cSScott Long return (list); 219fc58801cSScott Long } 220fc58801cSScott Long 221fc58801cSScott Long void 222fc58801cSScott Long mpt_free_pd_list(struct mpt_drive_list *list) 223fc58801cSScott Long { 224fc58801cSScott Long int i; 225fc58801cSScott Long 226fc58801cSScott Long for (i = 0; i < list->ndrives; i++) 227fc58801cSScott Long free(list->drives[i]); 228fc58801cSScott Long free(list); 229fc58801cSScott Long } 230fc58801cSScott Long 231fc58801cSScott Long int 232fc58801cSScott Long mpt_lookup_drive(struct mpt_drive_list *list, const char *drive, 233fc58801cSScott Long U8 *PhysDiskNum) 234fc58801cSScott Long { 235fc58801cSScott Long long val; 236fc58801cSScott Long uint8_t bus, id; 237fc58801cSScott Long char *cp; 238fc58801cSScott Long 239fc58801cSScott Long /* Look for a raw device id first. */ 240fc58801cSScott Long val = strtol(drive, &cp, 0); 241fc58801cSScott Long if (*cp == '\0') { 242fc58801cSScott Long if (val < 0 || val > 0xff) 243fc58801cSScott Long goto bad; 244fc58801cSScott Long *PhysDiskNum = val; 245fc58801cSScott Long return (0); 246fc58801cSScott Long } 247fc58801cSScott Long 248fc58801cSScott Long /* Look for a <bus>:<id> string. */ 249fc58801cSScott Long if (*cp == ':') { 250fc58801cSScott Long if (val < 0 || val > 0xff) 251fc58801cSScott Long goto bad; 252fc58801cSScott Long bus = val; 253fc58801cSScott Long val = strtol(cp + 1, &cp, 0); 254fc58801cSScott Long if (*cp != '\0') 255fc58801cSScott Long goto bad; 256fc58801cSScott Long if (val < 0 || val > 0xff) 257fc58801cSScott Long goto bad; 258fc58801cSScott Long id = val; 259fc58801cSScott Long 260fc58801cSScott Long for (val = 0; val < list->ndrives; val++) { 261fc58801cSScott Long if (list->drives[val]->PhysDiskBus == bus && 262fc58801cSScott Long list->drives[val]->PhysDiskID == id) { 263fc58801cSScott Long *PhysDiskNum = list->drives[val]->PhysDiskNum; 264fc58801cSScott Long return (0); 265fc58801cSScott Long } 266fc58801cSScott Long } 267fc58801cSScott Long errno = ENOENT; 268fc58801cSScott Long return (-1); 269fc58801cSScott Long } 270fc58801cSScott Long 271fc58801cSScott Long bad: 272fc58801cSScott Long errno = EINVAL; 273fc58801cSScott Long return (-1); 274fc58801cSScott Long } 275fc58801cSScott Long 276fc58801cSScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 277fc58801cSScott Long const char * 278fc58801cSScott Long mpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0 *pd_info) 279fc58801cSScott Long { 280fc58801cSScott Long RAID_PHYS_DISK0_INQUIRY_DATA *inq_data; 281fc58801cSScott Long u_char vendor[9], product[17], revision[5]; 282fc58801cSScott Long static char inq_string[64]; 283fc58801cSScott Long 284fc58801cSScott Long inq_data = &pd_info->InquiryData; 285fc58801cSScott Long cam_strvis(vendor, inq_data->VendorID, sizeof(inq_data->VendorID), 286fc58801cSScott Long sizeof(vendor)); 287fc58801cSScott Long cam_strvis(product, inq_data->ProductID, sizeof(inq_data->ProductID), 288fc58801cSScott Long sizeof(product)); 289fc58801cSScott Long cam_strvis(revision, inq_data->ProductRevLevel, 290fc58801cSScott Long sizeof(inq_data->ProductRevLevel), sizeof(revision)); 291fc58801cSScott Long 292fc58801cSScott Long /* Total hack. */ 293fc58801cSScott Long if (strcmp(vendor, "ATA") == 0) 294fc58801cSScott Long snprintf(inq_string, sizeof(inq_string), "<%s %s> SATA", 295fc58801cSScott Long product, revision); 296fc58801cSScott Long else 297fc58801cSScott Long snprintf(inq_string, sizeof(inq_string), "<%s %s %s> SAS", 298fc58801cSScott Long vendor, product, revision); 299fc58801cSScott Long return (inq_string); 300fc58801cSScott Long } 301fc58801cSScott Long 302fc58801cSScott Long /* Helper function to set a drive to a given state. */ 303fc58801cSScott Long static int 304fc58801cSScott Long drive_set_state(char *drive, U8 Action, U8 State, const char *name) 305fc58801cSScott Long { 306fc58801cSScott Long CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 307fc58801cSScott Long struct mpt_drive_list *list; 308fc58801cSScott Long U8 PhysDiskNum; 309fc58801cSScott Long int fd; 310fc58801cSScott Long 311fc58801cSScott Long fd = mpt_open(mpt_unit); 312fc58801cSScott Long if (fd < 0) { 313fc58801cSScott Long warn("mpt_open"); 314fc58801cSScott Long return (errno); 315fc58801cSScott Long } 316fc58801cSScott Long 317fc58801cSScott Long list = mpt_pd_list(fd); 318fc58801cSScott Long if (list == NULL) 319fc58801cSScott Long return (errno); 320fc58801cSScott Long 321fc58801cSScott Long if (mpt_lookup_drive(list, drive, &PhysDiskNum) < 0) { 322fc58801cSScott Long warn("Failed to find drive %s", drive); 323fc58801cSScott Long return (errno); 324fc58801cSScott Long } 325fc58801cSScott Long mpt_free_pd_list(list); 326fc58801cSScott Long 327fc58801cSScott Long /* Get the info for this drive. */ 328fc58801cSScott Long info = mpt_pd_info(fd, PhysDiskNum, NULL); 329fc58801cSScott Long if (info == NULL) { 330fc58801cSScott Long warn("Failed to fetch info for drive %u", PhysDiskNum); 331fc58801cSScott Long return (errno); 332fc58801cSScott Long } 333fc58801cSScott Long 334fc58801cSScott Long /* Try to change the state. */ 335fc58801cSScott Long if (info->PhysDiskStatus.State == State) { 336fc58801cSScott Long warnx("Drive %u is already in the desired state", PhysDiskNum); 337fc58801cSScott Long return (EINVAL); 338fc58801cSScott Long } 339fc58801cSScott Long 340fc58801cSScott Long if (mpt_raid_action(fd, Action, 0, 0, PhysDiskNum, 0, NULL, 0, NULL, 341fc58801cSScott Long NULL, 0, NULL, NULL, 0) < 0) { 342fc58801cSScott Long warn("Failed to set drive %u to %s", PhysDiskNum, name); 343fc58801cSScott Long return (errno); 344fc58801cSScott Long } 345fc58801cSScott Long 346fc58801cSScott Long free(info); 347fc58801cSScott Long close(fd); 348fc58801cSScott Long 349fc58801cSScott Long return (0); 350fc58801cSScott Long } 351fc58801cSScott Long 352fc58801cSScott Long static int 353fc58801cSScott Long fail_drive(int ac, char **av) 354fc58801cSScott Long { 355fc58801cSScott Long 356fc58801cSScott Long if (ac != 2) { 357fc58801cSScott Long warnx("fail: %s", ac > 2 ? "extra arguments" : 358fc58801cSScott Long "drive required"); 359fc58801cSScott Long return (EINVAL); 360fc58801cSScott Long } 361fc58801cSScott Long 362fc58801cSScott Long return (drive_set_state(av[1], MPI_RAID_ACTION_FAIL_PHYSDISK, 363fc58801cSScott Long MPI_PHYSDISK0_STATUS_FAILED_REQUESTED, "FAILED")); 364fc58801cSScott Long } 365fc58801cSScott Long MPT_COMMAND(top, fail, fail_drive); 366fc58801cSScott Long 367fc58801cSScott Long static int 368fc58801cSScott Long online_drive(int ac, char **av) 369fc58801cSScott Long { 370fc58801cSScott Long 371fc58801cSScott Long if (ac != 2) { 372fc58801cSScott Long warnx("online: %s", ac > 2 ? "extra arguments" : 373fc58801cSScott Long "drive required"); 374fc58801cSScott Long return (EINVAL); 375fc58801cSScott Long } 376fc58801cSScott Long 377fc58801cSScott Long return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_ONLINE, 378fc58801cSScott Long MPI_PHYSDISK0_STATUS_ONLINE, "ONLINE")); 379fc58801cSScott Long } 380fc58801cSScott Long MPT_COMMAND(top, online, online_drive); 381fc58801cSScott Long 382fc58801cSScott Long static int 383fc58801cSScott Long offline_drive(int ac, char **av) 384fc58801cSScott Long { 385fc58801cSScott Long 386fc58801cSScott Long if (ac != 2) { 387fc58801cSScott Long warnx("offline: %s", ac > 2 ? "extra arguments" : 388fc58801cSScott Long "drive required"); 389fc58801cSScott Long return (EINVAL); 390fc58801cSScott Long } 391fc58801cSScott Long 392fc58801cSScott Long return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_OFFLINE, 393fc58801cSScott Long MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED, "OFFLINE")); 394fc58801cSScott Long } 395fc58801cSScott Long MPT_COMMAND(top, offline, offline_drive); 396