xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/disk/disk_common.c (revision e44e85a7f9935f0428e188393e3da61b17e83884)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
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 <devid.h>
42 #include <sys/libdevid.h>
43 #include <pthread.h>
44 #include <inttypes.h>
45 #include <sys/dkio.h>
46 #include <sys/scsi/scsi_types.h>
47 #include <fm/topo_mod.h>
48 #include <fm/topo_list.h>
49 #include <fm/libdiskstatus.h>
50 #include <sys/fm/protocol.h>
51 #include "disk.h"
52 
53 /*
54  * disk node information.
55  */
56 typedef struct disk_di_node {
57 	topo_list_t	ddn_list;	/* list of disks */
58 
59 	/* the following two fields are always defined */
60 	char		*ddn_devid;	/* devid of disk */
61 	char		*ddn_dpath;	/* path to devinfo (may be vhci) */
62 	char		**ddn_ppath;	/* physical path to device (phci) */
63 	int		ddn_ppath_count;
64 
65 	char		*ddn_lpath;	/* logical path (public /dev name) */
66 
67 	char		*ddn_mfg;	/* misc information about device */
68 	char		*ddn_model;
69 	char		*ddn_serial;
70 	char		*ddn_firm;
71 	char		*ddn_cap;
72 
73 	char		**ddn_target_port;
74 	int		ddn_target_port_count;
75 } disk_di_node_t;
76 
77 /* common callback information for di_walk_node() and di_devlink_walk */
78 typedef struct disk_cbdata {
79 	topo_mod_t		*dcb_mod;
80 	topo_list_t		*dcb_list;
81 
82 	di_devlink_handle_t	dcb_devhdl;
83 	disk_di_node_t		*dcb_dnode;	/* for di_devlink_walk only */
84 } disk_cbdata_t;
85 
86 /*
87  * Given a /devices path for a whole disk, appending this extension gives the
88  * path to a raw device that can be opened.
89  */
90 #if defined(__i386) || defined(__amd64)
91 #define	PHYS_EXTN	":q,raw"
92 #elif defined(__sparc) || defined(__sparcv9)
93 #define	PHYS_EXTN	":c,raw"
94 #else
95 #error	Unknown architecture
96 #endif
97 
98 /*
99  * Methods for disks. This is used by the disk-transport module to
100  * generate ereports based off SCSI disk status.
101  */
102 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
103 	nvlist_t *, nvlist_t **);
104 
105 static const topo_method_t disk_methods[] = {
106 	{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
107 	    TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
108 	    disk_status },
109 	{ NULL }
110 };
111 
112 static const topo_pgroup_info_t io_pgroup = {
113 	TOPO_PGROUP_IO,
114 	TOPO_STABILITY_PRIVATE,
115 	TOPO_STABILITY_PRIVATE,
116 	1
117 };
118 
119 static const topo_pgroup_info_t disk_auth_pgroup = {
120 	FM_FMRI_AUTHORITY,
121 	TOPO_STABILITY_PRIVATE,
122 	TOPO_STABILITY_PRIVATE,
123 	1
124 };
125 
126 static const topo_pgroup_info_t storage_pgroup = {
127 	TOPO_PGROUP_STORAGE,
128 	TOPO_STABILITY_PRIVATE,
129 	TOPO_STABILITY_PRIVATE,
130 	1
131 };
132 
133 /*
134  * Set the properties of the disk node, from disk_di_node_t data.
135  * Properties include:
136  *	group: protocol	 properties: resource, asru, label, fru
137  *	group: authority properties: product-id, chasis-id, server-id
138  *	group: io	 properties: devfs-path, devid
139  *	group: storage	 properties:
140  *		- logical-disk, disk-model, disk-manufacturer, serial-number
141  *		- firmware-revision, capacity-in-bytes
142  */
143 static int
144 disk_set_props(topo_mod_t *mod, tnode_t *parent,
145     tnode_t *dtn, disk_di_node_t *dnode)
146 {
147 	nvlist_t	*asru = NULL;
148 	char		*label = NULL;
149 	nvlist_t	*fmri = NULL;
150 	int		err;
151 
152 	/* form and set the asru */
153 	if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
154 	    dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
155 		err = ETOPO_FMRI_UNKNOWN;
156 		topo_mod_dprintf(mod, "disk_set_props: "
157 		    "asru error %s\n", topo_strerror(err));
158 		goto error;
159 	}
160 	if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
161 		topo_mod_dprintf(mod, "disk_set_props: "
162 		    "asru_set error %s\n", topo_strerror(err));
163 		goto error;
164 	}
165 
166 	/* pull the label property down from our parent 'bay' node */
167 	if (topo_node_label(parent, &label, &err) != 0) {
168 		topo_mod_dprintf(mod, "disk_set_props: "
169 		    "label error %s\n", topo_strerror(err));
170 		goto error;
171 	}
172 	if (topo_node_label_set(dtn, label, &err) != 0) {
173 		topo_mod_dprintf(mod, "disk_set_props: "
174 		    "label_set error %s\n", topo_strerror(err));
175 		goto error;
176 	}
177 
178 	/* get the resource fmri, and use it as the fru */
179 	if (topo_node_resource(dtn, &fmri, &err) != 0) {
180 		topo_mod_dprintf(mod, "disk_set_props: "
181 		    "resource error: %s\n", topo_strerror(err));
182 		goto error;
183 	}
184 	if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
185 		topo_mod_dprintf(mod, "disk_set_props: "
186 		    "fru_set error: %s\n", topo_strerror(err));
187 		goto error;
188 	}
189 
190 	/* create/set the authority group */
191 	if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
192 	    (err != ETOPO_PROP_DEFD)) {
193 		topo_mod_dprintf(mod, "disk_set_props: "
194 		    "create disk_auth error %s\n", topo_strerror(err));
195 		goto error;
196 	}
197 
198 	/* create/set the devfs-path and devid in the io group */
199 	if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
200 		topo_mod_dprintf(mod, "disk_set_props: "
201 		    "create io error %s\n", topo_strerror(err));
202 		goto error;
203 	}
204 
205 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
206 	    TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
207 		topo_mod_dprintf(mod, "disk_set_props: "
208 		    "set dev error %s\n", topo_strerror(err));
209 		goto error;
210 	}
211 
212 	if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEVID,
213 	    TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
214 		topo_mod_dprintf(mod, "disk_set_props: "
215 		    "set devid error %s\n", topo_strerror(err));
216 		goto error;
217 	}
218 
219 	if (dnode->ddn_ppath_count != 0 &&
220 	    topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
221 	    TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
222 	    dnode->ddn_ppath_count, &err) != 0) {
223 		topo_mod_dprintf(mod, "disk_set_props: "
224 		    "set phys-path error %s\n", topo_strerror(err));
225 		goto error;
226 	}
227 
228 	/* create the storage group */
229 	if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
230 		topo_mod_dprintf(mod, "disk_set_props: "
231 		    "create storage error %s\n", topo_strerror(err));
232 		goto error;
233 	}
234 
235 	/* set the storage group public /dev name */
236 	if (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
237 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
238 	    dnode->ddn_lpath, &err) != 0) {
239 		topo_mod_dprintf(mod, "disk_set_props: "
240 		    "set disk_name error %s\n", topo_strerror(err));
241 		goto error;
242 	}
243 
244 	/* populate other misc storage group properties */
245 	if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
246 	    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
247 	    dnode->ddn_mfg, &err) != 0)) {
248 		topo_mod_dprintf(mod, "disk_set_props: "
249 		    "set mfg error %s\n", topo_strerror(err));
250 		goto error;
251 	}
252 	if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
253 	    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
254 	    dnode->ddn_model, &err) != 0)) {
255 		topo_mod_dprintf(mod, "disk_set_props: "
256 		    "set model error %s\n", topo_strerror(err));
257 		goto error;
258 	}
259 	if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
260 	    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
261 	    dnode->ddn_serial, &err) != 0)) {
262 		topo_mod_dprintf(mod, "disk_set_props: "
263 		    "set serial error %s\n", topo_strerror(err));
264 		goto error;
265 	}
266 	if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
267 	    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
268 	    dnode->ddn_firm, &err) != 0)) {
269 		topo_mod_dprintf(mod, "disk_set_props: "
270 		    "set firm error %s\n", topo_strerror(err));
271 		goto error;
272 	}
273 	if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
274 	    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
275 	    dnode->ddn_cap, &err) != 0)) {
276 		topo_mod_dprintf(mod, "disk_set_props: "
277 		    "set cap error %s\n", topo_strerror(err));
278 		goto error;
279 	}
280 	err = 0;
281 
282 out:	if (fmri)
283 		nvlist_free(fmri);
284 	if (label)
285 		topo_mod_strfree(mod, label);
286 	if (asru)
287 		nvlist_free(asru);
288 	return (err);
289 
290 error:	err = topo_mod_seterrno(mod, err);
291 	goto out;
292 }
293 
294 /*
295  * Trim leading and trailing whitespace from the string.
296  */
297 static char *
298 disk_trim_whitespace(topo_mod_t *mod, const char *begin)
299 {
300 	const char *end;
301 	char *buf;
302 	size_t count;
303 
304 	if (begin == NULL)
305 		return (NULL);
306 
307 	end = begin + strlen(begin);
308 
309 	while (begin < end && isspace(*begin))
310 		begin++;
311 	while (begin < end && isspace(*(end - 1)))
312 		end--;
313 
314 	count = end - begin;
315 	if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
316 		return (NULL);
317 
318 	(void) strlcpy(buf, begin, count + 1);
319 
320 	return (buf);
321 }
322 
323 /*
324  * Manufacturing strings can contain characters that are invalid for use in hc
325  * authority names.  This trims leading and trailing whitespace, and
326  * substitutes any characters known to be bad.
327  */
328 char *
329 disk_auth_clean(topo_mod_t *mod, const char *str)
330 {
331 	char *buf, *p;
332 
333 	if (str == NULL)
334 		return (NULL);
335 
336 	if ((buf = topo_mod_strdup(mod, str)) == NULL)
337 		return (NULL);
338 
339 	while ((p = strpbrk(buf, " :=")) != NULL)
340 		*p = '-';
341 
342 	return (buf);
343 }
344 
345 /* create the disk topo node */
346 static tnode_t *
347 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
348     disk_di_node_t *dnode, const char *name, topo_instance_t i)
349 {
350 	int		len;
351 	nvlist_t	*fmri;
352 	tnode_t		*dtn;
353 	char		*part = NULL;
354 	nvlist_t	*auth;
355 	char		*mfg, *model, *firm, *serial;
356 
357 	mfg = disk_auth_clean(mod, dnode->ddn_mfg);
358 	model = disk_auth_clean(mod, dnode->ddn_model);
359 	firm = disk_auth_clean(mod, dnode->ddn_firm);
360 	serial = disk_auth_clean(mod, dnode->ddn_serial);
361 
362 	/* form 'part=' of fmri as "<mfg>-<model>" */
363 	if (mfg != NULL && model != NULL) {
364 		len = strlen(mfg) + 1 + strlen(model) + 1;
365 		if ((part = topo_mod_alloc(mod, len)) != NULL)
366 			(void) snprintf(part, len, "%s-%s",
367 			    mfg, model);
368 	}
369 
370 	auth = topo_mod_auth(mod, parent);
371 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
372 	    auth, part ? part : model, firm, serial);
373 	nvlist_free(auth);
374 
375 	topo_mod_strfree(mod, part);
376 	topo_mod_strfree(mod, mfg);
377 	topo_mod_strfree(mod, model);
378 	topo_mod_strfree(mod, firm);
379 	topo_mod_strfree(mod, serial);
380 
381 	if (fmri == NULL) {
382 		topo_mod_dprintf(mod, "disk_tnode_create: "
383 		    "hcfmri (%s%d/%s%d) error %s\n",
384 		    topo_node_name(parent), topo_node_instance(parent),
385 		    name, i, topo_strerror(topo_mod_errno(mod)));
386 		return (NULL);
387 	}
388 
389 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
390 		topo_mod_dprintf(mod, "disk_tnode_create: "
391 		    "bind (%s%d/%s%d) error %s\n",
392 		    topo_node_name(parent), topo_node_instance(parent),
393 		    name, i, topo_strerror(topo_mod_errno(mod)));
394 		nvlist_free(fmri);
395 		return (NULL);
396 	}
397 	nvlist_free(fmri);
398 
399 	/* add the properties of the disk */
400 	if (disk_set_props(mod, parent, dtn, dnode) != 0) {
401 		topo_mod_dprintf(mod, "disk_tnode_create: "
402 		    "disk_set_props (%s%d/%s%d) error %s\n",
403 		    topo_node_name(parent), topo_node_instance(parent),
404 		    name, i, topo_strerror(topo_mod_errno(mod)));
405 		topo_node_unbind(dtn);
406 		return (NULL);
407 	}
408 	return (dtn);
409 }
410 
411 static int
412 disk_declare(topo_mod_t *mod, tnode_t *parent, disk_di_node_t *dnode)
413 {
414 	tnode_t		*dtn;
415 
416 	/* create the disk topo node: one disk per 'bay' */
417 	dtn = disk_tnode_create(mod, parent, dnode, DISK, 0);
418 	if (dtn == NULL) {
419 		topo_mod_dprintf(mod, "disk_declare: "
420 		    "disk_tnode_create error %s\n",
421 		    topo_strerror(topo_mod_errno(mod)));
422 		return (-1);
423 	}
424 
425 	/* register disk_methods against the disk topo node */
426 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
427 		topo_mod_dprintf(mod, "disk_declare: "
428 		    "topo_method_register error %s\n",
429 		    topo_strerror(topo_mod_errno(mod)));
430 		topo_node_unbind(dtn);
431 		return (-1);
432 	}
433 	return (0);
434 }
435 
436 int
437 disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
438     const char *path)
439 {
440 	disk_di_node_t		*dnode;
441 	int i;
442 
443 	/*
444 	 * Check for match using physical phci (ddn_ppath). Use
445 	 * di_devfs_path_match so generic.vs.non-generic names match.
446 	 */
447 	for (dnode = topo_list_next(listp); dnode != NULL;
448 	    dnode = topo_list_next(dnode)) {
449 		if (dnode->ddn_ppath == NULL)
450 			continue;
451 
452 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
453 			if (di_devfs_path_match(dnode->ddn_ppath[0], path))
454 				return (disk_declare(mod, parent, dnode));
455 		}
456 	}
457 
458 	topo_mod_dprintf(mod, "disk_declare_path: "
459 	    "failed to find disk matching path %s", path);
460 	return (0);
461 }
462 
463 int
464 disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
465     const char *addr)
466 {
467 	disk_di_node_t *dnode;
468 	int i;
469 
470 	/* Check for match using addr. */
471 	for (dnode = topo_list_next(listp); dnode != NULL;
472 	    dnode = topo_list_next(dnode)) {
473 		if (dnode->ddn_target_port == NULL)
474 			continue;
475 
476 		for (i = 0; i < dnode->ddn_target_port_count; i++) {
477 			if (strncmp(dnode->ddn_target_port[i], addr,
478 			    strcspn(dnode->ddn_target_port[i], ":")) == 0)
479 				return (disk_declare(mod, parent, dnode));
480 		}
481 	}
482 
483 	topo_mod_dprintf(mod, "disk_declare_addr: "
484 	    "failed to find disk matching addr %s", addr);
485 	return (0);
486 }
487 
488 /* di_devlink callback for disk_di_node_add */
489 static int
490 disk_devlink_callback(di_devlink_t dl, void *arg)
491 {
492 	disk_cbdata_t	*cbp = (disk_cbdata_t *)arg;
493 	topo_mod_t	*mod = cbp->dcb_mod;
494 	disk_di_node_t	*dnode = cbp->dcb_dnode;
495 	const char	*devpath;
496 	char		*ctds, *slice;
497 
498 	devpath = di_devlink_path(dl);
499 	if ((dnode == NULL) || (devpath == NULL))
500 		return (DI_WALK_TERMINATE);
501 
502 	/* trim the slice off the public name */
503 	if (((ctds = strrchr(devpath, '/')) != NULL) &&
504 	    ((slice = strchr(ctds, 's')) != NULL))
505 		*slice = '\0';
506 
507 	/* Establish the public /dev name (no slice) */
508 	dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
509 
510 	if (ctds && slice)
511 		*slice = 's';
512 	return (DI_WALK_TERMINATE);
513 }
514 
515 static void
516 disk_di_node_free(topo_mod_t *mod, disk_di_node_t *dnode)
517 {
518 	int i;
519 
520 	/* free the stuff we point to */
521 	topo_mod_strfree(mod, dnode->ddn_devid);
522 	for (i = 0; i < dnode->ddn_ppath_count; i++)
523 		topo_mod_strfree(mod, dnode->ddn_ppath[i]);
524 	topo_mod_free(mod, dnode->ddn_ppath,
525 	    dnode->ddn_ppath_count * sizeof (uintptr_t));
526 	topo_mod_strfree(mod, dnode->ddn_dpath);
527 	topo_mod_strfree(mod, dnode->ddn_lpath);
528 
529 	topo_mod_strfree(mod, dnode->ddn_mfg);
530 	topo_mod_strfree(mod, dnode->ddn_model);
531 	topo_mod_strfree(mod, dnode->ddn_serial);
532 	topo_mod_strfree(mod, dnode->ddn_firm);
533 	topo_mod_strfree(mod, dnode->ddn_cap);
534 
535 	for (i = 0; i < dnode->ddn_target_port_count; i++)
536 		topo_mod_strfree(mod, dnode->ddn_target_port[i]);
537 	topo_mod_free(mod, dnode->ddn_target_port,
538 	    dnode->ddn_target_port_count * sizeof (uintptr_t));
539 
540 	/* free self */
541 	topo_mod_free(mod, dnode, sizeof (disk_di_node_t));
542 }
543 
544 static int
545 disk_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
546 {
547 	topo_mod_t	*mod = cbp->dcb_mod;
548 	disk_di_node_t	*dnode;
549 	di_path_t	pnode;
550 	char		*path;
551 	int		mlen;
552 	char		*minorpath;
553 	char		*extn = ":a";
554 	char		*s;
555 	int64_t		*nblocksp;
556 	uint64_t	nblocks;
557 	int		*dblksizep;
558 	uint_t		dblksize;
559 	char		lentry[MAXPATHLEN];
560 	int		pathcount, portcount;
561 	int 		ret, i;
562 
563 	/* check for list duplicate using devid search */
564 	for (dnode = topo_list_next(cbp->dcb_list);
565 	    dnode != NULL; dnode = topo_list_next(dnode)) {
566 		if (devid_str_compare(dnode->ddn_devid, devid) == 0) {
567 			topo_mod_dprintf(mod, "disk_di_node_add: "
568 			    "already there %s\n", devid);
569 			return (0);
570 		}
571 	}
572 
573 	if ((dnode = topo_mod_zalloc(mod, sizeof (disk_di_node_t))) == NULL)
574 		return (-1);
575 
576 	/* Establish the devid. */
577 	dnode->ddn_devid = topo_mod_strdup(mod, devid);
578 	if (dnode->ddn_devid == NULL)
579 		goto error;
580 
581 	/* Establish the devinfo dpath */
582 	if ((path = di_devfs_path(node)) == NULL) {
583 		topo_mod_seterrno(mod, errno);
584 		goto error;
585 	}
586 
587 	dnode->ddn_dpath = topo_mod_strdup(mod, path);
588 	di_devfs_path_free(path);
589 	if (dnode->ddn_dpath == NULL)
590 		goto error;
591 
592 	/*
593 	 * Establish the physical ppath and target ports. If the device is
594 	 * non-mpxio then dpath and ppath are the same, and the target port is a
595 	 * property of the device node.
596 	 *
597 	 * If dpath is a client node under scsi_vhci, then iterate over all
598 	 * paths and get their physical paths and target port properrties.
599 	 * di_path_client_next_path call below will
600 	 * return non-NULL, and ppath is set to the physical path to the first
601 	 * pathinfo node.
602 	 *
603 	 * NOTE: It is possible to get a generic.vs.non-generic path
604 	 * for di_devfs_path.vs.di_path_devfs_path like:
605 	 *    xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
606 	 *  pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
607 	 * To resolve this issue disk_declare_path() needs to use the
608 	 * special di_devfs_path_match() interface.
609 	 */
610 	pathcount = portcount = 0;
611 	pnode = NULL;
612 	while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
613 		if ((ret = di_path_prop_lookup_strings(pnode,
614 		    "target-port", &s)) > 0)
615 			portcount += ret;
616 		pathcount++;
617 	}
618 
619 	if (pathcount == 0) {
620 		if ((dnode->ddn_ppath =
621 		    topo_mod_zalloc(mod, sizeof (uintptr_t))) == NULL)
622 			goto error;
623 
624 		dnode->ddn_ppath_count = 1;
625 		if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
626 		    dnode->ddn_dpath)) == NULL)
627 			goto error;
628 
629 		if ((ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
630 		    "target-port", &s)) > 0) {
631 			if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
632 			    ret * sizeof (uintptr_t))) == NULL)
633 				goto error;
634 
635 			dnode->ddn_target_port_count = ret;
636 
637 			for (i = 0; i < ret; i++) {
638 				if ((dnode->ddn_target_port[i] =
639 				    topo_mod_strdup(mod, s)) == NULL)
640 					goto error;
641 
642 				s += strlen(s) + 1;
643 			}
644 		}
645 	} else {
646 		if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
647 		    pathcount * sizeof (uintptr_t))) == NULL)
648 			goto error;
649 
650 		dnode->ddn_ppath_count = pathcount;
651 
652 		if (portcount != 0 &&
653 		    ((dnode->ddn_target_port = topo_mod_zalloc(mod,
654 		    portcount * sizeof (uintptr_t)))) == NULL)
655 			goto error;
656 
657 		dnode->ddn_target_port_count = portcount;
658 
659 		pnode = NULL;
660 		pathcount = portcount = 0;
661 		while ((pnode = di_path_client_next_path(node,
662 		    pnode)) != NULL) {
663 			if ((path = di_path_devfs_path(pnode)) == NULL) {
664 				topo_mod_seterrno(mod, errno);
665 				goto error;
666 			}
667 
668 			dnode->ddn_ppath[pathcount] =
669 			    topo_mod_strdup(mod, path);
670 			di_devfs_path_free(path);
671 			if (dnode->ddn_ppath[pathcount] == NULL)
672 				goto error;
673 
674 			if ((ret = di_path_prop_lookup_strings(pnode,
675 			    "target-port", &s)) > 0) {
676 				for (i = 0; i < ret; i++) {
677 					if ((dnode->ddn_target_port[portcount] =
678 					    topo_mod_strdup(mod, s)) == NULL)
679 						goto error;
680 
681 					portcount++;
682 					s += strlen(s) + 1;
683 				}
684 			}
685 
686 			pathcount++;
687 		}
688 	}
689 
690 	/*
691 	 * Find the public /dev name by adding a minor name and using
692 	 * di_devlink interface for reverse translation (use devinfo path).
693 	 */
694 	mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
695 	if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
696 		goto error;
697 	(void) snprintf(minorpath, mlen, "%s%s", dnode->ddn_dpath, extn);
698 	cbp->dcb_dnode = dnode;
699 	(void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/", minorpath,
700 	    DI_PRIMARY_LINK, cbp, disk_devlink_callback);
701 	topo_mod_free(mod, minorpath, mlen);
702 	if (dnode->ddn_lpath == NULL) {
703 		topo_mod_dprintf(mod, "disk_di_node_add: "
704 		    "failed to determine logical path");
705 		goto error;
706 	}
707 
708 	/* cache various bits of optional information about the disk */
709 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
710 	    INQUIRY_VENDOR_ID, &s) > 0) {
711 		if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
712 			goto error;
713 	}
714 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
715 	    INQUIRY_PRODUCT_ID, &s) > 0) {
716 		if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
717 			goto error;
718 	}
719 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
720 	    INQUIRY_REVISION_ID, &s) > 0) {
721 		if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
722 			goto error;
723 	}
724 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
725 	    INQUIRY_SERIAL_NO, &s) > 0) {
726 		if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
727 			goto error;
728 	}
729 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
730 	    "device-nblocks", &nblocksp) > 0) {
731 		nblocks = (uint64_t)*nblocksp;
732 		/*
733 		 * To save kernel memory, the driver may not define
734 		 * "device-dblksize" when its value is default DEV_BSIZE.
735 		 */
736 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
737 		    "device-dblksize", &dblksizep) > 0)
738 			dblksize = (uint_t)*dblksizep;
739 		else
740 			dblksize = DEV_BSIZE;		/* default value */
741 		(void) snprintf(lentry, sizeof (lentry),
742 		    "%" PRIu64, nblocks * dblksize);
743 		if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
744 			goto error;
745 	}
746 
747 	topo_mod_dprintf(mod, "disk_di_node_add: "
748 	    "adding %s\n", dnode->ddn_devid);
749 	topo_mod_dprintf(mod, "                  "
750 	    "       %s\n", dnode->ddn_dpath);
751 	for (i = 0; i < dnode->ddn_ppath_count; i++) {
752 		topo_mod_dprintf(mod, "                  "
753 		    "       %s\n", dnode->ddn_ppath[i]);
754 	}
755 	topo_list_append(cbp->dcb_list, dnode);
756 	return (0);
757 
758 error:
759 	disk_di_node_free(mod, dnode);
760 	return (-1);
761 }
762 
763 /* di_walk_node callback for disk_list_gather */
764 static int
765 disk_walk_di_nodes(di_node_t node, void *arg)
766 {
767 	char			*devidstr = NULL;
768 
769 	/* only interested in nodes that have devids */
770 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
771 	    DEVID_PROP_NAME, &devidstr) < 0) {
772 		return (DI_WALK_CONTINUE);
773 	}
774 
775 	/* create/find the devid scsi topology node */
776 	(void) disk_di_node_add(node, devidstr, arg);
777 
778 	return (DI_WALK_CONTINUE);
779 }
780 
781 int
782 disk_list_gather(topo_mod_t *mod, topo_list_t *listp)
783 {
784 	di_node_t		devtree;
785 	di_devlink_handle_t	devhdl;
786 	disk_cbdata_t		dcb;
787 
788 	if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
789 		topo_mod_dprintf(mod, "disk_list_gather: "
790 		    "di_init failed");
791 		return (-1);
792 	}
793 
794 	if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
795 		topo_mod_dprintf(mod, "disk_list_gather: "
796 		    "di_devlink_init failed");
797 		return (-1);
798 	}
799 
800 	dcb.dcb_mod = mod;
801 	dcb.dcb_list = listp;
802 	dcb.dcb_devhdl = devhdl;
803 
804 	/* walk the devinfo snapshot looking for nodes with devids */
805 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
806 	    disk_walk_di_nodes);
807 
808 	(void) di_devlink_fini(&devhdl);
809 
810 	return (0);
811 }
812 
813 void
814 disk_list_free(topo_mod_t *mod, topo_list_t *listp)
815 {
816 	disk_di_node_t	*dnode;
817 
818 	while ((dnode = topo_list_next(listp)) != NULL) {
819 		/* order of delete/free is important */
820 		topo_list_delete(listp, dnode);
821 		disk_di_node_free(mod, dnode);
822 	}
823 }
824 
825 /*
826  * Query the current disk status. If successful, the disk status is returned
827  * as an nvlist consisting of at least the following members:
828  *
829  *	protocol	string		Supported protocol (currently "scsi")
830  *
831  *	status		nvlist		Arbitrary protocol-specific information
832  *					about the current state of the disk.
833  *
834  *	faults		nvlist		A list of supported faults. Each
835  *					element of this list is a boolean value.
836  *					An element's existence indicates that
837  *					the drive supports detecting this fault,
838  *					and the value indicates the current
839  *					state of the fault.
840  *
841  *	<fault-name>	nvlist		For each fault named in 'faults', a
842  *					nvlist describing protocol-specific
843  *					attributes of the fault.
844  *
845  * This method relies on the libdiskstatus library to query this information.
846  */
847 static int
848 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
849     nvlist_t *in_nvl, nvlist_t **out_nvl)
850 {
851 	disk_status_t	*dsp;
852 	char		*devpath, *fullpath;
853 	size_t		pathlen;
854 	nvlist_t	*status;
855 	int		err;
856 
857 	*out_nvl = NULL;
858 
859 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
860 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
861 
862 	/*
863 	 * If the caller specifies the "path" parameter, then this indicates
864 	 * that we should use this instead of deriving it from the topo node
865 	 * itself.
866 	 */
867 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
868 		devpath = NULL;
869 	} else {
870 		/*
871 		 * Get the /devices path and attempt to open the disk status
872 		 * handle.
873 		 */
874 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
875 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
876 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
877 
878 		/*
879 		 * Note that sizeof(string) includes the terminating NULL byte
880 		 */
881 		pathlen = strlen(devpath) + sizeof ("/devices") +
882 		    sizeof (PHYS_EXTN) - 1;
883 
884 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
885 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
886 
887 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
888 		    PHYS_EXTN);
889 
890 		topo_mod_strfree(mod, devpath);
891 	}
892 
893 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
894 		if (devpath)
895 			topo_mod_free(mod, fullpath, pathlen);
896 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
897 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
898 	}
899 
900 	if (devpath)
901 		topo_mod_free(mod, fullpath, pathlen);
902 
903 	if ((status = disk_status_get(dsp)) == NULL) {
904 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
905 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
906 		disk_status_close(dsp);
907 		return (topo_mod_seterrno(mod, err));
908 	}
909 
910 	*out_nvl = status;
911 	disk_status_close(dsp);
912 	return (0);
913 }
914