xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/disk/disk.c (revision 7247f8883be6bcac5fe4735b6f87f873387dbbef)
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 	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 	topo_prop_get_string(parent, TOPO_BINDING_PGROUP, TOPO_BINDING_OCCUPANT,
253 	    &device, err);
254 
255 	dnode = topo_node_getspecific(dtn);
256 
257 	/* set the protocol group properties */
258 	disk_set_proto_props(mod, dtn, inst);
259 
260 	/* create/set the authority group */
261 	if (topo_pgroup_create(dtn, &disk_auth_pgroup, err) == 0) {
262 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
263 		    FM_FMRI_AUTH_PRODUCT, err);
264 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
265 		    FM_FMRI_AUTH_CHASSIS, err);
266 		(void) topo_prop_inherit(dtn, FM_FMRI_AUTHORITY,
267 		    FM_FMRI_AUTH_SERVER, err);
268 	}
269 
270 	/* create/set the devfs-path in the io group */
271 	(void) topo_pgroup_create(dtn, &io_pgroup, err);
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 	/* create the storage group */
278 	(void) topo_pgroup_create(dtn, &storage_pgroup, err);
279 
280 	/* set the storage group properties */
281 	ptr = strrchr(dnode->ddn_lpath, '/');
282 	ptr1 = strchr(ptr, 's');
283 	if (ptr1)
284 		*ptr1 = '\0';
285 	(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
286 	    TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
287 	    ptr+1, err);
288 	if (ptr1)
289 		*ptr1 = 's';
290 
291 
292 	/* populate the storage group properties */
293 	if (model) {
294 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
295 		    TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE, model, err);
296 	}
297 	if (manuf) {
298 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
299 		    TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE, manuf,
300 		    err);
301 	}
302 	if (serial) {
303 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
304 		    TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE, serial, err);
305 	}
306 	if (firm) {
307 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
308 		    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, firm, err);
309 	}
310 	if (cap) {
311 		(void) topo_prop_set_string(dtn, TOPO_STORAGE_PGROUP,
312 		    TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE, cap, err);
313 	}
314 }
315 
316 /* create the disk topo node */
317 /*ARGSUSED*/
318 static tnode_t *
319 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
320     const char *name, topo_instance_t i, char *model, char *manuf,
321     char *serial, char *firm, char *cap, void *priv)
322 {
323 	int		err, len = 0;
324 	nvlist_t	*fmri;
325 	tnode_t		*dtn;
326 	char 		*mm = NULL;
327 	char		*s;
328 	nvlist_t	*auth = topo_mod_auth(mod, parent);
329 
330 	if ((s = strchr(model, ' ')) != NULL) {
331 		*s = '-';
332 	}
333 	len = strlen(manuf) + strlen(model) + 2;
334 	if ((mm = topo_mod_alloc(mod, len)) != NULL)
335 		(void) snprintf(mm, len, "%s-%s", manuf, model);
336 	else
337 		mm = model;
338 
339 	fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
340 	    NULL, auth, mm, firm, serial);
341 
342 	nvlist_free(auth);
343 
344 	if (mm != model)
345 		topo_mod_free(mod, mm, len);
346 	else if (*s != NULL)
347 		*s = ' ';
348 
349 	if (fmri == NULL) {
350 		topo_mod_dprintf(mod,
351 		    "Unable to make nvlist for %s bind: %s.\n",
352 		    name, topo_mod_errmsg(mod));
353 		return (NULL);
354 	}
355 
356 	if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
357 		topo_mod_dprintf(mod,
358 		    "topo_node_bind (%s%d/%s%d) failed: %s\n",
359 		    topo_node_name(parent), topo_node_instance(parent),
360 		    name, i,
361 		    topo_strerror(topo_mod_errno(mod)));
362 		nvlist_free(fmri);
363 		return (NULL);
364 	}
365 	nvlist_free(fmri);
366 	topo_node_setspecific(dtn, priv);
367 
368 	/* add the properties of the disk */
369 	disk_set_props(dtn, parent, model, manuf, serial, firm, cap,
370 	    &err, mod);
371 
372 	return (dtn);
373 }
374 
375 /*ARGSUSED*/
376 static tnode_t *
377 disk_declare(tnode_t *parent, const char *name, topo_instance_t i,
378     void *priv, topo_mod_t *mod)
379 {
380 	tnode_t	*dtn;
381 	int	err;
382 	char	*func = "disk_declare";
383 	char	*model = NULL, *manuf = NULL, *serial = NULL;
384 	char	*cap = NULL, *firm = NULL;
385 	disk_di_node_t		*dnode  = (disk_di_node_t *)priv;
386 	nvlist_t		*fmri;
387 
388 	disk_storage_info(mod, dnode,
389 	    &model, &manuf, &serial, &firm, &cap);
390 
391 	/* create the node */
392 	dtn = disk_tnode_create(mod, parent,
393 	    name, i, model, manuf, serial, firm, cap, priv);
394 
395 	topo_mod_strfree(mod, model);
396 	topo_mod_strfree(mod, manuf);
397 	topo_mod_strfree(mod, serial);
398 	topo_mod_strfree(mod, firm);
399 	topo_mod_strfree(mod, cap);
400 
401 	if (dtn == NULL) {
402 		return (NULL);
403 	}
404 
405 	/* set the parent fru */
406 	if (topo_node_resource(parent, &fmri, &err) != 0) {
407 		topo_mod_dprintf(mod,
408 		    "%s: topo_node_resource error: %s\n", func,
409 		    topo_strerror(err));
410 		topo_node_unbind(dtn);
411 		return (NULL);
412 	}
413 	if (topo_node_fru_set(parent, fmri, 0, &err) != 0) {
414 		topo_mod_dprintf(mod, "%s topo_node_fru error: %s\n",
415 		    func, topo_strerror(err));
416 		nvlist_free(fmri);
417 		topo_node_unbind(dtn);
418 		return (NULL);
419 	}
420 
421 	if (topo_method_register(mod, dtn, disk_methods) != 0) {
422 		topo_mod_dprintf(mod,
423 		    "topo_method_register failed: %s\n",
424 		    topo_strerror(topo_mod_errno(mod)));
425 		nvlist_free(fmri);
426 		topo_node_unbind(dtn);
427 		return (NULL);
428 	}
429 
430 	nvlist_free(fmri);
431 
432 	return (dtn);
433 }
434 
435 /*ARGSUSED*/
436 static int
437 disk_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
438     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
439 {
440 	tnode_t		*diskn;
441 	char		*device;
442 	int		err;
443 	disk_di_node_t	*dnode;
444 
445 	if (strcmp(name, DISK) != 0) {
446 		topo_mod_dprintf(mod,
447 		    "Currently only know how to enumerate %s components.\n",
448 		    DISK);
449 		return (-1);
450 	}
451 
452 	topo_prop_get_string(rnode, TOPO_BINDING_PGROUP, TOPO_BINDING_OCCUPANT,
453 	    &device, &err);
454 
455 	if ((dnode = disk_di_node_match_device(device)) == NULL) {
456 		topo_mod_dprintf(mod,
457 		    "No occupant found for bay=%d.\n",
458 		    topo_node_instance(rnode));
459 		topo_mod_strfree(mod, device);
460 		return (-1);
461 	}
462 
463 	diskn = disk_declare(rnode, name, 0, dnode, mod);
464 	if (diskn == NULL) {
465 		topo_mod_dprintf(mod, "Enumeration of %s failed: %s\n",
466 		    DISK, topo_strerror(topo_mod_errno(mod)));
467 		topo_mod_strfree(mod, device);
468 		return (-1); /* mod_errno already set */
469 	}
470 	topo_mod_strfree(mod, device);
471 	return (0);
472 }
473 
474 /*
475  * Query the current disk status.  If successful, the disk status is returned as
476  * an nvlist consisting of at least the following members:
477  *
478  *	protocol	string		Supported protocol (currently "scsi")
479  *
480  *	status		nvlist		Arbitrary protocol-specific information
481  *					about the current state of the disk.
482  *
483  *	faults		nvlist		A list of supported faults.  Each
484  *					element of this list is a boolean value.
485  *					An element's existence indicates that
486  *					the drive supports detecting this fault,
487  *					and the value indicates the current
488  *					state of the fault.
489  *
490  *	<fault-name>	nvlist		For each fault named in 'faults', a
491  *					nvlist describing protocol-specific
492  *					attributes of the fault.
493  *
494  * This method relies on the libdiskstatus library to query this information.
495  */
496 static int
497 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
498     nvlist_t *in_nvl, nvlist_t **out_nvl)
499 {
500 	disk_status_t *dsp;
501 	char *devpath, *fullpath;
502 	size_t pathlen;
503 	int err;
504 	nvlist_t *status;
505 	*out_nvl = NULL;
506 
507 	if (vers != TOPO_METH_DISK_STATUS_VERSION)
508 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
509 
510 	/*
511 	 * If the caller specifies the "path" parameter, then this indicates
512 	 * that we should use this instead of deriving it from the topo node
513 	 * itself.
514 	 */
515 	if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
516 		devpath = NULL;
517 	} else {
518 		/*
519 		 * Get the /devices path and attempt to open the disk status
520 		 * handle.
521 		 */
522 		if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
523 		    TOPO_IO_DEV_PATH, &devpath, &err) != 0)
524 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
525 
526 		/*
527 		 * Note that sizeof(string) includes the terminating NULL byte
528 		 */
529 		pathlen = strlen(devpath) + sizeof ("/devices") +
530 		    sizeof (PHYS_EXTN) - 1;
531 
532 		if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
533 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
534 
535 		(void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
536 		    PHYS_EXTN);
537 
538 		topo_mod_strfree(mod, devpath);
539 	}
540 
541 	if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
542 		if (devpath)
543 			topo_mod_free(mod, fullpath, pathlen);
544 		return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
545 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP));
546 	}
547 
548 	if (devpath)
549 		topo_mod_free(mod, fullpath, pathlen);
550 
551 	if ((status = disk_status_get(dsp)) == NULL) {
552 		err = (disk_status_errno(dsp) == EDS_NOMEM ?
553 		    EMOD_NOMEM : EMOD_METHOD_NOTSUP);
554 		disk_status_close(dsp);
555 		return (topo_mod_seterrno(mod, err));
556 	}
557 
558 	*out_nvl = status;
559 	disk_status_close(dsp);
560 	return (0);
561 }
562 
563 /* di_devlink callback for disk_drvinst2devpath */
564 static int
565 disk_drvinst2devpath_devlink_callback(di_devlink_t dl, void *arg)
566 {
567 	char	**devpathp = (char **)arg;
568 	char	*devpath = (char *)di_devlink_path(dl);
569 
570 	*devpathp = strdup(devpath);
571 	return (DI_WALK_TERMINATE);
572 }
573 
574 static disk_di_node_t *
575 disk_di_node_add(int *instancep, char *devid, di_node_t node, topo_mod_t *mod)
576 {
577 	int		mlen;
578 	char		*devpath, *minorpath;
579 	char		*extn = ":a";
580 	disk_di_node_t		*dnode;
581 
582 	(void) pthread_mutex_lock(&(disk_di_nodes.disk_di_nodes_lock));
583 	for (dnode = topo_list_next(&(disk_di_nodes.disk_di_nodes_list));
584 	    dnode != NULL; dnode = topo_list_next(dnode)) {
585 		if (strcmp(dnode->ddn_devid, devid) == 0) {
586 			topo_mod_dprintf(mod,
587 			    "disk_node_add - already there %s\n", devid);
588 			(void) pthread_mutex_unlock(
589 			    &disk_di_nodes.disk_di_nodes_lock);
590 			return (dnode);	/* return existing node */
591 		}
592 	}
593 
594 	if ((dnode = topo_mod_alloc(mod, sizeof (disk_di_node_t))) == NULL) {
595 		topo_mod_dprintf(mod,
596 		    "disk_node_add - topo_mod_alloc failed\n");
597 		(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
598 		return (NULL);	/* return existing node */
599 	}
600 
601 	dnode->ddn_devid = strdup(devid);
602 	dnode->ddn_instance = *instancep;
603 	dnode->ddn_node = node;
604 	dnode->ddn_dpath  = di_devfs_path(node);
605 
606 	mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
607 	minorpath = topo_mod_alloc(mod, mlen);
608 	(void) snprintf(minorpath, mlen, "%s%s", dnode->ddn_dpath,
609 	    extn);
610 	/* walk devlink looking for node that maps to /device path */
611 	devpath = NULL;
612 	(void) di_devlink_walk(devlink_hdl, "^dsk/",
613 	    minorpath, DI_PRIMARY_LINK,
614 	    (void *)&devpath, disk_drvinst2devpath_devlink_callback);
615 	topo_mod_free(mod, minorpath, mlen);
616 	dnode->ddn_lpath = devpath;
617 
618 	topo_list_append(&disk_di_nodes.disk_di_nodes_list, (void *)dnode);
619 	(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
620 
621 	topo_mod_dprintf(mod,
622 	    "disk_node_add - adding %s inst: %d\n",
623 	    dnode->ddn_devid, *instancep);
624 	*instancep = (*instancep) + 1;
625 	return (dnode);
626 }
627 
628 /*ARGSUSED*/
629 static int
630 disk_walk_di_nodes(di_node_t node, void *arg)
631 {
632 	ddi_devid_t	devid = NULL;
633 	char		*devidstr;
634 	static int	instance_devid = 0;
635 	topo_mod_t	*mod = (topo_mod_t *)arg;
636 
637 	/* only interested in nodes that have devids */
638 	devid = (ddi_devid_t)di_devid(node);
639 	if (devid == NULL)
640 		return (DI_WALK_CONTINUE);
641 
642 	/* ... with a string representation of the devid */
643 	devidstr = devid_str_encode(devid, NULL);
644 	if (devidstr == NULL)
645 		return (DI_WALK_CONTINUE);
646 
647 	/* create/find the devid scsi topology node */
648 	(void) disk_di_node_add(&instance_devid, devidstr, node, mod);
649 	devid_str_free(devidstr);
650 	return (DI_WALK_CONTINUE);
651 }
652 
653 /*ARGSUSED*/
654 int
655 _topo_init(topo_mod_t *mod, topo_version_t version)
656 {
657 	di_node_t	devtree;
658 
659 	/*
660 	 * Turn on module debugging output
661 	 */
662 	if (getenv("TOPODISKDEBUG") != NULL)
663 		topo_mod_setdebug(mod);
664 	topo_mod_dprintf(mod, "initializing %s enumerator\n", DISK);
665 
666 	if (topo_mod_register(mod, &disk_info, TOPO_VERSION) != 0) {
667 		topo_mod_dprintf(mod, "%s registration failed: %s\n",
668 		    DISK, topo_mod_errmsg(mod));
669 		return (-1); /* mod errno already set */
670 	}
671 
672 	(void) pthread_mutex_init(&disk_di_nodes.disk_di_nodes_lock, NULL);
673 	disk_di_nodes.disk_di_nodes_list.l_next = NULL;
674 	disk_di_nodes.disk_di_nodes_list.l_prev = NULL;
675 
676 	devtree = di_init("/", DINFOCACHE);
677 	/* we don't get all the nodes with topo_mod_devinfo */
678 	if (devtree == NULL) {
679 		topo_mod_unregister(mod);
680 		(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
681 		topo_mod_dprintf(mod, "topo_mod_devinfo init failed.");
682 		return (-1);
683 	}
684 	/* walk the tree to get the devids */
685 	devlink_hdl = di_devlink_init(NULL, 0);
686 	if (devlink_hdl == DI_NODE_NIL) {
687 		topo_mod_unregister(mod);
688 		(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
689 		topo_mod_dprintf(mod, "di_devlink init failed.");
690 		return (-1);
691 	}
692 	(void) di_walk_node(devtree, DI_WALK_CLDFIRST, mod,
693 	    disk_walk_di_nodes);
694 
695 	if (devlink_hdl != NULL)
696 		(void) di_devlink_fini(&devlink_hdl);
697 
698 	topo_mod_dprintf(mod, "%s enumerator initialized\n", DISK);
699 
700 	return (0);
701 }
702 
703 void
704 _topo_fini(topo_mod_t *mod)
705 {
706 	disk_di_node_t	*dnode;
707 
708 	(void) pthread_mutex_lock(&disk_di_nodes.disk_di_nodes_lock);
709 	while ((dnode = topo_list_next(&(disk_di_nodes.disk_di_nodes_list)))
710 	    != NULL) {
711 		free(dnode->ddn_lpath);
712 		free(dnode->ddn_dpath);
713 		free(dnode->ddn_devid);
714 		topo_list_delete(&(disk_di_nodes.disk_di_nodes_list),
715 		    (void *)dnode);
716 		topo_mod_free(mod, dnode, sizeof (disk_di_node_t));
717 	}
718 	(void) pthread_mutex_unlock(&disk_di_nodes.disk_di_nodes_lock);
719 	(void) pthread_mutex_destroy(&disk_di_nodes.disk_di_nodes_lock);
720 	topo_mod_unregister(mod);
721 }
722