xref: /illumos-gate/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c (revision 0244979b1714a04f23ac9fa8367e59f6fb75d8f3)
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 *
disk_status_open(const char * path,int * error)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
disk_status_close(disk_status_t * dsp)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
disk_status_set_debug(boolean_t value)143  disk_status_set_debug(boolean_t value)
144  {
145  	ds_debug = value;
146  }
147  
148  /*
149   * Query basic information
150   */
151  const char *
disk_status_path(disk_status_t * dsp)152  disk_status_path(disk_status_t *dsp)
153  {
154  	return (dsp->ds_path);
155  }
156  
157  int
disk_status_errno(disk_status_t * dsp)158  disk_status_errno(disk_status_t *dsp)
159  {
160  	return (dsp->ds_error);
161  }
162  
163  nvlist_t *
disk_status_get(disk_status_t * dsp)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