mpt_drive.c revision 196212
1196212Sscottl/*- 2196212Sscottl * Copyright (c) 2008 Yahoo!, Inc. 3196212Sscottl * All rights reserved. 4196212Sscottl * Written by: John Baldwin <jhb@FreeBSD.org> 5196212Sscottl * 6196212Sscottl * Redistribution and use in source and binary forms, with or without 7196212Sscottl * modification, are permitted provided that the following conditions 8196212Sscottl * are met: 9196212Sscottl * 1. Redistributions of source code must retain the above copyright 10196212Sscottl * notice, this list of conditions and the following disclaimer. 11196212Sscottl * 2. Redistributions in binary form must reproduce the above copyright 12196212Sscottl * notice, this list of conditions and the following disclaimer in the 13196212Sscottl * documentation and/or other materials provided with the distribution. 14196212Sscottl * 3. Neither the name of the author nor the names of any co-contributors 15196212Sscottl * may be used to endorse or promote products derived from this software 16196212Sscottl * without specific prior written permission. 17196212Sscottl * 18196212Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19196212Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20196212Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21196212Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22196212Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23196212Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24196212Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25196212Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26196212Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27196212Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28196212Sscottl * SUCH DAMAGE. 29196212Sscottl */ 30196212Sscottl 31196212Sscottl#include <sys/cdefs.h> 32196212Sscottl__RCSID("$FreeBSD: head/usr.sbin/mptutil/mpt_drive.c 196212 2009-08-14 13:13:12Z scottl $"); 33196212Sscottl 34196212Sscottl#include <sys/param.h> 35196212Sscottl#include <sys/errno.h> 36196212Sscottl#include <ctype.h> 37196212Sscottl#include <err.h> 38196212Sscottl#include <libutil.h> 39196212Sscottl#include <limits.h> 40196212Sscottl#include <stdio.h> 41196212Sscottl#include <stdlib.h> 42196212Sscottl#include <string.h> 43196212Sscottl#include <strings.h> 44196212Sscottl#include <unistd.h> 45196212Sscottl 46196212Sscottl#include <camlib.h> 47196212Sscottl#include <cam/scsi/scsi_all.h> 48196212Sscottl 49196212Sscottl#include "mptutil.h" 50196212Sscottl 51196212Sscottlconst char * 52196212Sscottlmpt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0 *info) 53196212Sscottl{ 54196212Sscottl static char buf[16]; 55196212Sscottl 56196212Sscottl switch (info->PhysDiskStatus.State) { 57196212Sscottl case MPI_PHYSDISK0_STATUS_ONLINE: 58196212Sscottl if ((info->PhysDiskStatus.Flags & 59196212Sscottl MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC) && 60196212Sscottl info->PhysDiskSettings.HotSparePool == 0) 61196212Sscottl return ("REBUILD"); 62196212Sscottl else 63196212Sscottl return ("ONLINE"); 64196212Sscottl case MPI_PHYSDISK0_STATUS_MISSING: 65196212Sscottl return ("MISSING"); 66196212Sscottl case MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE: 67196212Sscottl return ("NOT COMPATIBLE"); 68196212Sscottl case MPI_PHYSDISK0_STATUS_FAILED: 69196212Sscottl return ("FAILED"); 70196212Sscottl case MPI_PHYSDISK0_STATUS_INITIALIZING: 71196212Sscottl return ("INITIALIZING"); 72196212Sscottl case MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED: 73196212Sscottl return ("OFFLINE REQUESTED"); 74196212Sscottl case MPI_PHYSDISK0_STATUS_FAILED_REQUESTED: 75196212Sscottl return ("FAILED REQUESTED"); 76196212Sscottl case MPI_PHYSDISK0_STATUS_OTHER_OFFLINE: 77196212Sscottl return ("OTHER OFFLINE"); 78196212Sscottl default: 79196212Sscottl sprintf(buf, "PSTATE 0x%02x", info->PhysDiskStatus.State); 80196212Sscottl return (buf); 81196212Sscottl } 82196212Sscottl} 83196212Sscottl 84196212Sscottl/* 85196212Sscottl * There are several ways to enumerate physical disks. Unfortunately, 86196212Sscottl * none of them are truly complete, so we have to build a union of all of 87196212Sscottl * them. Specifically: 88196212Sscottl * 89196212Sscottl * - IOC2 : This gives us a list of volumes, and by walking the volumes we 90196212Sscottl * can enumerate all of the drives attached to volumes including 91196212Sscottl * online drives and failed drives. 92196212Sscottl * - IOC3 : This gives us a list of all online physical drives including 93196212Sscottl * drives that are not part of a volume nor a spare drive. It 94196212Sscottl * does not include any failed drives. 95196212Sscottl * - IOC5 : This gives us a list of all spare drives including failed 96196212Sscottl * spares. 97196212Sscottl * 98196212Sscottl * The specific edge cases are that 1) a failed volume member can only be 99196212Sscottl * found via IOC2, 2) a drive that is neither a volume member nor a spare 100196212Sscottl * can only be found via IOC3, and 3) a failed spare can only be found via 101196212Sscottl * IOC5. 102196212Sscottl * 103196212Sscottl * To handle this, walk all of the three lists and use the following 104196212Sscottl * routine to add each drive encountered. It quietly succeeds if the 105196212Sscottl * drive is already present in the list. It also sorts the list as it 106196212Sscottl * inserts new drives. 107196212Sscottl */ 108196212Sscottlstatic int 109196212Sscottlmpt_pd_insert(int fd, struct mpt_drive_list *list, U8 PhysDiskNum) 110196212Sscottl{ 111196212Sscottl int i, j; 112196212Sscottl 113196212Sscottl /* 114196212Sscottl * First, do a simple linear search to see if we have already 115196212Sscottl * seen this drive. 116196212Sscottl */ 117196212Sscottl for (i = 0; i < list->ndrives; i++) { 118196212Sscottl if (list->drives[i]->PhysDiskNum == PhysDiskNum) 119196212Sscottl return (0); 120196212Sscottl if (list->drives[i]->PhysDiskNum > PhysDiskNum) 121196212Sscottl break; 122196212Sscottl } 123196212Sscottl 124196212Sscottl /* 125196212Sscottl * 'i' is our slot for the 'new' drive. Make room and then 126196212Sscottl * read the drive info. 127196212Sscottl */ 128196212Sscottl for (j = list->ndrives - 1; j >= i; j--) 129196212Sscottl list->drives[j + 1] = list->drives[j]; 130196212Sscottl list->drives[i] = mpt_pd_info(fd, PhysDiskNum, NULL); 131196212Sscottl if (list->drives[i] == NULL) 132196212Sscottl return (-1); 133196212Sscottl list->ndrives++; 134196212Sscottl return (0); 135196212Sscottl} 136196212Sscottl 137196212Sscottlstruct mpt_drive_list * 138196212Sscottlmpt_pd_list(int fd) 139196212Sscottl{ 140196212Sscottl CONFIG_PAGE_IOC_2 *ioc2; 141196212Sscottl CONFIG_PAGE_IOC_2_RAID_VOL *vol; 142196212Sscottl CONFIG_PAGE_RAID_VOL_0 **volumes; 143196212Sscottl RAID_VOL0_PHYS_DISK *rdisk; 144196212Sscottl CONFIG_PAGE_IOC_3 *ioc3; 145196212Sscottl IOC_3_PHYS_DISK *disk; 146196212Sscottl CONFIG_PAGE_IOC_5 *ioc5; 147196212Sscottl IOC_5_HOT_SPARE *spare; 148196212Sscottl struct mpt_drive_list *list; 149196212Sscottl int count, i, j; 150196212Sscottl 151196212Sscottl ioc2 = mpt_read_ioc_page(fd, 2, NULL); 152196212Sscottl if (ioc2 == NULL) { 153196212Sscottl warn("Failed to fetch volume list"); 154196212Sscottl return (NULL); 155196212Sscottl } 156196212Sscottl 157196212Sscottl ioc3 = mpt_read_ioc_page(fd, 3, NULL); 158196212Sscottl if (ioc3 == NULL) { 159196212Sscottl warn("Failed to fetch drive list"); 160196212Sscottl free(ioc2); 161196212Sscottl return (NULL); 162196212Sscottl } 163196212Sscottl 164196212Sscottl ioc5 = mpt_read_ioc_page(fd, 5, NULL); 165196212Sscottl if (ioc5 == NULL) { 166196212Sscottl warn("Failed to fetch spare list"); 167196212Sscottl free(ioc3); 168196212Sscottl free(ioc2); 169196212Sscottl return (NULL); 170196212Sscottl } 171196212Sscottl 172196212Sscottl /* 173196212Sscottl * Go ahead and read the info for all the volumes. For this 174196212Sscottl * pass we figure out how many physical drives there are. 175196212Sscottl */ 176196212Sscottl volumes = malloc(sizeof(*volumes) * ioc2->NumActiveVolumes); 177196212Sscottl count = 0; 178196212Sscottl vol = ioc2->RaidVolume; 179196212Sscottl for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 180196212Sscottl volumes[i] = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, 181196212Sscottl NULL); 182196212Sscottl if (volumes[i] == NULL) { 183196212Sscottl warn("Failed to read volume info"); 184196212Sscottl return (NULL); 185196212Sscottl } 186196212Sscottl count += volumes[i]->NumPhysDisks; 187196212Sscottl } 188196212Sscottl count += ioc3->NumPhysDisks; 189196212Sscottl count += ioc5->NumHotSpares; 190196212Sscottl 191196212Sscottl /* Walk the various lists enumerating drives. */ 192196212Sscottl list = malloc(sizeof(*list) + sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) * 193196212Sscottl count); 194196212Sscottl list->ndrives = 0; 195196212Sscottl 196196212Sscottl for (i = 0; i < ioc2->NumActiveVolumes; i++) { 197196212Sscottl rdisk = volumes[i]->PhysDisk; 198196212Sscottl for (j = 0; j < volumes[i]->NumPhysDisks; rdisk++, j++) 199196212Sscottl if (mpt_pd_insert(fd, list, rdisk->PhysDiskNum) < 0) 200196212Sscottl return (NULL); 201196212Sscottl free(volumes[i]); 202196212Sscottl } 203196212Sscottl free(ioc2); 204196212Sscottl free(volumes); 205196212Sscottl 206196212Sscottl spare = ioc5->HotSpare; 207196212Sscottl for (i = 0; i < ioc5->NumHotSpares; spare++, i++) 208196212Sscottl if (mpt_pd_insert(fd, list, spare->PhysDiskNum) < 0) 209196212Sscottl return (NULL); 210196212Sscottl free(ioc5); 211196212Sscottl 212196212Sscottl disk = ioc3->PhysDisk; 213196212Sscottl for (i = 0; i < ioc3->NumPhysDisks; disk++, i++) 214196212Sscottl if (mpt_pd_insert(fd, list, disk->PhysDiskNum) < 0) 215196212Sscottl return (NULL); 216196212Sscottl free(ioc3); 217196212Sscottl 218196212Sscottl return (list); 219196212Sscottl} 220196212Sscottl 221196212Sscottlvoid 222196212Sscottlmpt_free_pd_list(struct mpt_drive_list *list) 223196212Sscottl{ 224196212Sscottl int i; 225196212Sscottl 226196212Sscottl for (i = 0; i < list->ndrives; i++) 227196212Sscottl free(list->drives[i]); 228196212Sscottl free(list); 229196212Sscottl} 230196212Sscottl 231196212Sscottlint 232196212Sscottlmpt_lookup_drive(struct mpt_drive_list *list, const char *drive, 233196212Sscottl U8 *PhysDiskNum) 234196212Sscottl{ 235196212Sscottl long val; 236196212Sscottl uint8_t bus, id; 237196212Sscottl char *cp; 238196212Sscottl 239196212Sscottl /* Look for a raw device id first. */ 240196212Sscottl val = strtol(drive, &cp, 0); 241196212Sscottl if (*cp == '\0') { 242196212Sscottl if (val < 0 || val > 0xff) 243196212Sscottl goto bad; 244196212Sscottl *PhysDiskNum = val; 245196212Sscottl return (0); 246196212Sscottl } 247196212Sscottl 248196212Sscottl /* Look for a <bus>:<id> string. */ 249196212Sscottl if (*cp == ':') { 250196212Sscottl if (val < 0 || val > 0xff) 251196212Sscottl goto bad; 252196212Sscottl bus = val; 253196212Sscottl val = strtol(cp + 1, &cp, 0); 254196212Sscottl if (*cp != '\0') 255196212Sscottl goto bad; 256196212Sscottl if (val < 0 || val > 0xff) 257196212Sscottl goto bad; 258196212Sscottl id = val; 259196212Sscottl 260196212Sscottl for (val = 0; val < list->ndrives; val++) { 261196212Sscottl if (list->drives[val]->PhysDiskBus == bus && 262196212Sscottl list->drives[val]->PhysDiskID == id) { 263196212Sscottl *PhysDiskNum = list->drives[val]->PhysDiskNum; 264196212Sscottl return (0); 265196212Sscottl } 266196212Sscottl } 267196212Sscottl errno = ENOENT; 268196212Sscottl return (-1); 269196212Sscottl } 270196212Sscottl 271196212Sscottlbad: 272196212Sscottl errno = EINVAL; 273196212Sscottl return (-1); 274196212Sscottl} 275196212Sscottl 276196212Sscottl/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 277196212Sscottlconst char * 278196212Sscottlmpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0 *pd_info) 279196212Sscottl{ 280196212Sscottl RAID_PHYS_DISK0_INQUIRY_DATA *inq_data; 281196212Sscottl u_char vendor[9], product[17], revision[5]; 282196212Sscottl static char inq_string[64]; 283196212Sscottl 284196212Sscottl inq_data = &pd_info->InquiryData; 285196212Sscottl cam_strvis(vendor, inq_data->VendorID, sizeof(inq_data->VendorID), 286196212Sscottl sizeof(vendor)); 287196212Sscottl cam_strvis(product, inq_data->ProductID, sizeof(inq_data->ProductID), 288196212Sscottl sizeof(product)); 289196212Sscottl cam_strvis(revision, inq_data->ProductRevLevel, 290196212Sscottl sizeof(inq_data->ProductRevLevel), sizeof(revision)); 291196212Sscottl 292196212Sscottl /* Total hack. */ 293196212Sscottl if (strcmp(vendor, "ATA") == 0) 294196212Sscottl snprintf(inq_string, sizeof(inq_string), "<%s %s> SATA", 295196212Sscottl product, revision); 296196212Sscottl else 297196212Sscottl snprintf(inq_string, sizeof(inq_string), "<%s %s %s> SAS", 298196212Sscottl vendor, product, revision); 299196212Sscottl return (inq_string); 300196212Sscottl} 301196212Sscottl 302196212Sscottl/* Helper function to set a drive to a given state. */ 303196212Sscottlstatic int 304196212Sscottldrive_set_state(char *drive, U8 Action, U8 State, const char *name) 305196212Sscottl{ 306196212Sscottl CONFIG_PAGE_RAID_PHYS_DISK_0 *info; 307196212Sscottl struct mpt_drive_list *list; 308196212Sscottl U8 PhysDiskNum; 309196212Sscottl int fd; 310196212Sscottl 311196212Sscottl fd = mpt_open(mpt_unit); 312196212Sscottl if (fd < 0) { 313196212Sscottl warn("mpt_open"); 314196212Sscottl return (errno); 315196212Sscottl } 316196212Sscottl 317196212Sscottl list = mpt_pd_list(fd); 318196212Sscottl if (list == NULL) 319196212Sscottl return (errno); 320196212Sscottl 321196212Sscottl if (mpt_lookup_drive(list, drive, &PhysDiskNum) < 0) { 322196212Sscottl warn("Failed to find drive %s", drive); 323196212Sscottl return (errno); 324196212Sscottl } 325196212Sscottl mpt_free_pd_list(list); 326196212Sscottl 327196212Sscottl /* Get the info for this drive. */ 328196212Sscottl info = mpt_pd_info(fd, PhysDiskNum, NULL); 329196212Sscottl if (info == NULL) { 330196212Sscottl warn("Failed to fetch info for drive %u", PhysDiskNum); 331196212Sscottl return (errno); 332196212Sscottl } 333196212Sscottl 334196212Sscottl /* Try to change the state. */ 335196212Sscottl if (info->PhysDiskStatus.State == State) { 336196212Sscottl warnx("Drive %u is already in the desired state", PhysDiskNum); 337196212Sscottl return (EINVAL); 338196212Sscottl } 339196212Sscottl 340196212Sscottl if (mpt_raid_action(fd, Action, 0, 0, PhysDiskNum, 0, NULL, 0, NULL, 341196212Sscottl NULL, 0, NULL, NULL, 0) < 0) { 342196212Sscottl warn("Failed to set drive %u to %s", PhysDiskNum, name); 343196212Sscottl return (errno); 344196212Sscottl } 345196212Sscottl 346196212Sscottl free(info); 347196212Sscottl close(fd); 348196212Sscottl 349196212Sscottl return (0); 350196212Sscottl} 351196212Sscottl 352196212Sscottlstatic int 353196212Sscottlfail_drive(int ac, char **av) 354196212Sscottl{ 355196212Sscottl 356196212Sscottl if (ac != 2) { 357196212Sscottl warnx("fail: %s", ac > 2 ? "extra arguments" : 358196212Sscottl "drive required"); 359196212Sscottl return (EINVAL); 360196212Sscottl } 361196212Sscottl 362196212Sscottl return (drive_set_state(av[1], MPI_RAID_ACTION_FAIL_PHYSDISK, 363196212Sscottl MPI_PHYSDISK0_STATUS_FAILED_REQUESTED, "FAILED")); 364196212Sscottl} 365196212SscottlMPT_COMMAND(top, fail, fail_drive); 366196212Sscottl 367196212Sscottlstatic int 368196212Sscottlonline_drive(int ac, char **av) 369196212Sscottl{ 370196212Sscottl 371196212Sscottl if (ac != 2) { 372196212Sscottl warnx("online: %s", ac > 2 ? "extra arguments" : 373196212Sscottl "drive required"); 374196212Sscottl return (EINVAL); 375196212Sscottl } 376196212Sscottl 377196212Sscottl return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_ONLINE, 378196212Sscottl MPI_PHYSDISK0_STATUS_ONLINE, "ONLINE")); 379196212Sscottl} 380196212SscottlMPT_COMMAND(top, online, online_drive); 381196212Sscottl 382196212Sscottlstatic int 383196212Sscottloffline_drive(int ac, char **av) 384196212Sscottl{ 385196212Sscottl 386196212Sscottl if (ac != 2) { 387196212Sscottl warnx("offline: %s", ac > 2 ? "extra arguments" : 388196212Sscottl "drive required"); 389196212Sscottl return (EINVAL); 390196212Sscottl } 391196212Sscottl 392196212Sscottl return (drive_set_state(av[1], MPI_RAID_ACTION_PHYSDISK_OFFLINE, 393196212Sscottl MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED, "OFFLINE")); 394196212Sscottl} 395196212SscottlMPT_COMMAND(top, offline, offline_drive); 396