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 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2018, Joyent, Inc.
25  */
26 
27 /*
28  * Functions in this file are shared between the disk and ses enumerators.
29  *
30  * A topo_list_t of all disks is returned by a successful disk_list_gather()
31  * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
32  * node below a specific 'bay' parent node either disk_declare_path() or
33  * disk_declare_addr() are called. The caller determines which 'disk' is
34  * in which 'bay'. A disk's 'label' and 'authority' information come from
35  * its parent 'bay' node.
36  */
37 
38 #include <ctype.h>
39 #include <strings.h>
40 #include <libdevinfo.h>
41 #include <libdiskmgt.h>
42 #include <devid.h>
43 #include <sys/libdevid.h>
44 #include <pthread.h>
45 #include <inttypes.h>
46 #include <sys/dkio.h>
47 #include <sys/scsi/scsi_types.h>
48 #include <fm/topo_mod.h>
49 #include <fm/topo_list.h>
50 #include <fm/libdiskstatus.h>
51 #include <sys/fm/protocol.h>
52 #include <sys/scsi/generic/inquiry.h>
53 #include "disk.h"
54 
55 /* common callback information for di_walk_node() and di_devlink_walk */
56 typedef struct disk_cbdata {
57 	topo_mod_t		*dcb_mod;
58 	topo_list_t		*dcb_list;
59 
60 	di_devlink_handle_t	dcb_devhdl;
61 	dev_di_node_t		*dcb_dnode;	/* for di_devlink_walk only */
62 } disk_cbdata_t;
63 
64 /*
65  * Given a /devices path for a whole disk, appending this extension gives the
66  * path to a raw device that can be opened.
67  */
68 #if defined(__i386) || defined(__amd64)
69 #define	PHYS_EXTN	":q,raw"
70 #elif defined(__sparc) || defined(__sparcv9)
71 #define	PHYS_EXTN	":c,raw"
72 #else
73 #error	Unknown architecture
74 #endif
75 
76 /*
77  * Methods for disks. This is used by the disk-transport module to
78  * generate ereports based off SCSI disk status.
79  */
80 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
81 	nvlist_t *, nvlist_t **);
82 
83 static const topo_method_t disk_methods[] = {
84 	{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
85 	    TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
86 	    disk_status },
87 	{ NULL }
88 };
89 
90 static int disk_temp_reading(topo_mod_t *, tnode_t *, topo_version_t,
91     nvlist_t *, nvlist_t **);
92 
93 #define	TOPO_METH_DISK_TEMP		"disk_temp_reading"
94 #define	TOPO_METH_DISK_TEMP_DESC	"Disk Temperature Reading"
95 #define	TOPO_METH_DISK_TEMP_VERSION	0
96 
97 static const topo_method_t disk_fac_methods[] = {
98 	{ TOPO_METH_DISK_TEMP, TOPO_METH_DISK_TEMP_DESC,
99 	    TOPO_METH_DISK_TEMP_VERSION, TOPO_STABILITY_INTERNAL,
100 	    disk_temp_reading },
101 	{ NULL }
102 };
103 
104 static const topo_pgroup_info_t io_pgroup = {
105 	TOPO_PGROUP_IO,
106 	TOPO_STABILITY_PRIVATE,
107 	TOPO_STABILITY_PRIVATE,
108 	1
109 };
110 
111 static const topo_pgroup_info_t disk_auth_pgroup = {
112 	FM_FMRI_AUTHORITY,
113 	TOPO_STABILITY_PRIVATE,
114 	TOPO_STABILITY_PRIVATE,
115 	1
116 };
117 
118 static const topo_pgroup_info_t storage_pgroup = {
119 	TOPO_PGROUP_STORAGE,
120 	TOPO_STABILITY_PRIVATE,
121 	TOPO_STABILITY_PRIVATE,
122 	1
123 };
124 
125 /*
126  * Set the properties of the disk node, from dev_di_node_t data.
127  * Properties include:
128  *	group: protocol	 properties: resource, asru, label, fru
129  *	group: authority properties: product-id, chasis-id, server-id
130  *	group: io	 properties: devfs-path, devid
131  *	group: storage	 properties:
132  *		- logical-disk, disk-model, disk-manufacturer, serial-number
133  *		- firmware-revision, capacity-in-bytes
134  *
135  * NOTE: the io and storage groups won't be present if the dnode passed in is
136  * NULL. This happens when a disk is found through ses, but is not enumerated
137  * in the devinfo tree.
138  */
139 static int
140 disk_set_props(topo_mod_t *mod, tnode_t *parent,
141     tnode_t *dtn, dev_di_node_t *dnode)
142 {
143 	nvlist_t	*asru = NULL, *drive_attrs;
144 	char		*label = NULL;
145 	nvlist_t	*fmri = NULL;
146 	dm_descriptor_t drive_descr = NULL;
147 	uint32_t	rpm;
148 	int		err;
149 
150 	/* pull the label property down from our parent 'bay' node */
151 	if (topo_node_label(parent, &label, &err) != 0) {
152 		if (err != ETOPO_PROP_NOENT) {
153 			topo_mod_dprintf(mod, "disk_set_props: "
154 			    "label error %s\n", topo_strerror(err));
155 			goto error;
156 		}
157 	} else if (topo_node_label_set(dtn, label, &err) != 0) {
158 		topo_mod_dprintf(mod, "disk_set_props: "
159 		    "label_set error %s\n", topo_strerror(err));
160 		goto error;
161 	}
162 
163 	/* get the resource fmri, and use it as the fru */
164 	if (topo_node_resource(dtn, &fmri, &err) != 0) {
165 		topo_mod_dprintf(mod, "disk_set_props: "
166 		    "resource error: %s\n", topo_strerror(err));
167 		goto error;
168 	}
169 	if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
170 		topo_mod_dprintf(mod, "disk_set_props: "
171 		    "fru_set error: %s\n", topo_strerror(err));
172 		goto error;
173 	}
174 
175 	/* create/set the authority group */
176 	if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
177 	    (err != ETOPO_PROP_DEFD)) {
178 		topo_mod_dprintf(mod, "disk_set_props: "
179 		    "create disk_auth error %s\n", topo_strerror(err));
180 		goto error;
181 	}
182 
183 	/* create the storage group */
184 	if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
185 		topo_mod_dprintf(mod, "disk_set_props: "
186 		    "create storage error %s\n", topo_strerror(err));
187 		goto error;
188 	}
189 
190 	/* no dnode was found for this disk - skip the io and storage groups */
191 	if (dnode == NULL) {
192 		err = 0;
193 		goto out;
194 	}
195 
196 	/* form and set the asru */
197 	if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
198 	    dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
199 		err = ETOPO_FMRI_UNKNOWN;
200 		topo_mod_dprintf(mod, "disk_set_props: "
201 		    "asru error %s\n", topo_strerror(err));
202 		goto error;
203 	}
204 	if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
205 		topo_mod_dprintf(mod, "disk_set_props: "
206 		    "asru_set error %s\n", topo_strerror(err));
207 		goto error;
208 	}
209 
210 	/* create/set the devfs-path and devid in the io group */
211 	if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
212 		topo_mod_dprintf(mod, "disk_set_props: "
213 		    "create io error %s\n", topo_strerror(err));
214 		goto error;
215 	}
216 
217 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
218 	    TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
219 		topo_mod_dprintf(mod, "disk_set_props: "
220 		    "set dev error %s\n", topo_strerror(err));
221 		goto error;
222 	}
223 
224 	if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
225 	    TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
226 		topo_mod_dprintf(mod, "disk_set_props: "
227 		    "set devid error %s\n", topo_strerror(err));
228 		goto error;
229 	}
230 
231 	if (dnode->ddn_ppath_count != 0 &&
232 	    topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
233 	    TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
234 	    dnode->ddn_ppath_count, &err) != 0) {
235 		topo_mod_dprintf(mod, "disk_set_props: "
236 		    "set phys-path error %s\n", topo_strerror(err));
237 		goto error;
238 	}
239 
240 	/* set the storage group public /dev name */
241 	if (dnode->ddn_lpath != NULL &&
242 	    topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
243 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
244 	    dnode->ddn_lpath, &err) != 0) {
245 		topo_mod_dprintf(mod, "disk_set_props: "
246 		    "set disk_name error %s\n", topo_strerror(err));
247 		goto error;
248 	}
249 
250 	/* populate other misc storage group properties */
251 	if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
252 	    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
253 	    dnode->ddn_mfg, &err) != 0)) {
254 		topo_mod_dprintf(mod, "disk_set_props: "
255 		    "set mfg error %s\n", topo_strerror(err));
256 		goto error;
257 	}
258 	if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
259 	    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
260 	    dnode->ddn_model, &err) != 0)) {
261 		topo_mod_dprintf(mod, "disk_set_props: "
262 		    "set model error %s\n", topo_strerror(err));
263 		goto error;
264 	}
265 	if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
266 	    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
267 	    dnode->ddn_serial, &err) != 0)) {
268 		topo_mod_dprintf(mod, "disk_set_props: "
269 		    "set serial error %s\n", topo_strerror(err));
270 		goto error;
271 	}
272 	if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
273 	    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
274 	    dnode->ddn_firm, &err) != 0)) {
275 		topo_mod_dprintf(mod, "disk_set_props: "
276 		    "set firm error %s\n", topo_strerror(err));
277 		goto error;
278 	}
279 	if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
280 	    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
281 	    dnode->ddn_cap, &err) != 0)) {
282 		topo_mod_dprintf(mod, "disk_set_props: "
283 		    "set cap error %s\n", topo_strerror(err));
284 		goto error;
285 	}
286 
287 	if (dnode->ddn_devid == NULL ||
288 	    (drive_descr = dm_get_descriptor_by_name(DM_DRIVE,
289 	    dnode->ddn_devid, &err)) == NULL ||
290 	    (drive_attrs = dm_get_attributes(drive_descr, &err)) == NULL)
291 		goto out;
292 
293 	if (nvlist_lookup_boolean(drive_attrs, DM_SOLIDSTATE) == 0 ||
294 	    nvlist_lookup_uint32(drive_attrs, DM_RPM, &rpm) != 0)
295 		goto out;
296 
297 	if (topo_prop_set_uint32(dtn, TOPO_PGROUP_STORAGE, TOPO_STORAGE_RPM,
298 	    TOPO_PROP_IMMUTABLE, rpm, &err) != 0) {
299 		topo_mod_dprintf(mod, "disk_set_props: "
300 		    "set rpm error %s\n", topo_strerror(err));
301 		dm_free_descriptor(drive_descr);
302 		goto error;
303 	}
304 	err = 0;
305 
306 out:
307 	if (drive_descr != NULL)
308 		dm_free_descriptor(drive_descr);
309 	nvlist_free(fmri);
310 	if (label)
311 		topo_mod_strfree(mod, label);
312 	nvlist_free(asru);
313 	return (err);
314 
315 error:	err = topo_mod_seterrno(mod, err);
316 	goto out;
317 }
318 
319 /*
320  * Trim leading and trailing whitespace from the string.
321  */
322 static char *
323 disk_trim_whitespace(topo_mod_t *mod, const char *begin)
324 {
325 	const char *end;
326 	char *buf;
327 	size_t count;
328 
329 	if (begin == NULL)
330 		return (NULL);
331 
332 	end = begin + strlen(begin);
333 
334 	while (begin < end && isspace(*begin))
335 		begin++;
336 	while (begin < end && isspace(*(end - 1)))
337 		end--;
338 
339 	count = end - begin;
340 	if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
341 		return (NULL);
342 
343 	(void) strlcpy(buf, begin, count + 1);
344 
345 	return (buf);
346 }
347 
348 /*ARGSUSED*/
349 static int
350 disk_temp_reading(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
351     nvlist_t *in, nvlist_t **out)
352 {
353 	char *devid;
354 	uint32_t temp;
355 	dm_descriptor_t drive_descr = NULL;
356 	nvlist_t *drive_stats, *pargs, *nvl;
357 	int err;
358 
359 	if (vers > TOPO_METH_DISK_TEMP_VERSION)
360 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
361 
362 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &pargs) != 0 ||
363 	    nvlist_lookup_string(pargs, TOPO_IO_DEVID, &devid) != 0) {
364 		topo_mod_dprintf(mod, "Failed to lookup %s arg",
365 		    TOPO_IO_DEVID);
366 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
367 	}
368 
369 	if ((drive_descr = dm_get_descriptor_by_name(DM_DRIVE, devid,
370 	    &err)) == NULL) {
371 		topo_mod_dprintf(mod, "failed to get drive decriptor for %s",
372 		    devid);
373 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
374 	}
375 
376 	if ((drive_stats = dm_get_stats(drive_descr, DM_DRV_STAT_TEMPERATURE,
377 	    &err)) == NULL ||
378 	    nvlist_lookup_uint32(drive_stats, DM_TEMPERATURE, &temp) != 0) {
379 		topo_mod_dprintf(mod, "failed to read disk temp for %s",
380 		    devid);
381 		dm_free_descriptor(drive_descr);
382 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
383 	}
384 	dm_free_descriptor(drive_descr);
385 
386 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
387 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
388 	    TOPO_SENSOR_READING) != 0 ||
389 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) !=
390 	    0 || nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, (double)temp) != 0) {
391 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
392 		nvlist_free(nvl);
393 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
394 	}
395 	*out = nvl;
396 
397 	return (0);
398 }
399 
400 static int
401 disk_add_temp_sensor(topo_mod_t *mod, tnode_t *pnode, const char *devid)
402 {
403 	tnode_t *fnode;
404 	topo_pgroup_info_t pgi;
405 	nvlist_t *arg_nvl = NULL;
406 	int err;
407 
408 	if ((fnode = topo_node_facbind(mod, pnode, "temp",
409 	    TOPO_FAC_TYPE_SENSOR)) == NULL) {
410 		topo_mod_dprintf(mod, "failed to bind facility node");
411 		/* errno set */
412 		return (-1);
413 	}
414 
415 	/*
416 	 * Set props:
417 	 * - facility/sensor-class
418 	 * - facility/sensor-type
419 	 * - facility/units
420 	 */
421 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
422 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
423 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
424 	pgi.tpi_version = 1;
425 	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
426 		if (err != ETOPO_PROP_DEFD) {
427 			topo_mod_dprintf(mod,  "pgroups create failure (%s)\n",
428 			    topo_strerror(err));
429 			/* errno set */
430 			goto err;
431 		}
432 	}
433 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY,
434 	    TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
435 	    TOPO_SENSOR_CLASS_THRESHOLD, &err) != 0 ||
436 	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
437 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, TOPO_SENSOR_TYPE_TEMP,
438 	    &err) != 0 ||
439 	    topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
440 	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE,
441 	    TOPO_SENSOR_UNITS_DEGREES_C, &err) != 0) {
442 		topo_mod_dprintf(mod, "Failed to set props on facnode (%s)",
443 		    topo_strerror(err));
444 		/* errno set */
445 		goto err;
446 	}
447 
448 	/*
449 	 * Register a property method for facility/reading
450 	 */
451 	if (topo_method_register(mod, fnode, disk_fac_methods) < 0) {
452 		topo_mod_dprintf(mod, "failed to register facility methods");
453 		goto err;
454 	}
455 	if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0 ||
456 	    nvlist_add_string(arg_nvl, TOPO_IO_DEVID, devid) != 0) {
457 		topo_mod_dprintf(mod, "Failed build arg nvlist\n");
458 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
459 		goto err;
460 	}
461 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
462 	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "disk_temp_reading",
463 	    arg_nvl, &err) != 0) {
464 		topo_mod_dprintf(mod, "Failed to register %s propmeth "
465 		    "on fac node %s (%s)\n", TOPO_SENSOR_READING,
466 		    topo_node_name(fnode), topo_strerror(err));
467 		/* errno set */
468 		goto err;
469 	}
470 	nvlist_free(arg_nvl);
471 	return (0);
472 err:
473 	topo_node_unbind(fnode);
474 	nvlist_free(arg_nvl);
475 	return (-1);
476 }
477 
478 /* create the disk topo node */
479 static int
480 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
481     dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
482 {
483 	int		len;
484 	nvlist_t	*fmri;
485 	tnode_t		*dtn;
486 	char		*part = NULL;
487 	nvlist_t	*auth;
488 	char		*mfg, *model, *firm, *serial;
489 
490 	*rval = NULL;
491 	if (dnode != NULL) {
492 		mfg = topo_mod_clean_str(mod, dnode->ddn_mfg);
493 		model = topo_mod_clean_str(mod, dnode->ddn_model);
494 		firm = topo_mod_clean_str(mod, dnode->ddn_firm);
495 		serial = topo_mod_clean_str(mod, dnode->ddn_serial);
496 	} else {
497 		mfg = model = firm = serial = NULL;
498 	}
499 
500 	/* form 'part=' of fmri as "<mfg>-<model>" */
501 	if (mfg != NULL && model != NULL) {
502 		len = strlen(mfg) + 1 + strlen(model) + 1;
503 		if ((part = topo_mod_alloc(mod, len)) != NULL)
504 			(void) snprintf(part, len, "%s-%s",
505 			    mfg, model);
506 	}
507 
508 	auth = topo_mod_auth(mod, parent);
509 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
510 	    auth, part ? part : model, firm, serial);
511 	nvlist_free(auth);
512 
513 	topo_mod_strfree(mod, part);
514 	topo_mod_strfree(mod, mfg);
515 	topo_mod_strfree(mod, model);
516 	topo_mod_strfree(mod, firm);
517 	topo_mod_strfree(mod, serial);
518 
519 	if (fmri == NULL) {
520 		topo_mod_dprintf(mod, "disk_tnode_create: "
521 		    "hcfmri (%s%d/%s%d) error %s\n",
522 		    topo_node_name(parent), topo_node_instance(parent),
523 		    name, i, topo_strerror(topo_mod_errno(mod)));
524 		return (-1);
525 	}
526 
527 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
528 		if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
529 			/*
530 			 * if disk 0 is already there then we're done
531 			 */
532 			nvlist_free(fmri);
533 			return (0);
534 		}
535 		topo_mod_dprintf(mod, "disk_tnode_create: "
536 		    "bind (%s%d/%s%d) error %s\n",
537 		    topo_node_name(parent), topo_node_instance(parent),
538 		    name, i, topo_strerror(topo_mod_errno(mod)));
539 		nvlist_free(fmri);
540 		return (-1);
541 	}
542 	nvlist_free(fmri);
543 
544 	/* add the properties of the disk */
545 	if (disk_set_props(mod, parent, dtn, dnode) != 0) {
546 		topo_mod_dprintf(mod, "disk_tnode_create: "
547 		    "disk_set_props (%s%d/%s%d) error %s\n",
548 		    topo_node_name(parent), topo_node_instance(parent),
549 		    name, i, topo_strerror(topo_mod_errno(mod)));
550 		topo_node_unbind(dtn);
551 		return (-1);
552 	}
553 
554 	if (dnode != NULL && dnode->ddn_devid != NULL &&
555 	    disk_add_temp_sensor(mod, dtn, dnode->ddn_devid) != 0) {
556 		topo_mod_dprintf(mod, "disk_tnode_create: failed to create "
557 		    "temperature sensor node on bay=%d/disk=0",
558 		    topo_node_instance(parent));
559 	}
560 	*rval = dtn;
561 	return (0);
562 }
563 
564 static int
565 disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
566     tnode_t **childp)
567 {
568 	tnode_t		*dtn = NULL;
569 	int		rval;
570 
571 	rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
572 	if (dtn == NULL) {
573 		if (rval == 0)
574 			return (0);
575 		topo_mod_dprintf(mod, "disk_declare: "
576 		    "disk_tnode_create error %s\n",
577 		    topo_strerror(topo_mod_errno(mod)));
578 		return (-1);
579 	}
580 
581 	/* register disk_methods against the disk topo node */
582 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
583 		topo_mod_dprintf(mod, "disk_declare: "
584 		    "topo_method_register error %s\n",
585 		    topo_strerror(topo_mod_errno(mod)));
586 		topo_node_unbind(dtn);
587 		return (-1);
588 	}
589 	if (childp != NULL)
590 		*childp = dtn;
591 	return (0);
592 }
593 
594 int
595 disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
596     const char *path)
597 {
598 	dev_di_node_t		*dnode;
599 	int i;
600 
601 	/*
602 	 * Check for match using physical phci (ddn_ppath). Use
603 	 * di_devfs_path_match so generic.vs.non-generic names match.
604 	 */
605 	for (dnode = topo_list_next(listp); dnode != NULL;
606 	    dnode = topo_list_next(dnode)) {
607 		if (dnode->ddn_ppath == NULL)
608 			continue;
609 
610 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
611 			if (di_devfs_path_match(dnode->ddn_ppath[0], path))
612 				return (disk_declare(mod, parent, dnode, NULL));
613 		}
614 	}
615 
616 	topo_mod_dprintf(mod, "disk_declare_path: "
617 	    "failed to find disk matching path %s", path);
618 	return (0);
619 }
620 
621 int
622 disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
623     const char *addr, tnode_t **childp)
624 {
625 	dev_di_node_t *dnode;
626 	int i;
627 
628 	/* Check for match using addr. */
629 	for (dnode = topo_list_next(listp); dnode != NULL;
630 	    dnode = topo_list_next(dnode)) {
631 		if (dnode->ddn_target_port == NULL)
632 			continue;
633 
634 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
635 			if ((dnode->ddn_target_port[i] != NULL) &&
636 			    (strncmp(dnode->ddn_target_port[i], addr,
637 			    strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
638 				topo_mod_dprintf(mod, "disk_declare_addr: "
639 				    "found disk matching addr %s", addr);
640 				return (disk_declare(mod, parent, dnode,
641 				    childp));
642 			}
643 		}
644 	}
645 
646 	topo_mod_dprintf(mod, "disk_declare_addr: "
647 	    "failed to find disk matching addr %s", addr);
648 
649 	return (1);
650 }
651 
652 /*
653  * Try to find a disk based on the bridge-port property. This is most often used
654  * for SATA devices which are attached to a SAS controller and are therefore
655  * behind a SATL bridge port. SES only knows of devices based on this SAS WWN,
656  * not based on any SATA GUIDs.
657  */
658 int
659 disk_declare_bridge(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
660     const char *addr, tnode_t **childp)
661 {
662 	dev_di_node_t *dnode;
663 	int i;
664 
665 	/* Check for match using addr. */
666 	for (dnode = topo_list_next(listp); dnode != NULL;
667 	    dnode = topo_list_next(dnode)) {
668 		if (dnode->ddn_bridge_port == NULL)
669 			continue;
670 
671 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
672 			if ((dnode->ddn_bridge_port[i] != NULL) &&
673 			    (strncmp(dnode->ddn_bridge_port[i], addr,
674 			    strcspn(dnode->ddn_bridge_port[i], ":"))) == 0) {
675 				topo_mod_dprintf(mod, "disk_declare_bridge: "
676 				    "found disk matching bridge %s", addr);
677 				return (disk_declare(mod, parent, dnode,
678 				    childp));
679 			}
680 		}
681 	}
682 
683 	topo_mod_dprintf(mod, "disk_declare_bridge: "
684 	    "failed to find disk matching bridge %s", addr);
685 
686 	return (1);
687 }
688 
689 /*
690  * Used to declare a disk that has been discovered through other means (usually
691  * ses), that is not enumerated in the devinfo tree.
692  */
693 int
694 disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
695 {
696 	return (disk_declare(mod, parent, NULL, childp));
697 }
698 
699 /* di_devlink callback for dev_di_node_add */
700 static int
701 disk_devlink_callback(di_devlink_t dl, void *arg)
702 {
703 	disk_cbdata_t	*cbp = (disk_cbdata_t *)arg;
704 	topo_mod_t	*mod = cbp->dcb_mod;
705 	dev_di_node_t	*dnode = cbp->dcb_dnode;
706 	const char	*devpath;
707 	char		*ctds, *slice;
708 
709 	devpath = di_devlink_path(dl);
710 	if ((dnode == NULL) || (devpath == NULL))
711 		return (DI_WALK_TERMINATE);
712 
713 	/* trim the slice off the public name */
714 	if (((ctds = strrchr(devpath, '/')) != NULL) &&
715 	    ((slice = strchr(ctds, 's')) != NULL))
716 		*slice = '\0';
717 
718 	/* Establish the public /dev name (no slice) */
719 	dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
720 
721 	if (ctds && slice)
722 		*slice = 's';
723 	return (DI_WALK_TERMINATE);
724 }
725 
726 static void
727 dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
728 {
729 	int i;
730 
731 	/* free the stuff we point to */
732 	if (dnode->ddn_devid)
733 		topo_mod_strfree(mod, dnode->ddn_devid);
734 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
735 		/* topo_mod_strfree does NULL checking. */
736 		topo_mod_strfree(mod, dnode->ddn_ppath[i]);
737 		topo_mod_strfree(mod, dnode->ddn_target_port[i]);
738 		topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
739 		topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
740 	}
741 	topo_mod_free(mod, dnode->ddn_ppath,
742 	    dnode->ddn_ppath_count * sizeof (char *));
743 	topo_mod_free(mod, dnode->ddn_target_port,
744 	    dnode->ddn_ppath_count * sizeof (char *));
745 	topo_mod_free(mod, dnode->ddn_attached_port,
746 	    dnode->ddn_ppath_count * sizeof (char *));
747 	topo_mod_free(mod, dnode->ddn_bridge_port,
748 	    dnode->ddn_ppath_count * sizeof (char *));
749 	topo_mod_strfree(mod, dnode->ddn_dpath);
750 	topo_mod_strfree(mod, dnode->ddn_lpath);
751 
752 	topo_mod_strfree(mod, dnode->ddn_mfg);
753 	topo_mod_strfree(mod, dnode->ddn_model);
754 	topo_mod_strfree(mod, dnode->ddn_serial);
755 	topo_mod_strfree(mod, dnode->ddn_firm);
756 	topo_mod_strfree(mod, dnode->ddn_cap);
757 
758 	/* free self */
759 	topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
760 }
761 
762 static int
763 dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
764 {
765 	topo_mod_t	*mod = cbp->dcb_mod;
766 	dev_di_node_t	*dnode;
767 	di_path_t	pnode;
768 	char		*path;
769 	int		mlen;
770 	char		*minorpath;
771 	char		*extn = ":a";
772 	char		*s;
773 	int64_t		*nblocksp;
774 	uint64_t	nblocks;
775 	int		*dblksizep;
776 	uint_t		dblksize;
777 	char		lentry[MAXPATHLEN];
778 	int		pathcount;
779 	int		*inq_dtype, itype;
780 	int		i;
781 
782 	if (devid) {
783 		/*
784 		 * Check for list duplicate using devid search.
785 		 * Note if there is no devid, then we can end up with duplicates
786 		 * in the list, but this doesn't do any harm.
787 		 */
788 		for (dnode = topo_list_next(cbp->dcb_list);
789 		    dnode != NULL; dnode = topo_list_next(dnode)) {
790 			if (dnode->ddn_devid &&
791 			    devid_str_compare(dnode->ddn_devid, devid) == 0) {
792 				topo_mod_dprintf(mod, "dev_di_node_add: "
793 				    "already there %s\n", devid);
794 				return (0);
795 			}
796 		}
797 	}
798 
799 	if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
800 		return (-1);
801 
802 	if (devid) {
803 		/* Establish the devid. */
804 		dnode->ddn_devid = topo_mod_strdup(mod, devid);
805 		if (dnode->ddn_devid == NULL)
806 			goto error;
807 	}
808 
809 	/* Establish the devinfo dpath */
810 	if ((path = di_devfs_path(node)) == NULL) {
811 		(void) topo_mod_seterrno(mod, errno);
812 		goto error;
813 	}
814 
815 	dnode->ddn_dpath = topo_mod_strdup(mod, path);
816 	di_devfs_path_free(path);
817 	if (dnode->ddn_dpath == NULL)
818 		goto error;
819 
820 	/*
821 	 * Establish the physical ppath and target ports. If the device is
822 	 * non-mpxio then dpath and ppath are the same, and the target port is a
823 	 * property of the device node.
824 	 *
825 	 * If dpath is a client node under scsi_vhci, then iterate over all
826 	 * paths and get their physical paths and target port properrties.
827 	 * di_path_client_next_path call below will
828 	 * return non-NULL, and ppath is set to the physical path to the first
829 	 * pathinfo node.
830 	 *
831 	 * NOTE: It is possible to get a generic.vs.non-generic path
832 	 * for di_devfs_path.vs.di_path_devfs_path like:
833 	 *    xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
834 	 *  pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
835 	 * To resolve this issue disk_declare_path() needs to use the
836 	 * special di_devfs_path_match() interface.
837 	 */
838 	pathcount = 0;
839 	pnode = NULL;
840 	while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
841 		pathcount++;
842 	}
843 
844 	if (pathcount == 0) {
845 		if ((dnode->ddn_ppath =
846 		    topo_mod_zalloc(mod, sizeof (char *))) == NULL)
847 			goto error;
848 
849 		dnode->ddn_ppath_count = 1;
850 		if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
851 		    dnode->ddn_dpath)) == NULL)
852 			goto error;
853 
854 		if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
855 		    sizeof (char *))) == NULL)
856 			goto error;
857 
858 		if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
859 		    sizeof (char *))) == NULL)
860 			goto error;
861 
862 		if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
863 		    sizeof (char *))) == NULL)
864 			goto error;
865 
866 		/* There should be only one target port for a devinfo node. */
867 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
868 		    SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
869 			if ((dnode->ddn_target_port[0] =
870 			    topo_mod_strdup(mod,
871 			    scsi_wwnstr_skip_ua_prefix(s))) ==
872 			    NULL)
873 				goto error;
874 		}
875 
876 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
877 		    SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
878 			/* There should be one attached port if any. */
879 			if ((dnode->ddn_attached_port[0] =
880 			    topo_mod_strdup(mod,
881 			    scsi_wwnstr_skip_ua_prefix(s))) ==
882 			    NULL)
883 				goto error;
884 		}
885 
886 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
887 		    SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
888 			/* There should be one bridge port if any. */
889 			if ((dnode->ddn_bridge_port[0] =
890 			    topo_mod_strdup(mod,
891 			    scsi_wwnstr_skip_ua_prefix(s))) ==
892 			    NULL)
893 				goto error;
894 		}
895 
896 	} else {
897 		/* processing a scsi_vhci device. */
898 		if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
899 		    pathcount * sizeof (char *))) == NULL)
900 			goto error;
901 
902 		dnode->ddn_ppath_count = pathcount;
903 
904 		if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
905 		    pathcount * sizeof (char *))) == NULL)
906 			goto error;
907 
908 		if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
909 		    pathcount * sizeof (char *))) == NULL)
910 			goto error;
911 
912 		if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
913 		    pathcount * sizeof (char *))) == NULL)
914 			goto error;
915 
916 		pnode = NULL;
917 		pathcount = 0;
918 		while ((pnode = di_path_client_next_path(node,
919 		    pnode)) != NULL) {
920 			if ((path = di_path_devfs_path(pnode)) == NULL) {
921 				(void) topo_mod_seterrno(mod, errno);
922 				goto error;
923 			}
924 
925 			dnode->ddn_ppath[pathcount] =
926 			    topo_mod_strdup(mod, path);
927 			di_devfs_path_free(path);
928 			if (dnode->ddn_ppath[pathcount] == NULL)
929 				goto error;
930 
931 			if ((di_path_prop_lookup_strings(pnode,
932 			    SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
933 				if ((dnode->ddn_target_port[pathcount] =
934 				    topo_mod_strdup(mod,
935 				    scsi_wwnstr_skip_ua_prefix(s))) ==
936 				    NULL)
937 					goto error;
938 			}
939 
940 			if ((di_path_prop_lookup_strings(pnode,
941 			    SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
942 				if ((dnode->ddn_attached_port[pathcount] =
943 				    topo_mod_strdup(mod,
944 				    scsi_wwnstr_skip_ua_prefix(s))) ==
945 				    NULL)
946 					goto error;
947 			}
948 
949 			if ((di_path_prop_lookup_strings(pnode,
950 			    SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
951 				if ((dnode->ddn_bridge_port[pathcount] =
952 				    topo_mod_strdup(mod,
953 				    scsi_wwnstr_skip_ua_prefix(s))) ==
954 				    NULL)
955 					goto error;
956 			}
957 
958 			pathcount++;
959 		}
960 	}
961 
962 	/*
963 	 * Find the public /dev name for a disk by adding a minor name and using
964 	 * di_devlink interface for reverse translation (use devinfo path).
965 	 */
966 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
967 	    &inq_dtype) > 0) {
968 		dnode->ddn_dtype = *inq_dtype;
969 		itype = (*inq_dtype) & DTYPE_MASK;
970 		if (itype == DTYPE_DIRECT) {
971 			mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
972 			if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
973 				goto error;
974 			(void) snprintf(minorpath, mlen, "%s%s",
975 			    dnode->ddn_dpath, extn);
976 			cbp->dcb_dnode = dnode;
977 			(void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
978 			    minorpath, DI_PRIMARY_LINK, cbp,
979 			    disk_devlink_callback);
980 			topo_mod_free(mod, minorpath, mlen);
981 			if (dnode->ddn_lpath == NULL) {
982 				topo_mod_dprintf(mod, "dev_di_node_add: "
983 				    "failed to determine logical path");
984 			}
985 		}
986 	} else {
987 		dnode->ddn_dtype = DTYPE_UNKNOWN;
988 	}
989 
990 	/* cache various bits of optional information about the device. */
991 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
992 	    INQUIRY_VENDOR_ID, &s) > 0) {
993 		if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
994 			goto error;
995 	}
996 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
997 	    INQUIRY_PRODUCT_ID, &s) > 0) {
998 		if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
999 			goto error;
1000 	}
1001 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
1002 	    INQUIRY_REVISION_ID, &s) > 0) {
1003 		if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
1004 			goto error;
1005 	}
1006 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
1007 	    INQUIRY_SERIAL_NO, &s) > 0) {
1008 		if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
1009 			goto error;
1010 	} else {
1011 		/*
1012 		 * Many USB disk devices don't emulate serial inquiry number
1013 		 * because their serial number can be longer than the standard
1014 		 * SCSI length. If we didn't get an inquiry serial number, fill
1015 		 * one in this way.
1016 		 */
1017 		di_node_t parent;
1018 
1019 		if ((parent = di_parent_node(node)) != DI_NODE_NIL &&
1020 		    di_prop_lookup_strings(DDI_DEV_T_ANY, parent,
1021 		    "usb-serialno", &s) > 0) {
1022 			if ((dnode->ddn_serial = disk_trim_whitespace(mod,
1023 			    s)) == NULL) {
1024 				goto error;
1025 			}
1026 		}
1027 	}
1028 
1029 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
1030 	    "device-nblocks", &nblocksp) > 0) {
1031 		nblocks = (uint64_t)*nblocksp;
1032 		/*
1033 		 * To save kernel memory, the driver may not define
1034 		 * "device-dblksize" when its value is default DEV_BSIZE.
1035 		 */
1036 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
1037 		    "device-dblksize", &dblksizep) > 0)
1038 			dblksize = (uint_t)*dblksizep;
1039 		else
1040 			dblksize = DEV_BSIZE;		/* default value */
1041 		(void) snprintf(lentry, sizeof (lentry),
1042 		    "%" PRIu64, nblocks * dblksize);
1043 		if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
1044 			goto error;
1045 	}
1046 
1047 	topo_mod_dprintf(mod, "dev_di_node_add: "
1048 	    "adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
1049 	topo_mod_dprintf(mod, "                  "
1050 	    "       %s\n", dnode->ddn_dpath);
1051 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
1052 		topo_mod_dprintf(mod, "                  "
1053 		    "       %s\n", dnode->ddn_ppath[i]);
1054 	}
1055 	topo_list_append(cbp->dcb_list, dnode);
1056 	return (0);
1057 
1058 error:
1059 	dev_di_node_free(mod, dnode);
1060 	return (-1);
1061 }
1062 
1063 /* di_walk_node callback for disk_list_gather */
1064 static int
1065 dev_walk_di_nodes(di_node_t node, void *arg)
1066 {
1067 	char			*devidstr = NULL;
1068 	char			*s;
1069 	int			*val;
1070 
1071 	/*
1072 	 * If it's not a scsi_vhci client and doesn't have a target_port
1073 	 * property and doesn't have a target property then it's not a storage
1074 	 * device and we're not interested.
1075 	 */
1076 	if (di_path_client_next_path(node, NULL) == NULL &&
1077 	    di_prop_lookup_strings(DDI_DEV_T_ANY, node,
1078 	    SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
1079 	    di_prop_lookup_ints(DDI_DEV_T_ANY, node,
1080 	    SCSI_ADDR_PROP_TARGET, &val) <= 0) {
1081 		return (DI_WALK_CONTINUE);
1082 	}
1083 	(void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
1084 	    DEVID_PROP_NAME, &devidstr);
1085 
1086 	/* create/find the devid scsi topology node */
1087 	(void) dev_di_node_add(node, devidstr, arg);
1088 
1089 	return (DI_WALK_CONTINUE);
1090 }
1091 
1092 int
1093 dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
1094 {
1095 	di_node_t		devtree;
1096 	di_devlink_handle_t	devhdl;
1097 	disk_cbdata_t		dcb;
1098 
1099 	if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
1100 		topo_mod_dprintf(mod, "disk_list_gather: "
1101 		    "topo_mod_devinfo() failed");
1102 		return (-1);
1103 	}
1104 
1105 	if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
1106 		topo_mod_dprintf(mod, "disk_list_gather: "
1107 		    "di_devlink_init() failed");
1108 		return (-1);
1109 	}
1110 
1111 	dcb.dcb_mod = mod;
1112 	dcb.dcb_list = listp;
1113 	dcb.dcb_devhdl = devhdl;
1114 
1115 	/* walk the devinfo snapshot looking for disk nodes */
1116 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
1117 	    dev_walk_di_nodes);
1118 
1119 	(void) di_devlink_fini(&devhdl);
1120 
1121 	return (0);
1122 }
1123 
1124 void
1125 dev_list_free(topo_mod_t *mod, topo_list_t *listp)
1126 {
1127 	dev_di_node_t	*dnode;
1128 
1129 	while ((dnode = topo_list_next(listp)) != NULL) {
1130 		/* order of delete/free is important */
1131 		topo_list_delete(listp, dnode);
1132 		dev_di_node_free(mod, dnode);
1133 	}
1134 }
1135 
1136 /*
1137  * Query the current disk status. If successful, the disk status is returned
1138  * as an nvlist consisting of at least the following members:
1139  *
1140  *	protocol	string		Supported protocol (currently "scsi")
1141  *
1142  *	status		nvlist		Arbitrary protocol-specific information
1143  *					about the current state of the disk.
1144  *
1145  *	faults		nvlist		A list of supported faults. Each
1146  *					element of this list is a boolean value.
1147  *					An element's existence indicates that
1148  *					the drive supports detecting this fault,
1149  *					and the value indicates the current
1150  *					state of the fault.
1151  *
1152  *	<fault-name>	nvlist		For each fault named in 'faults', a
1153  *					nvlist describing protocol-specific
1154  *					attributes of the fault.
1155  *
1156  * This method relies on the libdiskstatus library to query this information.
1157  */
1158 static int
1159 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
1160     nvlist_t *in_nvl, nvlist_t **out_nvl)
1161 {
1162 	disk_status_t	*dsp;
1163 	char		*devpath, *fullpath;
1164 	size_t		pathlen;
1165 	nvlist_t	*status;
1166 	int		err;
1167 
1168 	*out_nvl = NULL;
1169 
1170 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
1171 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1172 
1173 	/*
1174 	 * If the caller specifies the "path" parameter, then this indicates
1175 	 * that we should use this instead of deriving it from the topo node
1176 	 * itself.
1177 	 */
1178 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
1179 		devpath = NULL;
1180 	} else {
1181 		/*
1182 		 * Get the /devices path and attempt to open the disk status
1183 		 * handle.
1184 		 */
1185 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
1186 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
1187 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
1188 
1189 		/*
1190 		 * Note that sizeof(string) includes the terminating NULL byte
1191 		 */
1192 		pathlen = strlen(devpath) + sizeof ("/devices") +
1193 		    sizeof (PHYS_EXTN) - 1;
1194 
1195 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
1196 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
1197 
1198 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
1199 		    PHYS_EXTN);
1200 
1201 		topo_mod_strfree(mod, devpath);
1202 	}
1203 
1204 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
1205 		if (devpath)
1206 			topo_mod_free(mod, fullpath, pathlen);
1207 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
1208 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
1209 	}
1210 
1211 	if (devpath)
1212 		topo_mod_free(mod, fullpath, pathlen);
1213 
1214 	if ((status = disk_status_get(dsp)) == NULL) {
1215 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
1216 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
1217 		disk_status_close(dsp);
1218 		return (topo_mod_seterrno(mod, err));
1219 	}
1220 
1221 	*out_nvl = status;
1222 	disk_status_close(dsp);
1223 	return (0);
1224 }
1225