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 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * Disk status library 29 * 30 * This library is responsible for querying health and other status information 31 * from disk drives. It is intended to be a generic interface, however only 32 * SCSI (and therefore SATA) disks are currently supported. The library is 33 * capable of detecting the following status conditions: 34 * 35 * - Predictive failure 36 * - Overtemp 37 * - Self-test failure 38 * - Solid State Media wearout 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 nvlist_free(dsp->ds_ssmwearout); 135 if (dsp->ds_data) 136 dsp->ds_transport->dt_close(dsp->ds_data); 137 (void) close(dsp->ds_fd); 138 free(dsp->ds_path); 139 free(dsp); 140 } 141 142 void 143 disk_status_set_debug(boolean_t value) 144 { 145 ds_debug = value; 146 } 147 148 /* 149 * Query basic information 150 */ 151 const char * 152 disk_status_path(disk_status_t *dsp) 153 { 154 return (dsp->ds_path); 155 } 156 157 int 158 disk_status_errno(disk_status_t *dsp) 159 { 160 return (dsp->ds_error); 161 } 162 163 nvlist_t * 164 disk_status_get(disk_status_t *dsp) 165 { 166 nvlist_t *nvl = NULL; 167 nvlist_t *faults = NULL; 168 int err; 169 170 /* 171 * Scan (or rescan) the current device. 172 */ 173 nvlist_free(dsp->ds_testfail); 174 nvlist_free(dsp->ds_predfail); 175 nvlist_free(dsp->ds_overtemp); 176 nvlist_free(dsp->ds_ssmwearout); 177 dsp->ds_ssmwearout = NULL; 178 dsp->ds_testfail = dsp->ds_overtemp = dsp->ds_predfail = NULL; 179 dsp->ds_faults = 0; 180 181 /* 182 * Even if there is an I/O failure when trying to scan the device, we 183 * can still return the current state. 184 */ 185 if (dsp->ds_transport->dt_scan(dsp->ds_data) != 0 && 186 dsp->ds_error != EDS_IO) 187 return (NULL); 188 189 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) 190 goto nverror; 191 192 if ((err = nvlist_add_string(nvl, "protocol", "scsi")) != 0 || 193 (err = nvlist_add_nvlist(nvl, "status", dsp->ds_state)) != 0) 194 goto nverror; 195 196 /* 197 * Construct the list of faults. 198 */ 199 if ((err = nvlist_alloc(&faults, NV_UNIQUE_NAME, 0)) != 0) 200 goto nverror; 201 202 if (dsp->ds_predfail != NULL) { 203 if ((err = nvlist_add_boolean_value(faults, 204 FM_EREPORT_SCSI_PREDFAIL, 205 (dsp->ds_faults & DS_FAULT_PREDFAIL) != 0)) != 0 || 206 (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_PREDFAIL, 207 dsp->ds_predfail)) != 0) 208 goto nverror; 209 } 210 211 if (dsp->ds_testfail != NULL) { 212 if ((err = nvlist_add_boolean_value(faults, 213 FM_EREPORT_SCSI_TESTFAIL, 214 (dsp->ds_faults & DS_FAULT_TESTFAIL) != 0)) != 0 || 215 (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_TESTFAIL, 216 dsp->ds_testfail)) != 0) 217 goto nverror; 218 } 219 220 if (dsp->ds_overtemp != NULL) { 221 if ((err = nvlist_add_boolean_value(faults, 222 FM_EREPORT_SCSI_OVERTEMP, 223 (dsp->ds_faults & DS_FAULT_OVERTEMP) != 0)) != 0 || 224 (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_OVERTEMP, 225 dsp->ds_overtemp)) != 0) 226 goto nverror; 227 } 228 229 if (dsp->ds_ssmwearout != NULL) { 230 if ((err = nvlist_add_boolean_value(faults, 231 FM_EREPORT_SCSI_SSMWEAROUT, 232 (dsp->ds_faults & DS_FAULT_SSMWEAROUT) != 0)) != 0 || 233 (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_SSMWEAROUT, 234 dsp->ds_ssmwearout)) != 0) 235 goto nverror; 236 } 237 238 if ((err = nvlist_add_nvlist(nvl, "faults", faults)) != 0) 239 goto nverror; 240 241 nvlist_free(faults); 242 return (nvl); 243 244 nverror: 245 assert(err == ENOMEM); 246 nvlist_free(nvl); 247 nvlist_free(faults); 248 (void) ds_set_errno(dsp, EDS_NOMEM); 249 return (NULL); 250 } 251