mpt_drive.c revision 215046
1156888Srwatson/*- 2156888Srwatson * Copyright (c) 2008 Yahoo!, Inc. 3156888Srwatson * All rights reserved. 4156888Srwatson * Written by: John Baldwin <jhb@FreeBSD.org> 5156888Srwatson * 6156888Srwatson * Redistribution and use in source and binary forms, with or without 7156888Srwatson * modification, are permitted provided that the following conditions 8156888Srwatson * are met: 9156888Srwatson * 1. Redistributions of source code must retain the above copyright 10156888Srwatson * notice, this list of conditions and the following disclaimer. 11156888Srwatson * 2. Redistributions in binary form must reproduce the above copyright 12156888Srwatson * notice, this list of conditions and the following disclaimer in the 13156888Srwatson * documentation and/or other materials provided with the distribution. 14156888Srwatson * 3. Neither the name of the author nor the names of any co-contributors 15156888Srwatson * may be used to endorse or promote products derived from this software 16156888Srwatson * without specific prior written permission. 17156888Srwatson * 18156888Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19156888Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20156888Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21156888Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22156888Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23156888Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24156888Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25156888Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26156888Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27156888Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28156888Srwatson * SUCH DAMAGE. 29156888Srwatson */ 30156888Srwatson 31156888Srwatson#include <sys/cdefs.h> 32156888Srwatson__RCSID("$FreeBSD: head/usr.sbin/mptutil/mpt_drive.c 215046 2010-11-09 19:28:06Z jhb $"); 33156888Srwatson 34156888Srwatson#include <sys/param.h> 35156888Srwatson#include <sys/errno.h> 36156888Srwatson#include <ctype.h> 37156888Srwatson#include <err.h> 38156888Srwatson#include <libutil.h> 39156888Srwatson#include <limits.h> 40156888Srwatson#include <stdio.h> 41156888Srwatson#include <stdlib.h> 42156888Srwatson#include <string.h> 43156888Srwatson#include <strings.h> 44156888Srwatson#include <unistd.h> 45156888Srwatson 46156888Srwatson#include <camlib.h> 47156888Srwatson#include <cam/scsi/scsi_all.h> 48156888Srwatson 49156888Srwatson#include "mptutil.h" 50156888Srwatson 51156888Srwatsonconst char * 52156888Srwatsonmpt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0 *info) 53156888Srwatson{ 54156888Srwatson static char buf[16]; 55156888Srwatson 56156888Srwatson switch (info->PhysDiskStatus.State) { 57156888Srwatson case MPI_PHYSDISK0_STATUS_ONLINE: 58156888Srwatson if ((info->PhysDiskStatus.Flags & 59156888Srwatson MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC) && 60156888Srwatson info->PhysDiskSettings.HotSparePool == 0) 61156888Srwatson return ("REBUILD"); 62156888Srwatson else 63156888Srwatson return ("ONLINE"); 64156888Srwatson case MPI_PHYSDISK0_STATUS_MISSING: 65156888Srwatson return ("MISSING"); 66156888Srwatson case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE: 67156888Srwatson return ("NOT COMPATIBLE"); 68156888Srwatson case MPI_PHYSDISK0_STATUS_FAILED: 69156888Srwatson return ("FAILED"); 70156888Srwatson case MPI_PHYSDISK0_STATUS_INITIALIZING: 71156888Srwatson return ("INITIALIZING"); 72156888Srwatson case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED: 73156888Srwatson return ("OFFLINE REQUESTED"); 74156889Srwatson case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED: 75156888Srwatson return ("FAILED REQUESTED"); 76156888Srwatson case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE: 77156888Srwatson return ("OTHER OFFLINE"); 78156889Srwatson default: 79156889Srwatson sprintf(buf, "PSTATE 0x%02x", info->PhysDiskStatus.State); 80156889Srwatson return (buf); 81156889Srwatson } 82156889Srwatson} 83156889Srwatson 84156889Srwatson/* 85156889Srwatson * There are several ways to enumerate physical disks. Unfortunately, 86156889Srwatson * none of them are truly complete, so we have to build a union of all of 87156889Srwatson * them. Specifically: 88156889Srwatson * 89156889Srwatson * - IOC2 : This gives us a list of volumes, and by walking the volumes we 90156888Srwatson * can enumerate all of the drives attached to volumes including 91156888Srwatson * online drives and failed drives. 92156888Srwatson * - IOC3 : This gives us a list of all online physical drives including 93156888Srwatson * drives that are not part of a volume nor a spare drive. It 94156888Srwatson * does not include any failed drives. 95156888Srwatson * - IOC5 : This gives us a list of all spare drives including failed 96156888Srwatson * spares. 97156888Srwatson * 98156888Srwatson * The specific edge cases are that 1) a failed volume member can only be 99156888Srwatson * found via IOC2, 2) a drive that is neither a volume member nor a spare 100156888Srwatson * can only be found via IOC3, and 3) a failed spare can only be found via 101156888Srwatson * IOC5. 102156888Srwatson * 103156888Srwatson * To handle this, walk all of the three lists and use the following 104156888Srwatson * routine to add each drive encountered. It quietly succeeds if the 105162599Srwatson * drive is already present in the list. It also sorts the list as it 106162599Srwatson * inserts new drives. 107162599Srwatson */ 108162599Srwatsonstatic int 109162599Srwatsonmpt_pd_insert(int fd, struct mpt_drive_list *list, U8 PhysDiskNum) 110162599Srwatson{ 111156888Srwatson int i, j; 112162599Srwatson 113159264Srwatson /* 114159264Srwatson * First, do a simple linear search to see if we have already 115156888Srwatson * seen this drive. 116162599Srwatson */ 117162599Srwatson for (i = 0; i < list->ndrives; i++) { 118162599Srwatson if (list->drives[i]->PhysDiskNum == PhysDiskNum) 119162599Srwatson return (0); 120162599Srwatson if (list->drives[i]->PhysDiskNum > PhysDiskNum) 121162599Srwatson break; 122162599Srwatson } 123156888Srwatson 124156888Srwatson /* 125159264Srwatson * 'i' is our slot for the 'new' drive. Make room and then 126162599Srwatson * read the drive info. 127159264Srwatson */ 128159332Srwatson for (j = list->ndrives - 1; j >= i; j--) 129156888Srwatson list->drives[j + 1] = list->drives[j]; 130156888Srwatson list->drives[i] = mpt_pd_info(fd, PhysDiskNum, NULL); 131156888Srwatson if (list->drives[i] == NULL) 132156889Srwatson return (errno); 133162599Srwatson list->ndrives++; 134162599Srwatson return (0); 135156888Srwatson} 136162599Srwatson 137162599Srwatsonstruct mpt_drive_list * 138162599Srwatsonmpt_pd_list(int fd) 139156888Srwatson{ 140162599Srwatson CONFIG_PAGE_IOC_2 *ioc2; 141156888Srwatson CONFIG_PAGE_IOC_2_RAID_VOL *vol; 142162599Srwatson CONFIG_PAGE_RAID_VOL_0 **volumes; 143162599Srwatson RAID_VOL0_PHYS_DISK *rdisk; 144156889Srwatson CONFIG_PAGE_IOC_3 *ioc3; 145156888Srwatson IOC_3_PHYS_DISK *disk; 146156888Srwatson CONFIG_PAGE_IOC_5 *ioc5; 147162599Srwatson IOC_5_HOT_SPARE *spare; 148162599Srwatson struct mpt_drive_list *list; 149162599Srwatson int count, error, i, j; 150162599Srwatson 151162599Srwatson ioc2 = mpt_read_ioc_page(fd, 2, NULL); 152162599Srwatson if (ioc2 == NULL) { 153162599Srwatson error = errno; 154162599Srwatson warn("Failed to fetch volume list"); 155162599Srwatson errno = error; 156162599Srwatson return (NULL); 157162599Srwatson } 158162599Srwatson 159162599Srwatson ioc3 = mpt_read_ioc_page(fd, 3, NULL); 160162599Srwatson if (ioc3 == NULL) { 161162599Srwatson error = errno; 162162599Srwatson warn("Failed to fetch drive list"); 163162599Srwatson free(ioc2); 164162599Srwatson errno = error; 165162599Srwatson return (NULL); 166162599Srwatson } 167162599Srwatson 168162599Srwatson ioc5 = mpt_read_ioc_page(fd, 5, NULL); 169162599Srwatson if (ioc5 == NULL) { 170162599Srwatson error = errno; 171156888Srwatson warn("Failed to fetch spare list"); 172162599Srwatson free(ioc3); 173162599Srwatson free(ioc2); 174162599Srwatson errno = error; 175162599Srwatson return (NULL); 176156888Srwatson } 177156889Srwatson 178162599Srwatson /* 179162599Srwatson * Go ahead and read the info for all the volumes. For this 180162599Srwatson * pass we figure out how many physical drives there are. 181156888Srwatson */ 182162599Srwatson volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 183156889Srwatson count = 0; 184162599Srwatson vol = ioc2->RaidVolume; 185156888Srwatson for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 186162599Srwatson volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 187162599Srwatson NULL); 188162599Srwatson if (volumes[i] == NULL) { 189162599Srwatson error = errno; 190156888Srwatson warn("Failed to read volume info"); 191162599Srwatson errno = error; 192162599Srwatson return (NULL); 193156888Srwatson } 194162599Srwatson count += volumes[i]->NumPhysDisks; 195156888Srwatson } 196156889Srwatson count += ioc3->NumPhysDisks; 197162599Srwatson count += ioc5->NumHotSpares; 198162599Srwatson 199162599Srwatson /* Walk the various lists enumerating drives. */ 200156888Srwatson list = malloc(sizeof(*list) + sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) * 201162599Srwatson count); 202156888Srwatson list->ndrives = 0; 203156888Srwatson 204162508Srwatson for (i = 0; i < ioc2->NumActiveVolumes; i++) { 205156888Srwatson rdisk = volumes[i]->PhysDisk; 206156888Srwatson for (j = 0; j < volumes[i]->NumPhysDisks; rdisk++, j++) 207156888Srwatson if (mpt_pd_insert(fd, list, rdisk->PhysDiskNum) < 0) 208156888Srwatson return (NULL); 209156889Srwatson free(volumes[i]); 210156889Srwatson } 211156889Srwatson free(ioc2); 212162599Srwatson free(volumes); 213156889Srwatson 214156889Srwatson spare = ioc5->HotSpare; 215156889Srwatson for (i = 0; i < ioc5->NumHotSpares; spare++, i++) 216156889Srwatson if (mpt_pd_insert(fd, list, spare->PhysDiskNum) < 0) 217156888Srwatson return (NULL); 218162599Srwatson free(ioc5); 219162599Srwatson 220162599Srwatson disk = ioc3->PhysDisk; 221162599Srwatson for (i = 0; i < ioc3->NumPhysDisks; disk++, i++) 222162599Srwatson if (mpt_pd_insert(fd, list, disk->PhysDiskNum) < 0) 223162599Srwatson return (NULL); 224162599Srwatson free(ioc3); 225162599Srwatson 226162599Srwatson return (list); 227162599Srwatson} 228162599Srwatson 229165604Srwatsonvoid 230162599Srwatsonmpt_free_pd_list(struct mpt_drive_list *list) 231162599Srwatson{ 232162599Srwatson int i; 233162599Srwatson 234156888Srwatson for (i = 0; i < list->ndrives; i++) 235156888Srwatson free(list->drives[i]); 236162599Srwatson free(list); 237159264Srwatson} 238162599Srwatson 239162599Srwatsonint 240162599Srwatsonmpt_lookup_drive(struct mpt_drive_list *list, const char *drive, 241162599Srwatson U8 *PhysDiskNum) 242156888Srwatson{ 243156888Srwatson long val; 244162599Srwatson uint8_t bus, id; 245162599Srwatson char *cp; 246162599Srwatson 247162599Srwatson /* Look for a raw device id first. */ 248162599Srwatson val = strtol(drive, &cp, 0); 249165604Srwatson if (*cp == '\0') { 250165604Srwatson if (val < 0 || val > 0xff) 251156888Srwatson goto bad; 252162599Srwatson *PhysDiskNum = val; 253162599Srwatson return (0); 254162599Srwatson } 255162599Srwatson 256162599Srwatson /* Look for a <bus>:<id> string. */ 257162599Srwatson if (*cp == ':') { 258162599Srwatson if (val < 0 || val > 0xff) 259162599Srwatson goto bad; 260162599Srwatson bus = val; 261162599Srwatson val = strtol(cp + 1, &cp, 0); 262162599Srwatson if (*cp != '\0') 263162599Srwatson goto bad; 264162599Srwatson if (val < 0 || val > 0xff) 265162599Srwatson goto bad; 266162599Srwatson id = val; 267162599Srwatson 268162599Srwatson for (val = 0; val < list->ndrives; val++) { 269162599Srwatson if (list->drives[val]->PhysDiskBus == bus && 270162599Srwatson list->drives[val]->PhysDiskID == id) { 271156888Srwatson *PhysDiskNum = list->drives[val]->PhysDiskNum; 272156888Srwatson return (0); 273156888Srwatson } 274162599Srwatson } 275156888Srwatson return (ENOENT); 276162599Srwatson } 277162599Srwatson 278156888Srwatsonbad: 279162599Srwatson return (EINVAL); 280162599Srwatson} 281162599Srwatson 282162599Srwatson/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 283162599Srwatsonconst char * 284162599Srwatsonmpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0 *pd_info) 285162599Srwatson{ 286162599Srwatson RAID_PHYS_DISK0_INQUIRY_DATA *inq_data; 287162599Srwatson u_char vendor[9], product[17], revision[5]; 288162599Srwatson static char inq_string[64]; 289162599Srwatson 290162599Srwatson inq_data = &pd_info->InquiryData; 291162599Srwatson cam_strvis(vendor, inq_data->VendorID, sizeof(inq_data->VendorID), 292156888Srwatson sizeof(vendor)); 293156888Srwatson cam_strvis(product, inq_data->ProductID, sizeof(inq_data->ProductID), 294156888Srwatson sizeof(product)); 295156888Srwatson cam_strvis(revision, inq_data->ProductRevLevel, 296156888Srwatson sizeof(inq_data->ProductRevLevel), sizeof(revision)); 297156888Srwatson 298156888Srwatson /* Total hack. */ 299156888Srwatson if (strcmp(vendor, "ATA") == 0) 300165604Srwatson snprintf(inq_string, sizeof(inq_string), "<%s %s> SATA", 301165604Srwatson product, revision); 302165604Srwatson else 303156888Srwatson snprintf(inq_string, sizeof(inq_string), "<%s %s %s> SAS", 304156888Srwatson vendor, product, revision); 305156888Srwatson return (inq_string); 306156888Srwatson} 307156888Srwatson 308156888Srwatson/* Helper function to set a drive to a given state. */ 309156888Srwatsonstatic int 310156888Srwatsondrive_set_state(char *drive, U8 Action, U8 State, const char *name) 311156888Srwatson{ 312156888Srwatson CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 313156888Srwatson struct mpt_drive_list *list; 314156888Srwatson U8 PhysDiskNum; 315156888Srwatson int error, fd; 316156888Srwatson 317156888Srwatson fd = mpt_open(mpt_unit); 318156888Srwatson if (fd < 0) { 319156888Srwatson error = errno; 320156888Srwatson warn("mpt_open"); 321156888Srwatson return (error); 322156888Srwatson } 323156888Srwatson 324156888Srwatson list = mpt_pd_list(fd); 325156888Srwatson if (list == NULL) 326156888Srwatson return (errno); 327156888Srwatson 328156888Srwatson if (mpt_lookup_drive(list, drive, &PhysDiskNum) < 0) { 329156888Srwatson error = errno; 330156888Srwatson warn("Failed to find drive %s", drive); 331156888Srwatson return (error); 332156888Srwatson } 333156888Srwatson mpt_free_pd_list(list); 334156888Srwatson 335156888Srwatson /* Get the info for this drive. */ 336156888Srwatson info = mpt_pd_info(fd, PhysDiskNum, NULL); 337156888Srwatson if (info == NULL) { 338156888Srwatson error = errno; 339156888Srwatson warn("Failed to fetch info for drive %u", PhysDiskNum); 340156888Srwatson return (error); 341170196Srwatson } 342170196Srwatson 343170196Srwatson /* Try to change the state. */ 344170196Srwatson if (info->PhysDiskStatus.State == State) { 345170196Srwatson warnx("Drive %u is already in the desired state", PhysDiskNum); 346156888Srwatson return (EINVAL); 347156888Srwatson } 348156888Srwatson 349156888Srwatson error = mpt_raid_action(fd, Action, 0, 0, PhysDiskNum, 0, NULL, 0, NULL, 350156888Srwatson NULL, 0, NULL, NULL, 0); 351156888Srwatson if (error) { 352159264Srwatson warnc(error, "Failed to set drive %u to %s", PhysDiskNum, name); 353159264Srwatson return (error); 354159264Srwatson } 355159264Srwatson 356159264Srwatson free(info); 357159263Srwatson close(fd); 358159263Srwatson 359159263Srwatson return (0); 360159263Srwatson} 361159263Srwatson 362159264Srwatsonstatic int 363159269Srwatsonfail_drive(int ac, char **av) 364159269Srwatson{ 365159269Srwatson 366162599Srwatson if (ac != 2) { 367159263Srwatson warnx("fail: %s", ac > 2 ? "extra arguments" : 368162599Srwatson "drive required"); 369162599Srwatson return (EINVAL); 370162599Srwatson } 371162599Srwatson 372159269Srwatson return (drive_set_state(av[1], MPI_RAID_ACTION_FAIL_PHYSDISK, 373162599Srwatson MPI_PHYSDISK0_STATUS_FAILED_REQUESTED, "FAILED")); 374162599Srwatson} 375159264SrwatsonMPT_COMMAND(top, fail, fail_drive); 376162380Scsjp 377159269Srwatsonstatic int 378162380Scsjponline_drive(int ac, char **av) 379159269Srwatson{ 380159264Srwatson 381162380Scsjp if (ac != 2) { 382162380Scsjp warnx("online: %s", ac > 2 ? "extra arguments" : 383162380Scsjp "drive required"); 384159269Srwatson return (EINVAL); 385159264Srwatson } 386159269Srwatson 387159269Srwatson return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_ONLINE, 388159269Srwatson MPI_PHYSDISK0_STATUS_ONLINE, "ONLINE")); 389159269Srwatson} 390159269SrwatsonMPT_COMMAND(top, online, online_drive); 391159269Srwatson 392159269Srwatsonstatic int 393159264Srwatsonoffline_drive(int ac, char **av) 394162599Srwatson{ 395162599Srwatson 396159269Srwatson if (ac != 2) { 397159269Srwatson warnx("offline: %s", ac > 2 ? "extra arguments" : 398159264Srwatson "drive required"); 399159269Srwatson return (EINVAL); 400159269Srwatson } 401159269Srwatson 402159269Srwatson return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_OFFLINE, 403159269Srwatson MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED, "OFFLINE")); 404159269Srwatson} 405159269SrwatsonMPT_COMMAND(top, offline, offline_drive); 406159269Srwatson