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