mpt_drive.c revision 215046
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 2008 Yahoo!, Inc. 31590Srgrimes * All rights reserved. 41590Srgrimes * Written by: John Baldwin <jhb@FreeBSD.org> 51590Srgrimes * 61590Srgrimes * Redistribution and use in source and binary forms, with or without 71590Srgrimes * modification, are permitted provided that the following conditions 81590Srgrimes * are met: 91590Srgrimes * 1. Redistributions of source code must retain the above copyright 101590Srgrimes * notice, this list of conditions and the following disclaimer. 111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer in the 131590Srgrimes * documentation and/or other materials provided with the distribution. 141590Srgrimes * 3. Neither the name of the author nor the names of any co-contributors 151590Srgrimes * may be used to endorse or promote products derived from this software 161590Srgrimes * without specific prior written permission. 171590Srgrimes * 181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281590Srgrimes * SUCH DAMAGE. 291590Srgrimes */ 301590Srgrimes 3127443Scharnier#include <sys/cdefs.h> 321590Srgrimes__RCSID("$FreeBSD: head/usr.sbin/mptutil/mpt_drive.c 215046 2010-11-09 19:28:06Z jhb $"); 331590Srgrimes 341590Srgrimes#include <sys/param.h> 351590Srgrimes#include <sys/errno.h> 361590Srgrimes#include <ctype.h> 3727443Scharnier#include <err.h> 381590Srgrimes#include <libutil.h> 3927443Scharnier#include <limits.h> 401590Srgrimes#include <stdio.h> 4199112Sobrien#include <stdlib.h> 4299112Sobrien#include <string.h> 431590Srgrimes#include <strings.h> 4455206Speter#include <unistd.h> 452215Scsgr 462215Scsgr#include <camlib.h> 4755206Speter#include <cam/scsi/scsi_all.h> 481590Srgrimes 491590Srgrimes#include "mptutil.h" 50100824Sdwmalone 511590Srgrimesconst char * 52100824Sdwmalonempt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0 *info) 531590Srgrimes{ 541590Srgrimes static char buf[16]; 551590Srgrimes 56165758Srodrigc switch (info->PhysDiskStatus.State) { 57176471Sdes case MPI_PHYSDISK0_STATUS_ONLINE: 58219043Sdchagin if ((info->PhysDiskStatus.Flags & 59176471Sdes MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC) && 60219043Sdchagin info->PhysDiskSettings.HotSparePool == 0) 61176471Sdes return ("REBUILD"); 62176471Sdes else 63176471Sdes return ("ONLINE"); 64176471Sdes case MPI_PHYSDISK0_STATUS_MISSING: 65176471Sdes return ("MISSING"); 66176471Sdes case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE: 67176471Sdes return ("NOT COMPATIBLE"); 68190168Sdelphij case MPI_PHYSDISK0_STATUS_FAILED: 69176471Sdes return ("FAILED"); 70190168Sdelphij case MPI_PHYSDISK0_STATUS_INITIALIZING: 71165916Sjhb return ("INITIALIZING"); 7227443Scharnier case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED: 73176471Sdes return ("OFFLINE REQUESTED"); 74176471Sdes case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED: 7527443Scharnier return ("FAILED REQUESTED"); 76176471Sdes case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE: 771590Srgrimes return ("OTHER OFFLINE"); 781590Srgrimes default: 791590Srgrimes sprintf(buf, "PSTATE 0x%02x", info->PhysDiskStatus.State); 80176471Sdes return (buf); 8127443Scharnier } 8227443Scharnier} 831590Srgrimes 84158766Snetchild/* 851590Srgrimes * There are several ways to enumerate physical disks. Unfortunately, 86219043Sdchagin * none of them are truly complete, so we have to build a union of all of 87219043Sdchagin * them. Specifically: 88100824Sdwmalone * 89100824Sdwmalone * - IOC2 : This gives us a list of volumes, and by walking the volumes we 90219043Sdchagin * can enumerate all of the drives attached to volumes including 91219043Sdchagin * online drives and failed drives. 92100824Sdwmalone * - IOC3 : This gives us a list of all online physical drives including 93115759Speter * drives that are not part of a volume nor a spare drive. It 94115759Speter * does not include any failed drives. 95100824Sdwmalone * - IOC5 : This gives us a list of all spare drives including failed 96219138Sdchagin * spares. 97100824Sdwmalone * 98234494Sjhb * The specific edge cases are that 1) a failed volume member can only be 99226329Sdes * found via IOC2, 2) a drive that is neither a volume member nor a spare 100226329Sdes * can only be found via IOC3, and 3) a failed spare can only be found via 101100824Sdwmalone * IOC5. 102176471Sdes * 103176471Sdes * To handle this, walk all of the three lists and use the following 104176471Sdes * routine to add each drive encountered. It quietly succeeds if the 105226269Sdes * drive is already present in the list. It also sorts the list as it 106233925Sjhb * inserts new drives. 107233925Sjhb */ 108100824Sdwmalonestatic int 109226157Sdesmpt_pd_insert(int fd, struct mpt_drive_list *list, U8 PhysDiskNum) 110100824Sdwmalone{ 111176471Sdes int i, j; 112219043Sdchagin 113100824Sdwmalone /* 1141590Srgrimes * First, do a simple linear search to see if we have already 1151590Srgrimes * seen this drive. 116176471Sdes */ 1171590Srgrimes for (i = 0; i < list->ndrives; i++) { 1181590Srgrimes if (list->drives[i]->PhysDiskNum == PhysDiskNum) 119226262Sdes return (0); 120226262Sdes if (list->drives[i]->PhysDiskNum > PhysDiskNum) 121226262Sdes break; 122226262Sdes } 123226262Sdes 124226262Sdes /* 125226262Sdes * 'i' is our slot for the 'new' drive. Make room and then 126226262Sdes * read the drive info. 127226164Sdes */ 128219138Sdchagin for (j = list->ndrives - 1; j >= i; j--) 129219138Sdchagin list->drives[j + 1] = list->drives[j]; 130219138Sdchagin list->drives[i] = mpt_pd_info(fd, PhysDiskNum, NULL); 131219138Sdchagin if (list->drives[i] == NULL) 132219138Sdchagin return (errno); 133219138Sdchagin list->ndrives++; 134219138Sdchagin return (0); 135219138Sdchagin} 136219138Sdchagin 137219138Sdchaginstruct mpt_drive_list * 138219138Sdchaginmpt_pd_list(int fd) 139219138Sdchagin{ 140219138Sdchagin CONFIG_PAGE_IOC_2 *ioc2; 141219138Sdchagin CONFIG_PAGE_IOC_2_RAID_VOL *vol; 142219138Sdchagin CONFIG_PAGE_RAID_VOL_0 **volumes; 143219138Sdchagin RAID_VOL0_PHYS_DISK *rdisk; 144219138Sdchagin CONFIG_PAGE_IOC_3 *ioc3; 145219138Sdchagin IOC_3_PHYS_DISK *disk; 146219138Sdchagin CONFIG_PAGE_IOC_5 *ioc5; 147219138Sdchagin IOC_5_HOT_SPARE *spare; 148219138Sdchagin struct mpt_drive_list *list; 149219138Sdchagin int count, error, i, j; 150219138Sdchagin 151219138Sdchagin ioc2 = mpt_read_ioc_page(fd, 2, NULL); 152219138Sdchagin if (ioc2 == NULL) { 153219138Sdchagin error = errno; 154219043Sdchagin warn("Failed to fetch volume list"); 155219043Sdchagin errno = error; 156219043Sdchagin return (NULL); 157219043Sdchagin } 158219043Sdchagin 159219043Sdchagin ioc3 = mpt_read_ioc_page(fd, 3, NULL); 160219043Sdchagin if (ioc3 == NULL) { 161219043Sdchagin error = errno; 162219043Sdchagin warn("Failed to fetch drive list"); 163100824Sdwmalone free(ioc2); 164100824Sdwmalone errno = error; 1651590Srgrimes return (NULL); 1661590Srgrimes } 167100824Sdwmalone 1681590Srgrimes ioc5 = mpt_read_ioc_page(fd, 5, NULL); 169112201Sjhb if (ioc5 == NULL) { 170115759Speter error = errno; 171219043Sdchagin warn("Failed to fetch spare list"); 1721590Srgrimes free(ioc3); 173226153Sdes free(ioc2); 17411823Sache errno = error; 175219043Sdchagin return (NULL); 176226153Sdes } 177219043Sdchagin 178219043Sdchagin /* 179219043Sdchagin * Go ahead and read the info for all the volumes. For this 1801590Srgrimes * pass we figure out how many physical drives there are. 1811590Srgrimes */ 1821590Srgrimes volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 1831590Srgrimes count = 0; 1841590Srgrimes vol = ioc2->RaidVolume; 1851590Srgrimes for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 1861590Srgrimes volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 1871590Srgrimes NULL); 1881590Srgrimes if (volumes[i] == NULL) { 1891590Srgrimes error = errno; 1901590Srgrimes warn("Failed to read volume info"); 1911590Srgrimes errno = error; 1921590Srgrimes return (NULL); 1931590Srgrimes } 1941590Srgrimes count += volumes[i]->NumPhysDisks; 195115759Speter } 196115759Speter count += ioc3->NumPhysDisks; 197115759Speter count += ioc5->NumHotSpares; 198176471Sdes 199176471Sdes /* Walk the various lists enumerating drives. */ 200176471Sdes list = malloc(sizeof(*list) + sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) * 201152331Srwatson count); 202152331Srwatson list->ndrives = 0; 203152331Srwatson 204123187Speter for (i = 0; i < ioc2->NumActiveVolumes; i++) { 205123187Speter rdisk = volumes[i]->PhysDisk; 206123187Speter for (j = 0; j < volumes[i]->NumPhysDisks; rdisk++, j++) 207151930Srwatson if (mpt_pd_insert(fd, list, rdisk->PhysDiskNum) < 0) 208151930Srwatson return (NULL); 209151930Srwatson free(volumes[i]); 2101590Srgrimes } 2111590Srgrimes free(ioc2); 2121590Srgrimes free(volumes); 2131590Srgrimes 2141590Srgrimes spare = ioc5->HotSpare; 2151590Srgrimes for (i = 0; i < ioc5->NumHotSpares; spare++, i++) 2161590Srgrimes if (mpt_pd_insert(fd, list, spare->PhysDiskNum) < 0) 2171590Srgrimes return (NULL); 21827443Scharnier free(ioc5); 21927443Scharnier 2201590Srgrimes disk = ioc3->PhysDisk; 2211590Srgrimes for (i = 0; i < ioc3->NumPhysDisks; disk++, i++) 2221590Srgrimes if (mpt_pd_insert(fd, list, disk->PhysDiskNum) < 0) 2231590Srgrimes return (NULL); 2241590Srgrimes free(ioc3); 22519853Sfenner 2261590Srgrimes return (list); 2271590Srgrimes} 228226153Sdes 22927443Scharniervoid 23027443Scharniermpt_free_pd_list(struct mpt_drive_list *list) 23127443Scharnier{ 23227443Scharnier int i; 233219043Sdchagin 234112201Sjhb for (i = 0; i < list->ndrives; i++) 2351590Srgrimes free(list->drives[i]); 236112201Sjhb free(list); 237112201Sjhb} 238151930Srwatson 239226153Sdesint 240203551Sjhmpt_lookup_drive(struct mpt_drive_list *list, const char *drive, 241203551Sjh U8 *PhysDiskNum) 242203551Sjh{ 243203551Sjh long val; 244203551Sjh uint8_t bus, id; 245151930Srwatson char *cp; 246151930Srwatson 247226153Sdes /* Look for a raw device id first. */ 248203551Sjh val = strtol(drive, &cp, 0); 249112201Sjhb if (*cp == '\0') { 250112201Sjhb if (val < 0 || val > 0xff) 251112201Sjhb goto bad; 252112201Sjhb *PhysDiskNum = val; 2531590Srgrimes return (0); 254236577Sjhb } 255236577Sjhb 256115759Speter /* Look for a <bus>:<id> string. */ 25727443Scharnier if (*cp == ':') { 25827443Scharnier if (val < 0 || val > 0xff) 2591590Srgrimes goto bad; 260226153Sdes bus = val; 26127443Scharnier val = strtol(cp + 1, &cp, 0); 26227443Scharnier if (*cp != '\0') 2631590Srgrimes goto bad; 2641590Srgrimes if (val < 0 || val > 0xff) 26527443Scharnier goto bad; 26627443Scharnier id = val; 267219043Sdchagin 268219043Sdchagin for (val = 0; val < list->ndrives; val++) { 269219043Sdchagin if (list->drives[val]->PhysDiskBus == bus && 270236577Sjhb list->drives[val]->PhysDiskID == id) { 271236577Sjhb *PhysDiskNum = list->drives[val]->PhysDiskNum; 272115759Speter return (0); 2731590Srgrimes } 2741590Srgrimes } 275112201Sjhb return (ENOENT); 2761590Srgrimes } 2771590Srgrimes 278219138Sdchaginbad: 279219138Sdchagin return (EINVAL); 280219138Sdchagin} 281219138Sdchagin 282219138Sdchagin/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 283219138Sdchaginconst char * 2841590Srgrimesmpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0 *pd_info) 2851590Srgrimes{ 286219138Sdchagin RAID_PHYS_DISK0_INQUIRY_DATA *inq_data; 287219138Sdchagin u_char vendor[9], product[17], revision[5]; 288219138Sdchagin static char inq_string[64]; 289219138Sdchagin 290219138Sdchagin inq_data = &pd_info->InquiryData; 291219138Sdchagin cam_strvis(vendor, inq_data->VendorID, sizeof(inq_data->VendorID), 2921590Srgrimes sizeof(vendor)); 2931590Srgrimes cam_strvis(product, inq_data->ProductID, sizeof(inq_data->ProductID), 294189707Sjhb sizeof(product)); 2951590Srgrimes cam_strvis(revision, inq_data->ProductRevLevel, 2961590Srgrimes sizeof(inq_data->ProductRevLevel), sizeof(revision)); 2971590Srgrimes 2981590Srgrimes /* Total hack. */ 2991590Srgrimes if (strcmp(vendor, "ATA") == 0) 3001590Srgrimes snprintf(inq_string, sizeof(inq_string), "<%s %s> SATA", 301219138Sdchagin product, revision); 3021590Srgrimes else 3031590Srgrimes snprintf(inq_string, sizeof(inq_string), "<%s %s %s> SAS", 304234494Sjhb vendor, product, revision); 305234494Sjhb return (inq_string); 306234494Sjhb} 307234494Sjhb 3081590Srgrimes/* Helper function to set a drive to a given state. */ 30918400Sphkstatic int 31018470Sphkdrive_set_state(char *drive, U8 Action, U8 State, const char *name) 31118400Sphk{ 312176471Sdes CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 313176471Sdes struct mpt_drive_list *list; 314176471Sdes U8 PhysDiskNum; 315226269Sdes int error, fd; 316226269Sdes 317233925Sjhb fd = mpt_open(mpt_unit); 318233925Sjhb if (fd < 0) { 319233925Sjhb error = errno; 320233925Sjhb warn("mpt_open"); 321233925Sjhb return (error); 322233925Sjhb } 323233925Sjhb 324112203Sjhb list = mpt_pd_list(fd); 325112203Sjhb if (list == NULL) 326112203Sjhb return (errno); 3271590Srgrimes 3281590Srgrimes if (mpt_lookup_drive(list, drive, &PhysDiskNum) < 0) { 329226153Sdes error = errno; 3301590Srgrimes warn("Failed to find drive %s", drive); 331100824Sdwmalone return (error); 3321590Srgrimes } 3331590Srgrimes mpt_free_pd_list(list); 334100824Sdwmalone 335100824Sdwmalone /* Get the info for this drive. */ 3361590Srgrimes info = mpt_pd_info(fd, PhysDiskNum, NULL); 3371590Srgrimes if (info == NULL) { 3381590Srgrimes error = errno; 3391590Srgrimes warn("Failed to fetch info for drive %u", PhysDiskNum); 340226153Sdes return (error); 3411590Srgrimes } 3421590Srgrimes 3431590Srgrimes /* Try to change the state. */ 3441590Srgrimes if (info->PhysDiskStatus.State == State) { 3451590Srgrimes warnx("Drive %u is already in the desired state", PhysDiskNum); 346219043Sdchagin return (EINVAL); 347219043Sdchagin } 348219043Sdchagin 349219043Sdchagin error = mpt_raid_action(fd, Action, 0, 0, PhysDiskNum, 0, NULL, 0, NULL, 350219043Sdchagin NULL, 0, NULL, NULL, 0); 351219043Sdchagin if (error) { 352219043Sdchagin warnc(error, "Failed to set drive %u to %s", PhysDiskNum, name); 353219043Sdchagin return (error); 354219043Sdchagin } 355219043Sdchagin 356219043Sdchagin free(info); 357219043Sdchagin close(fd); 358219043Sdchagin 359219043Sdchagin return (0); 360219043Sdchagin} 361219043Sdchagin 362219043Sdchaginstatic int 363219043Sdchaginfail_drive(int ac, char **av) 364219043Sdchagin{ 365219043Sdchagin 366219043Sdchagin if (ac != 2) { 367219043Sdchagin warnx("fail: %s", ac > 2 ? "extra arguments" : 368219043Sdchagin "drive required"); 369219043Sdchagin return (EINVAL); 370219043Sdchagin } 371219043Sdchagin 372219043Sdchagin return (drive_set_state(av[1], MPI_RAID_ACTION_FAIL_PHYSDISK, 373219043Sdchagin MPI_PHYSDISK0_STATUS_FAILED_REQUESTED, "FAILED")); 374219043Sdchagin} 375219043SdchaginMPT_COMMAND(top, fail, fail_drive); 376219043Sdchagin 377219043Sdchaginstatic int 378219043Sdchaginonline_drive(int ac, char **av) 379219043Sdchagin{ 380219043Sdchagin 381219043Sdchagin if (ac != 2) { 382219043Sdchagin warnx("online: %s", ac > 2 ? "extra arguments" : 383219043Sdchagin "drive required"); 384219043Sdchagin return (EINVAL); 385219043Sdchagin } 386219043Sdchagin 387219043Sdchagin return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_ONLINE, 388219043Sdchagin MPI_PHYSDISK0_STATUS_ONLINE, "ONLINE")); 389219043Sdchagin} 390219043SdchaginMPT_COMMAND(top, online, online_drive); 391219043Sdchagin 392219043Sdchaginstatic int 393219043Sdchaginoffline_drive(int ac, char **av) 394219043Sdchagin{ 395219043Sdchagin 396219043Sdchagin if (ac != 2) { 397219043Sdchagin warnx("offline: %s", ac > 2 ? "extra arguments" : 398219043Sdchagin "drive required"); 399219043Sdchagin return (EINVAL); 400219043Sdchagin } 401219043Sdchagin 402219043Sdchagin return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_OFFLINE, 403219043Sdchagin MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED, "OFFLINE")); 404219043Sdchagin} 405219043SdchaginMPT_COMMAND(top, offline, offline_drive); 406219043Sdchagin