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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <strings.h>
29 #include <devid.h>
30 #include <pthread.h>
31 #include <inttypes.h>
32 #include <sys/dkio.h>
33 #include <sys/scsi/scsi_types.h>
34 #include <fm/topo_mod.h>
35 #include <fm/topo_list.h>
36 #include <fm/libdiskstatus.h>
37 #include <sys/fm/protocol.h>
38 #include "disk.h"
39 
40 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
41 	nvlist_t *, nvlist_t **);
42 
43 /*
44  * Given a /devices path for a whole disk, appending this extension gives the
45  * path to a raw device that can be opened.
46  */
47 #if defined(__i386) || defined(__amd64)
48 #define	PHYS_EXTN	":q,raw"
49 #elif defined(__sparc) || defined(__sparcv9)
50 #define	PHYS_EXTN	":c,raw"
51 #else
52 #error	Unknown architecture
53 #endif
54 
55 static int disk_enum(topo_mod_t *, tnode_t *, const char *,
56 	topo_instance_t, topo_instance_t, void *, void *);
57 
58 static const topo_modops_t disk_ops =
59 	{ disk_enum, NULL };
60 
61 const topo_modinfo_t disk_info =
62 	{DISK, FM_FMRI_SCHEME_HC, DISK_VERSION, &disk_ops};
63 
64 static const topo_pgroup_info_t io_pgroup =
65 	{ TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
66 
67 static const topo_pgroup_info_t disk_auth_pgroup = {
68 	FM_FMRI_AUTHORITY,
69 	TOPO_STABILITY_PRIVATE,
70 	TOPO_STABILITY_PRIVATE,
71 };
72 
73 static const topo_pgroup_info_t storage_pgroup = {
74 	TOPO_STORAGE_PGROUP,
75 	TOPO_STABILITY_PRIVATE,
76 	TOPO_STABILITY_PRIVATE,
77 	1
78 };
79 
80 /*
81  * Methods for disks.  This is used by the disk-transport module to
82  * generate ereports based off SCSI disk status.
83  */
84 static const topo_method_t disk_methods[] = {
85 	{ TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
86 	    TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
87 	    disk_status },
88 	{ NULL }
89 };
90 static di_devlink_handle_t	devlink_hdl = NULL;
91 
92 /* disk node information */
93 typedef struct disk_di_node {
94 	topo_list_t	ddn_list;
95 	int		ddn_instance;
96 	char		*ddn_devid;
97 	di_node_t	ddn_node;
98 	char		*ddn_lpath;   /* logical path */
99 	char		*ddn_dpath;   /* device path */
100 }disk_di_node_t;
101 
102 typedef struct disk_di_nodes {
103 	pthread_mutex_t disk_di_nodes_lock;
104 	topo_list_t disk_di_nodes_list;
105 }disk_di_nodes_t;
106 
107 /* list of devices */
108 static disk_di_nodes_t disk_di_nodes;
109 
110 /* given a device find it in the global device list */
111 static disk_di_node_t *
112 disk_di_node_match_device(char *device)
113 {
114 	disk_di_node_t		*dnode;
115 
116 	(void) pthread_mutex_lock(&disk_di_nodes.disk_di_nodes_lock);
117 	for (dnode = topo_list_next(&(disk_di_nodes.disk_di_nodes_list));
118 	    dnode != NULL; dnode = topo_list_next(dnode)) {
119 		if (dnode->ddn_devid != NULL &&
120 		    strcmp(device,
121 		    dnode->ddn_dpath) == 0) {
122 			(void) pthread_mutex_unlock(
123 			    &disk_di_nodes.disk_di_nodes_lock);
124 			return (dnode);
125 		}
126 	}
127 	(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
128 	return (NULL);
129 }
130 
131 /* get the disk storage group information */
132 static void
133 disk_storage_info(topo_mod_t *mod, disk_di_node_t *dnode,
134     char **model, char **manuf, char **serial, char **firm, char **cap)
135 {
136 	char		*entry;
137 	di_node_t	node = dnode->ddn_node;
138 	int64_t		*nblocksp;
139 	uint64_t	nblocks;
140 	int		*dblksizep;
141 	uint_t		dblksize;
142 	char		lentry[MAXPATHLEN];
143 
144 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
145 	    INQUIRY_VENDOR_ID, &entry) > 0) {
146 		*manuf = topo_mod_strdup(mod, entry);
147 	}
148 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
149 	    INQUIRY_PRODUCT_ID, &entry) > 0) {
150 		*model = topo_mod_strdup(mod, entry);
151 	}
152 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
153 	    INQUIRY_REVISION_ID, &entry) > 0) {
154 		*firm = topo_mod_strdup(mod, entry);
155 	}
156 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
157 	    INQUIRY_SERIAL_NO, &entry) > 0) {
158 		*serial = topo_mod_strdup(mod, entry);
159 	}
160 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
161 	    "device-nblocks", &nblocksp) > 0) {
162 		nblocks = (uint64_t)*nblocksp;
163 		/*
164 		 * To save kernel memory, the driver may not
165 		 * define "device-dblksize" when its value is
166 		 * the default DEV_BSIZE value.
167 		 */
168 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
169 		    "device-dblksize", &dblksizep) > 0)
170 			dblksize = (uint_t)*dblksizep;
171 		else
172 			dblksize = DEV_BSIZE;		/* default value */
173 		(void) snprintf(lentry, sizeof (lentry),
174 		    "%" PRIu64, nblocks * dblksize);
175 		*cap = topo_mod_strdup(mod, lentry);
176 	}
177 }
178 
179 /* populate the protocol group properties */
180 static void
181 disk_set_proto_props(topo_mod_t *mod, tnode_t *dtn, int pinst)
182 {
183 	int		err;
184 	nvlist_t	*asru = NULL;
185 	char		label[32];
186 	char		*func = "disk_set_proto_props";
187 	nvlist_t	*fmri;
188 	disk_di_node_t	*dnode;
189 
190 	/* set the asru */
191 	dnode = topo_node_getspecific(dtn);
192 	asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
193 	    dnode->ddn_dpath, dnode->ddn_devid);
194 	if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
195 		topo_mod_dprintf(mod,
196 		    "%s: topo_node_asru_set error %d\n",
197 		    func, err);
198 		nvlist_free(asru);
199 		(void) topo_mod_seterrno(mod, err);
200 		return;
201 	}
202 	nvlist_free(asru);
203 
204 	(void) snprintf(label, sizeof (label), "HD_ID_%d", pinst);
205 	if (topo_node_label_set(dtn, label, &err) != 0) {
206 		topo_mod_dprintf(mod, "%s: label error %s\n", func,
207 		    topo_strerror(err));
208 		(void) topo_mod_seterrno(mod, err);
209 		return;
210 	}
211 
212 	/* get the resource property  */
213 	if (topo_node_resource(dtn, &fmri, &err) != 0) {
214 		topo_mod_dprintf(mod,
215 		    "%s: topo_node_resource error: %s\n", func,
216 		    topo_strerror(err));
217 		(void) topo_mod_seterrno(mod, err);
218 		return;
219 	}
220 
221 	/* set the child fru to the same as the resource */
222 	if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
223 		topo_mod_dprintf(mod,
224 		    "%s: topo_node_fru_set error: %s\n", func,
225 		    topo_strerror(err));
226 		(void) topo_mod_seterrno(mod, err);
227 		nvlist_free(fmri);
228 		return;
229 	}
230 	nvlist_free(fmri);
231 }
232 
233 
234 /*
235  * Set the properties of the disk node which include:
236  *	group: protocol  properties: resource, asru, label, fru
237  *	group: authority properties: product-id, chasis-id, server-id
238  *	group: io	 properties: devfs-path
239  *	group: storage   properties:
240  *		- logical-disk, disk-model, disk-manufacturer, serial-number
241  *		- firmware-revision, capacity-in-bytes
242  */
243 static void
244 disk_set_props(tnode_t *dtn, tnode_t *parent, char *model, char *manuf,
245     char *serial, char *firm, char *cap, int *err, topo_mod_t *mod)
246 {
247 	char	*device;
248 	char 	*ptr, *ptr1;
249 	int	inst = topo_node_instance(parent);
250 	disk_di_node_t	*dnode;
251 
252 	dnode = topo_node_getspecific(dtn);
253 
254 	/* set the protocol group properties */
255 	disk_set_proto_props(mod, dtn, inst);
256 
257 	/* create/set the authority group */
258 	if (topo_pgroup_create(dtn, &disk_auth_pgroup, err) == 0) {
259 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
260 		    FM_FMRI_AUTH_PRODUCT, err);
261 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
262 		    FM_FMRI_AUTH_CHASSIS, err);
263 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
264 		    FM_FMRI_AUTH_SERVER, err);
265 	}
266 
267 	/* create/set the devfs-path in the io group */
268 	(void) topo_pgroup_create(dtn, &io_pgroup, err);
269 
270 	if (topo_prop_get_string(parent, TOPO_BINDING_PGROUP,
271 	    TOPO_BINDING_OCCUPANT, &device, err) == 0) {
272 		(void) topo_prop_set_string(dtn, TOPO_PGROUP_IO,
273 		    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, device, err);
274 
275 		topo_mod_strfree(mod, device);
276 	}
277 
278 	/* create the storage group */
279 	(void) topo_pgroup_create(dtn, &storage_pgroup, err);
280 
281 	/* set the storage group properties */
282 	ptr = strrchr(dnode->ddn_lpath, '/');
283 	ptr1 = strchr(ptr, 's');
284 	if (ptr1)
285 		*ptr1 = '\0';
286 	(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
287 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
288 	    ptr+1, err);
289 	if (ptr1)
290 		*ptr1 = 's';
291 
292 
293 	/* populate the storage group properties */
294 	if (model) {
295 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
296 		    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE, model, err);
297 	}
298 	if (manuf) {
299 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
300 		    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE, manuf,
301 		    err);
302 	}
303 	if (serial) {
304 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
305 		    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE, serial, err);
306 	}
307 	if (firm) {
308 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
309 		    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, firm, err);
310 	}
311 	if (cap) {
312 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
313 		    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE, cap, err);
314 	}
315 }
316 
317 /* create the disk topo node */
318 /*ARGSUSED*/
319 static tnode_t *
320 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
321     const char *name, topo_instance_t i, char *model, char *manuf,
322     char *serial, char *firm, char *cap, void *priv)
323 {
324 	int		err, len = 0;
325 	nvlist_t	*fmri;
326 	tnode_t		*dtn;
327 	char 		*mm = NULL;
328 	char		*s;
329 	nvlist_t	*auth = topo_mod_auth(mod, parent);
330 
331 	if ((s = strchr(model, ' ')) != NULL) {
332 		*s = '-';
333 	}
334 	len = strlen(manuf) + strlen(model) + 2;
335 	if ((mm = topo_mod_alloc(mod, len)) != NULL)
336 		(void) snprintf(mm, len, "%s-%s", manuf, model);
337 	else
338 		mm = model;
339 
340 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
341 	    NULL, auth, mm, firm, serial);
342 
343 	nvlist_free(auth);
344 
345 	if (mm != model)
346 		topo_mod_free(mod, mm, len);
347 	else if (*s != NULL)
348 		*s = ' ';
349 
350 	if (fmri == NULL) {
351 		topo_mod_dprintf(mod,
352 		    "Unable to make nvlist for %s bind: %s.\n",
353 		    name, topo_mod_errmsg(mod));
354 		return (NULL);
355 	}
356 
357 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
358 		topo_mod_dprintf(mod,
359 		    "topo_node_bind (%s%d/%s%d) failed: %s\n",
360 		    topo_node_name(parent), topo_node_instance(parent),
361 		    name, i,
362 		    topo_strerror(topo_mod_errno(mod)));
363 		nvlist_free(fmri);
364 		return (NULL);
365 	}
366 	nvlist_free(fmri);
367 	topo_node_setspecific(dtn, priv);
368 
369 	/* add the properties of the disk */
370 	disk_set_props(dtn, parent, model, manuf, serial, firm, cap,
371 	    &err, mod);
372 
373 	return (dtn);
374 }
375 
376 /*ARGSUSED*/
377 static tnode_t *
378 disk_declare(tnode_t *parent, const char *name, topo_instance_t i,
379     void *priv, topo_mod_t *mod)
380 {
381 	tnode_t	*dtn;
382 	int	err;
383 	char	*func = "disk_declare";
384 	char	*model = NULL, *manuf = NULL, *serial = NULL;
385 	char	*cap = NULL, *firm = NULL;
386 	disk_di_node_t		*dnode  = (disk_di_node_t *)priv;
387 	nvlist_t		*fmri;
388 
389 	disk_storage_info(mod, dnode,
390 	    &model, &manuf, &serial, &firm, &cap);
391 
392 	/* create the node */
393 	dtn = disk_tnode_create(mod, parent,
394 	    name, i, model, manuf, serial, firm, cap, priv);
395 
396 	topo_mod_strfree(mod, model);
397 	topo_mod_strfree(mod, manuf);
398 	topo_mod_strfree(mod, serial);
399 	topo_mod_strfree(mod, firm);
400 	topo_mod_strfree(mod, cap);
401 
402 	if (dtn == NULL) {
403 		return (NULL);
404 	}
405 
406 	/* set the parent fru */
407 	if (topo_node_resource(parent, &fmri, &err) != 0) {
408 		topo_mod_dprintf(mod,
409 		    "%s: topo_node_resource error: %s\n", func,
410 		    topo_strerror(err));
411 		topo_node_unbind(dtn);
412 		return (NULL);
413 	}
414 	if (topo_node_fru_set(parent, fmri, 0, &err) != 0) {
415 		topo_mod_dprintf(mod, "%s topo_node_fru error: %s\n",
416 		    func, topo_strerror(err));
417 		nvlist_free(fmri);
418 		topo_node_unbind(dtn);
419 		return (NULL);
420 	}
421 
422 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
423 		topo_mod_dprintf(mod,
424 		    "topo_method_register failed: %s\n",
425 		    topo_strerror(topo_mod_errno(mod)));
426 		nvlist_free(fmri);
427 		topo_node_unbind(dtn);
428 		return (NULL);
429 	}
430 
431 	nvlist_free(fmri);
432 
433 	return (dtn);
434 }
435 
436 /*ARGSUSED*/
437 static int
438 disk_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
439     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
440 {
441 	tnode_t		*diskn;
442 	char		*device;
443 	int		err;
444 	disk_di_node_t	*dnode;
445 
446 	if (strcmp(name, DISK) != 0) {
447 		topo_mod_dprintf(mod,
448 		    "Currently only know how to enumerate %s components.\n",
449 		    DISK);
450 		return (-1);
451 	}
452 
453 	if (topo_prop_get_string(rnode, TOPO_BINDING_PGROUP,
454 	    TOPO_BINDING_OCCUPANT, &device, &err) != 0)
455 		return (-1);
456 
457 	if ((dnode = disk_di_node_match_device(device)) == NULL) {
458 		topo_mod_dprintf(mod,
459 		    "No occupant found for bay=%d.\n",
460 		    topo_node_instance(rnode));
461 		topo_mod_strfree(mod, device);
462 		return (-1);
463 	}
464 
465 	diskn = disk_declare(rnode, name, 0, dnode, mod);
466 	if (diskn == NULL) {
467 		topo_mod_dprintf(mod, "Enumeration of %s failed: %s\n",
468 		    DISK, topo_strerror(topo_mod_errno(mod)));
469 		topo_mod_strfree(mod, device);
470 		return (-1); /* mod_errno already set */
471 	}
472 	topo_mod_strfree(mod, device);
473 	return (0);
474 }
475 
476 /*
477  * Query the current disk status.  If successful, the disk status is returned as
478  * an nvlist consisting of at least the following members:
479  *
480  *	protocol	string		Supported protocol (currently "scsi")
481  *
482  *	status		nvlist		Arbitrary protocol-specific information
483  *					about the current state of the disk.
484  *
485  *	faults		nvlist		A list of supported faults.  Each
486  *					element of this list is a boolean value.
487  *					An element's existence indicates that
488  *					the drive supports detecting this fault,
489  *					and the value indicates the current
490  *					state of the fault.
491  *
492  *	<fault-name>	nvlist		For each fault named in 'faults', a
493  *					nvlist describing protocol-specific
494  *					attributes of the fault.
495  *
496  * This method relies on the libdiskstatus library to query this information.
497  */
498 static int
499 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
500     nvlist_t *in_nvl, nvlist_t **out_nvl)
501 {
502 	disk_status_t *dsp;
503 	char *devpath, *fullpath;
504 	size_t pathlen;
505 	int err;
506 	nvlist_t *status;
507 	*out_nvl = NULL;
508 
509 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
510 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
511 
512 	/*
513 	 * If the caller specifies the "path" parameter, then this indicates
514 	 * that we should use this instead of deriving it from the topo node
515 	 * itself.
516 	 */
517 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
518 		devpath = NULL;
519 	} else {
520 		/*
521 		 * Get the /devices path and attempt to open the disk status
522 		 * handle.
523 		 */
524 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
525 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
526 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
527 
528 		/*
529 		 * Note that sizeof(string) includes the terminating NULL byte
530 		 */
531 		pathlen = strlen(devpath) + sizeof ("/devices") +
532 		    sizeof (PHYS_EXTN) - 1;
533 
534 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
535 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
536 
537 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
538 		    PHYS_EXTN);
539 
540 		topo_mod_strfree(mod, devpath);
541 	}
542 
543 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
544 		if (devpath)
545 			topo_mod_free(mod, fullpath, pathlen);
546 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
547 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
548 	}
549 
550 	if (devpath)
551 		topo_mod_free(mod, fullpath, pathlen);
552 
553 	if ((status = disk_status_get(dsp)) == NULL) {
554 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
555 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
556 		disk_status_close(dsp);
557 		return (topo_mod_seterrno(mod, err));
558 	}
559 
560 	*out_nvl = status;
561 	disk_status_close(dsp);
562 	return (0);
563 }
564 
565 /* di_devlink callback for disk_drvinst2devpath */
566 static int
567 disk_drvinst2devpath_devlink_callback(di_devlink_t dl, void *arg)
568 {
569 	char	**devpathp = (char **)arg;
570 	char	*devpath = (char *)di_devlink_path(dl);
571 
572 	*devpathp = strdup(devpath);
573 	return (DI_WALK_TERMINATE);
574 }
575 
576 static disk_di_node_t *
577 disk_di_node_add(int *instancep, char *devid, di_node_t node, topo_mod_t *mod)
578 {
579 	int		mlen;
580 	char		*devpath, *minorpath;
581 	char		*extn = ":a";
582 	disk_di_node_t		*dnode;
583 
584 	(void) pthread_mutex_lock(&(disk_di_nodes.disk_di_nodes_lock));
585 	for (dnode = topo_list_next(&(disk_di_nodes.disk_di_nodes_list));
586 	    dnode != NULL; dnode = topo_list_next(dnode)) {
587 		if (strcmp(dnode->ddn_devid, devid) == 0) {
588 			topo_mod_dprintf(mod,
589 			    "disk_node_add - already there %s\n", devid);
590 			(void) pthread_mutex_unlock(
591 			    &disk_di_nodes.disk_di_nodes_lock);
592 			return (dnode);	/* return existing node */
593 		}
594 	}
595 
596 	if ((dnode = topo_mod_alloc(mod, sizeof (disk_di_node_t))) == NULL) {
597 		topo_mod_dprintf(mod,
598 		    "disk_node_add - topo_mod_alloc failed\n");
599 		(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
600 		return (NULL);	/* return existing node */
601 	}
602 
603 	dnode->ddn_devid = strdup(devid);
604 	dnode->ddn_instance = *instancep;
605 	dnode->ddn_node = node;
606 	dnode->ddn_dpath  = di_devfs_path(node);
607 
608 	mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
609 	minorpath = topo_mod_alloc(mod, mlen);
610 	(void) snprintf(minorpath, mlen, "%s%s", dnode->ddn_dpath,
611 	    extn);
612 	/* walk devlink looking for node that maps to /device path */
613 	devpath = NULL;
614 	(void) di_devlink_walk(devlink_hdl, "^dsk/",
615 	    minorpath, DI_PRIMARY_LINK,
616 	    (void *)&devpath, disk_drvinst2devpath_devlink_callback);
617 	topo_mod_free(mod, minorpath, mlen);
618 	dnode->ddn_lpath = devpath;
619 
620 	topo_list_append(&disk_di_nodes.disk_di_nodes_list, (void *)dnode);
621 	(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
622 
623 	topo_mod_dprintf(mod,
624 	    "disk_node_add - adding %s inst: %d\n",
625 	    dnode->ddn_devid, *instancep);
626 	*instancep = (*instancep) + 1;
627 	return (dnode);
628 }
629 
630 /*ARGSUSED*/
631 static int
632 disk_walk_di_nodes(di_node_t node, void *arg)
633 {
634 	ddi_devid_t	devid = NULL;
635 	char		*devidstr;
636 	static int	instance_devid = 0;
637 	topo_mod_t	*mod = (topo_mod_t *)arg;
638 
639 	/* only interested in nodes that have devids */
640 	devid = (ddi_devid_t)di_devid(node);
641 	if (devid == NULL)
642 		return (DI_WALK_CONTINUE);
643 
644 	/* ... with a string representation of the devid */
645 	devidstr = devid_str_encode(devid, NULL);
646 	if (devidstr == NULL)
647 		return (DI_WALK_CONTINUE);
648 
649 	/* create/find the devid scsi topology node */
650 	(void) disk_di_node_add(&instance_devid, devidstr, node, mod);
651 	devid_str_free(devidstr);
652 	return (DI_WALK_CONTINUE);
653 }
654 
655 /*ARGSUSED*/
656 int
657 _topo_init(topo_mod_t *mod, topo_version_t version)
658 {
659 	di_node_t	devtree;
660 
661 	/*
662 	 * Turn on module debugging output
663 	 */
664 	if (getenv("TOPODISKDEBUG") != NULL)
665 		topo_mod_setdebug(mod);
666 	topo_mod_dprintf(mod, "initializing %s enumerator\n", DISK);
667 
668 	if (topo_mod_register(mod, &disk_info, TOPO_VERSION) != 0) {
669 		topo_mod_dprintf(mod, "%s registration failed: %s\n",
670 		    DISK, topo_mod_errmsg(mod));
671 		return (-1); /* mod errno already set */
672 	}
673 
674 	(void) pthread_mutex_init(&disk_di_nodes.disk_di_nodes_lock, NULL);
675 	disk_di_nodes.disk_di_nodes_list.l_next = NULL;
676 	disk_di_nodes.disk_di_nodes_list.l_prev = NULL;
677 
678 	devtree = di_init("/", DINFOCACHE);
679 	/* we don't get all the nodes with topo_mod_devinfo */
680 	if (devtree == NULL) {
681 		topo_mod_unregister(mod);
682 		(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
683 		topo_mod_dprintf(mod, "topo_mod_devinfo init failed.");
684 		return (-1);
685 	}
686 	/* walk the tree to get the devids */
687 	devlink_hdl = di_devlink_init(NULL, 0);
688 	if (devlink_hdl == DI_NODE_NIL) {
689 		topo_mod_unregister(mod);
690 		(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
691 		topo_mod_dprintf(mod, "di_devlink init failed.");
692 		return (-1);
693 	}
694 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, mod,
695 	    disk_walk_di_nodes);
696 
697 	if (devlink_hdl != NULL)
698 		(void) di_devlink_fini(&devlink_hdl);
699 
700 	topo_mod_dprintf(mod, "%s enumerator initialized\n", DISK);
701 
702 	return (0);
703 }
704 
705 void
706 _topo_fini(topo_mod_t *mod)
707 {
708 	disk_di_node_t	*dnode;
709 
710 	(void) pthread_mutex_lock(&disk_di_nodes.disk_di_nodes_lock);
711 	while ((dnode = topo_list_next(&(disk_di_nodes.disk_di_nodes_list)))
712 	    != NULL) {
713 		free(dnode->ddn_lpath);
714 		free(dnode->ddn_dpath);
715 		free(dnode->ddn_devid);
716 		topo_list_delete(&(disk_di_nodes.disk_di_nodes_list),
717 		    (void *)dnode);
718 		topo_mod_free(mod, dnode, sizeof (disk_di_node_t));
719 	}
720 	(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
721 	(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
722 	topo_mod_unregister(mod);
723 }
724