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