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