1*4198Seschrock /* 2*4198Seschrock * CDDL HEADER START 3*4198Seschrock * 4*4198Seschrock * The contents of this file are subject to the terms of the 5*4198Seschrock * Common Development and Distribution License (the "License"). 6*4198Seschrock * You may not use this file except in compliance with the License. 7*4198Seschrock * 8*4198Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*4198Seschrock * or http://www.opensolaris.org/os/licensing. 10*4198Seschrock * See the License for the specific language governing permissions 11*4198Seschrock * and limitations under the License. 12*4198Seschrock * 13*4198Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14*4198Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*4198Seschrock * If applicable, add the following below this CDDL HEADER, with the 16*4198Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17*4198Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18*4198Seschrock * 19*4198Seschrock * CDDL HEADER END 20*4198Seschrock */ 21*4198Seschrock /* 22*4198Seschrock * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*4198Seschrock * Use is subject to license terms. 24*4198Seschrock */ 25*4198Seschrock 26*4198Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 27*4198Seschrock 28*4198Seschrock /* 29*4198Seschrock * Disk status library 30*4198Seschrock * 31*4198Seschrock * This library is responsible for querying health and other status information 32*4198Seschrock * from disk drives. It is intended to be a generic interface, however only 33*4198Seschrock * SCSI (and therefore SATA) disks are currently supported. The library is 34*4198Seschrock * capable of detecting the following status conditions: 35*4198Seschrock * 36*4198Seschrock * - Predictive failure 37*4198Seschrock * - Overtemp 38*4198Seschrock * - Self-test failure 39*4198Seschrock */ 40*4198Seschrock 41*4198Seschrock #include <assert.h> 42*4198Seschrock #include <errno.h> 43*4198Seschrock #include <fcntl.h> 44*4198Seschrock #include <libdevinfo.h> 45*4198Seschrock #include <libdiskstatus.h> 46*4198Seschrock #include <stdlib.h> 47*4198Seschrock #include <string.h> 48*4198Seschrock #include <sys/fm/io/scsi.h> 49*4198Seschrock #include <sys/stat.h> 50*4198Seschrock #include <unistd.h> 51*4198Seschrock 52*4198Seschrock #include "ds_impl.h" 53*4198Seschrock #include "ds_scsi.h" 54*4198Seschrock 55*4198Seschrock static ds_transport_t *ds_transports[] = { 56*4198Seschrock &ds_scsi_sim_transport, 57*4198Seschrock &ds_scsi_uscsi_transport 58*4198Seschrock }; 59*4198Seschrock 60*4198Seschrock #define NTRANSPORTS (sizeof (ds_transports) / sizeof (ds_transports[0])) 61*4198Seschrock 62*4198Seschrock /* 63*4198Seschrock * Open a handle to a disk. This will fail if the device cannot be opened, or 64*4198Seschrock * if no suitable transport exists for communicating with the device. 65*4198Seschrock */ 66*4198Seschrock disk_status_t * 67*4198Seschrock disk_status_open(const char *path, int *error) 68*4198Seschrock { 69*4198Seschrock disk_status_t *dsp; 70*4198Seschrock ds_transport_t *t; 71*4198Seschrock int i; 72*4198Seschrock 73*4198Seschrock if ((dsp = calloc(sizeof (disk_status_t), 1)) == NULL) { 74*4198Seschrock *error = EDS_NOMEM; 75*4198Seschrock return (NULL); 76*4198Seschrock } 77*4198Seschrock 78*4198Seschrock if ((dsp->ds_fd = open(path, O_RDWR)) < 0) { 79*4198Seschrock *error = EDS_CANT_OPEN; 80*4198Seschrock free(dsp); 81*4198Seschrock return (NULL); 82*4198Seschrock } 83*4198Seschrock 84*4198Seschrock if ((dsp->ds_path = strdup(path)) == NULL) { 85*4198Seschrock *error = EDS_NOMEM; 86*4198Seschrock disk_status_close(dsp); 87*4198Seschrock return (NULL); 88*4198Seschrock } 89*4198Seschrock 90*4198Seschrock for (i = 0; i < NTRANSPORTS; i++) { 91*4198Seschrock t = ds_transports[i]; 92*4198Seschrock 93*4198Seschrock dsp->ds_transport = t; 94*4198Seschrock 95*4198Seschrock nvlist_free(dsp->ds_state); 96*4198Seschrock dsp->ds_state = NULL; 97*4198Seschrock if (nvlist_alloc(&dsp->ds_state, NV_UNIQUE_NAME, 0) != 0) { 98*4198Seschrock *error = EDS_NOMEM; 99*4198Seschrock disk_status_close(dsp); 100*4198Seschrock return (NULL); 101*4198Seschrock } 102*4198Seschrock 103*4198Seschrock if ((dsp->ds_data = t->dt_open(dsp)) == NULL) { 104*4198Seschrock if (dsp->ds_error != EDS_NO_TRANSPORT) { 105*4198Seschrock *error = dsp->ds_error; 106*4198Seschrock disk_status_close(dsp); 107*4198Seschrock return (NULL); 108*4198Seschrock } 109*4198Seschrock } else { 110*4198Seschrock dsp->ds_error = 0; 111*4198Seschrock break; 112*4198Seschrock } 113*4198Seschrock } 114*4198Seschrock 115*4198Seschrock if (dsp->ds_error == EDS_NO_TRANSPORT) { 116*4198Seschrock *error = dsp->ds_error; 117*4198Seschrock disk_status_close(dsp); 118*4198Seschrock return (NULL); 119*4198Seschrock } 120*4198Seschrock 121*4198Seschrock return (dsp); 122*4198Seschrock } 123*4198Seschrock 124*4198Seschrock /* 125*4198Seschrock * Close a handle to a disk. 126*4198Seschrock */ 127*4198Seschrock void 128*4198Seschrock disk_status_close(disk_status_t *dsp) 129*4198Seschrock { 130*4198Seschrock nvlist_free(dsp->ds_state); 131*4198Seschrock nvlist_free(dsp->ds_predfail); 132*4198Seschrock nvlist_free(dsp->ds_overtemp); 133*4198Seschrock nvlist_free(dsp->ds_testfail); 134*4198Seschrock if (dsp->ds_data) 135*4198Seschrock dsp->ds_transport->dt_close(dsp->ds_data); 136*4198Seschrock (void) close(dsp->ds_fd); 137*4198Seschrock free(dsp->ds_path); 138*4198Seschrock free(dsp); 139*4198Seschrock } 140*4198Seschrock 141*4198Seschrock void 142*4198Seschrock disk_status_set_debug(boolean_t value) 143*4198Seschrock { 144*4198Seschrock ds_debug = value; 145*4198Seschrock } 146*4198Seschrock 147*4198Seschrock /* 148*4198Seschrock * Query basic information 149*4198Seschrock */ 150*4198Seschrock const char * 151*4198Seschrock disk_status_path(disk_status_t *dsp) 152*4198Seschrock { 153*4198Seschrock return (dsp->ds_path); 154*4198Seschrock } 155*4198Seschrock 156*4198Seschrock int 157*4198Seschrock disk_status_errno(disk_status_t *dsp) 158*4198Seschrock { 159*4198Seschrock return (dsp->ds_error); 160*4198Seschrock } 161*4198Seschrock 162*4198Seschrock nvlist_t * 163*4198Seschrock disk_status_get(disk_status_t *dsp) 164*4198Seschrock { 165*4198Seschrock nvlist_t *nvl = NULL; 166*4198Seschrock nvlist_t *faults = NULL; 167*4198Seschrock int err; 168*4198Seschrock 169*4198Seschrock /* 170*4198Seschrock * Scan (or rescan) the current device. 171*4198Seschrock */ 172*4198Seschrock nvlist_free(dsp->ds_testfail); 173*4198Seschrock nvlist_free(dsp->ds_predfail); 174*4198Seschrock nvlist_free(dsp->ds_overtemp); 175*4198Seschrock dsp->ds_testfail = dsp->ds_overtemp = dsp->ds_predfail = NULL; 176*4198Seschrock dsp->ds_faults = 0; 177*4198Seschrock 178*4198Seschrock /* 179*4198Seschrock * Even if there is an I/O failure when trying to scan the device, we 180*4198Seschrock * can still return the current state. 181*4198Seschrock */ 182*4198Seschrock if (dsp->ds_transport->dt_scan(dsp->ds_data) != 0 && 183*4198Seschrock dsp->ds_error != EDS_IO) 184*4198Seschrock return (NULL); 185*4198Seschrock 186*4198Seschrock if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) 187*4198Seschrock goto nverror; 188*4198Seschrock 189*4198Seschrock if ((err = nvlist_add_string(nvl, "protocol", "scsi")) != 0 || 190*4198Seschrock (err = nvlist_add_nvlist(nvl, "status", dsp->ds_state)) != 0) 191*4198Seschrock goto nverror; 192*4198Seschrock 193*4198Seschrock /* 194*4198Seschrock * Construct the list of faults. 195*4198Seschrock */ 196*4198Seschrock if ((err = nvlist_alloc(&faults, NV_UNIQUE_NAME, 0)) != 0) 197*4198Seschrock goto nverror; 198*4198Seschrock 199*4198Seschrock if (dsp->ds_predfail != NULL) { 200*4198Seschrock if ((err = nvlist_add_boolean_value(faults, 201*4198Seschrock FM_EREPORT_SCSI_PREDFAIL, 202*4198Seschrock (dsp->ds_faults & DS_FAULT_PREDFAIL) != 0)) != 0 || 203*4198Seschrock (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_PREDFAIL, 204*4198Seschrock dsp->ds_predfail)) != 0) 205*4198Seschrock goto nverror; 206*4198Seschrock } 207*4198Seschrock 208*4198Seschrock if (dsp->ds_testfail != NULL) { 209*4198Seschrock if ((err = nvlist_add_boolean_value(faults, 210*4198Seschrock FM_EREPORT_SCSI_TESTFAIL, 211*4198Seschrock (dsp->ds_faults & DS_FAULT_TESTFAIL) != 0)) != 0 || 212*4198Seschrock (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_TESTFAIL, 213*4198Seschrock dsp->ds_testfail)) != 0) 214*4198Seschrock goto nverror; 215*4198Seschrock } 216*4198Seschrock 217*4198Seschrock if (dsp->ds_overtemp != NULL) { 218*4198Seschrock if ((err = nvlist_add_boolean_value(faults, 219*4198Seschrock FM_EREPORT_SCSI_OVERTEMP, 220*4198Seschrock (dsp->ds_faults & DS_FAULT_OVERTEMP) != 0)) != 0 || 221*4198Seschrock (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_OVERTEMP, 222*4198Seschrock dsp->ds_overtemp)) != 0) 223*4198Seschrock goto nverror; 224*4198Seschrock } 225*4198Seschrock 226*4198Seschrock if ((err = nvlist_add_nvlist(nvl, "faults", faults)) != 0) 227*4198Seschrock goto nverror; 228*4198Seschrock 229*4198Seschrock nvlist_free(faults); 230*4198Seschrock return (nvl); 231*4198Seschrock 232*4198Seschrock nverror: 233*4198Seschrock assert(err == ENOMEM); 234*4198Seschrock nvlist_free(nvl); 235*4198Seschrock nvlist_free(faults); 236*4198Seschrock (void) ds_set_errno(dsp, EDS_NOMEM); 237*4198Seschrock return (NULL); 238*4198Seschrock } 239