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