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