xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ses/ses.c (revision a724c049b7e0dd8612bc3aaec84e96e80511050d)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <dirent.h>
28 #include <devid.h>
29 #include <fm/libdiskstatus.h>
30 #include <inttypes.h>
31 #include <pthread.h>
32 #include <strings.h>
33 #include <unistd.h>
34 #include <sys/dkio.h>
35 #include <sys/fm/protocol.h>
36 #include <sys/scsi/scsi_types.h>
37 
38 #include "disk.h"
39 #include "ses.h"
40 
41 #define	SES_VERSION	1
42 
43 #define	SES_SNAP_FREQ		1000	/* in milliseconds */
44 
45 #define	SES_STATUS_UNAVAIL(s)	\
46 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
47 
48 /*
49  * Because multiple SES targets can be part of a single chassis, we construct
50  * our own hierarchy that takes this into account.  These SES targets may refer
51  * to the same devices (multiple paths) or to different devices (managing
52  * different portions of the space).  We arrange things into a
53  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
54  * nodes found so far.
55  */
56 typedef struct ses_alt_node {
57 	topo_list_t		san_link;
58 	ses_node_t		*san_node;
59 } ses_alt_node_t;
60 
61 typedef struct ses_enum_node {
62 	topo_list_t		sen_link;
63 	ses_node_t		*sen_node;
64 	topo_list_t		sen_alt_nodes;
65 	uint64_t		sen_type;
66 	uint64_t		sen_instance;
67 	ses_enum_target_t	*sen_target;
68 } ses_enum_node_t;
69 
70 typedef struct ses_enum_chassis {
71 	topo_list_t		sec_link;
72 	topo_list_t		sec_nodes;
73 	topo_list_t		sec_targets;
74 	const char		*sec_csn;
75 	ses_node_t		*sec_enclosure;
76 	ses_enum_target_t	*sec_target;
77 	topo_instance_t		sec_instance;
78 	boolean_t		sec_hasdev;
79 	boolean_t		sec_internal;
80 } ses_enum_chassis_t;
81 
82 typedef struct ses_enum_data {
83 	topo_list_t		sed_disks;
84 	topo_list_t		sed_chassis;
85 	ses_enum_chassis_t	*sed_current;
86 	ses_enum_target_t	*sed_target;
87 	int			sed_errno;
88 	char			*sed_name;
89 	topo_mod_t		*sed_mod;
90 	topo_instance_t		sed_instance;
91 } ses_enum_data_t;
92 
93 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
94     nvlist_t **);
95 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
96     nvlist_t **);
97 
98 static const topo_method_t ses_component_methods[] = {
99 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
100 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
101 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
102 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
103 	{ NULL }
104 };
105 
106 static const topo_method_t ses_bay_methods[] = {
107 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
108 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
109 	{ NULL }
110 };
111 
112 static const topo_method_t ses_enclosure_methods[] = {
113 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
114 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
115 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
116 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
117 	{ NULL }
118 };
119 
120 static void
121 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
122 {
123 	if (--stp->set_refcount == 0) {
124 		ses_snap_rele(stp->set_snap);
125 		ses_close(stp->set_target);
126 		topo_mod_strfree(mod, stp->set_devpath);
127 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
128 	}
129 }
130 
131 static void
132 ses_data_free(ses_enum_data_t *sdp)
133 {
134 	topo_mod_t *mod = sdp->sed_mod;
135 	ses_enum_chassis_t *cp;
136 	ses_enum_node_t *np;
137 	ses_enum_target_t *tp;
138 	ses_alt_node_t *ap;
139 
140 	while ((cp = topo_list_next(&sdp->sed_chassis)) != NULL) {
141 		topo_list_delete(&sdp->sed_chassis, cp);
142 
143 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
144 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
145 			    NULL) {
146 				topo_list_delete(&np->sen_alt_nodes, ap);
147 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
148 			}
149 			topo_list_delete(&cp->sec_nodes, np);
150 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
151 		}
152 
153 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
154 			topo_list_delete(&cp->sec_targets, tp);
155 			ses_target_free(mod, tp);
156 		}
157 
158 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
159 	}
160 
161 	disk_list_free(mod, &sdp->sed_disks);
162 	topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
163 }
164 
165 /*
166  * For enclosure nodes, we have a special contains method.  By default, the hc
167  * walker will compare the node name and instance number to determine if an
168  * FMRI matches.  For enclosures where the enumeration order is impossible to
169  * predict, we instead use the chassis-id as a unique identifier, and ignore
170  * the instance number.
171  */
172 static int
173 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
174 {
175 	uint8_t v1, v2;
176 	nvlist_t **hcp1, **hcp2;
177 	int err, i;
178 	uint_t nhcp1, nhcp2;
179 	nvlist_t *a1, *a2;
180 	char *c1, *c2;
181 	int mindepth;
182 
183 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
184 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
185 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
186 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
187 
188 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
189 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
190 	if (err != 0)
191 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
192 
193 	/*
194 	 * If the chassis-id doesn't match, then these FMRIs are not
195 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
196 	 * have no choice but to fall back to the instance ID.
197 	 */
198 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
199 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
200 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
201 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
202 		if (strcmp(c1, c2) != 0)
203 			return (0);
204 
205 		mindepth = 1;
206 	} else {
207 		mindepth = 0;
208 	}
209 
210 	if (nhcp2 < nhcp1)
211 		return (0);
212 
213 	for (i = 0; i < nhcp1; i++) {
214 		char *nm1 = NULL;
215 		char *nm2 = NULL;
216 		char *id1 = NULL;
217 		char *id2 = NULL;
218 
219 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
220 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
221 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
222 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
223 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
224 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
225 
226 		if (strcmp(nm1, nm2) == 0 &&
227 		    (i < mindepth || strcmp(id1, id2) == 0))
228 			continue;
229 
230 		return (0);
231 	}
232 
233 	return (1);
234 }
235 
236 /*ARGSUSED*/
237 static int
238 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
239     nvlist_t *in, nvlist_t **out)
240 {
241 	int ret;
242 	nvlist_t *nv1, *nv2;
243 
244 	if (version > TOPO_METH_CONTAINS_VERSION)
245 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
246 
247 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
248 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
249 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
250 
251 	ret = fmri_contains(mod, nv1, nv2);
252 	if (ret < 0)
253 		return (-1);
254 
255 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
256 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
257 		    ret) == 0)
258 			return (0);
259 		else
260 			nvlist_free(*out);
261 	}
262 
263 	return (-1);
264 
265 }
266 
267 /*
268  * Return a current instance of the node.  This is somewhat complicated because
269  * we need to take a new snapshot in order to get the new data, but we don't
270  * want to be constantly taking SES snapshots if the consumer is going to do a
271  * series of queries.  So we adopt the strategy of assuming that the SES state
272  * is not going to be rapidly changing, and limit our snapshot frequency to
273  * some defined bounds.
274  */
275 ses_node_t *
276 ses_node_lock(topo_mod_t *mod, tnode_t *tn)
277 {
278 	struct timeval tv;
279 	ses_enum_target_t *tp = topo_node_getspecific(tn);
280 	uint64_t prev, now;
281 	ses_snap_t *snap;
282 	int err;
283 	uint64_t nodeid;
284 	ses_node_t *np;
285 
286 	if (tp == NULL) {
287 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
288 		return (NULL);
289 	}
290 
291 	(void) pthread_mutex_lock(&tp->set_lock);
292 
293 	/*
294 	 * Determine if we need to take a new snapshot.
295 	 */
296 	if (gettimeofday(&tv, NULL) != 0) {
297 		tv.tv_sec = time(NULL);
298 		tv.tv_usec = 0;
299 	}
300 
301 	now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
302 	prev = tp->set_snaptime.tv_sec * 1000 +
303 	    tp->set_snaptime.tv_usec / 1000;
304 
305 	if (now - prev > SES_SNAP_FREQ &&
306 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
307 		if (ses_snap_generation(snap) !=
308 		    ses_snap_generation(tp->set_snap)) {
309 			/*
310 			 * If we find ourselves in this situation, we're in
311 			 * trouble.  The generation count has changed, which
312 			 * indicates that our current topology is out of date.
313 			 * But we need to consult the new topology in order to
314 			 * determine presence at this moment in time.  We can't
315 			 * go back and change the topo snapshot in situ, so
316 			 * we'll just have to fail the call in this unlikely
317 			 * scenario.
318 			 */
319 			ses_snap_rele(snap);
320 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
321 			(void) pthread_mutex_unlock(&tp->set_lock);
322 			return (NULL);
323 		} else {
324 			ses_snap_rele(tp->set_snap);
325 			tp->set_snap = snap;
326 		}
327 		tp->set_snaptime = tv;
328 	}
329 
330 	snap = tp->set_snap;
331 
332 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
333 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
334 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
335 
336 	return (np);
337 }
338 
339 /*ARGSUSED*/
340 void
341 ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
342 {
343 	ses_enum_target_t *tp = topo_node_getspecific(tn);
344 
345 	verify(tp != NULL);
346 
347 	(void) pthread_mutex_unlock(&tp->set_lock);
348 }
349 
350 /*
351  * Determine if the element is present.
352  */
353 /*ARGSUSED*/
354 static int
355 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
356     nvlist_t *in, nvlist_t **out)
357 {
358 	boolean_t present;
359 	ses_node_t *np;
360 	nvlist_t *props, *nvl;
361 	uint64_t status;
362 
363 	if ((np = ses_node_lock(mod, tn)) == NULL)
364 		return (-1);
365 
366 	verify((props = ses_node_props(np)) != NULL);
367 	verify(nvlist_lookup_uint64(props,
368 	    SES_PROP_STATUS_CODE, &status) == 0);
369 
370 	ses_node_unlock(mod, tn);
371 
372 	present = (status != SES_ESC_NOT_INSTALLED);
373 
374 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
375 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
376 
377 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
378 	    present) != 0) {
379 		nvlist_free(nvl);
380 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
381 	}
382 
383 	*out = nvl;
384 
385 	return (0);
386 }
387 
388 /*
389  * Sets standard properties for a ses node (enclosure or bay).  This includes
390  * setting the FRU to be the same as the resource, as well as setting the
391  * authority information.
392  */
393 static int
394 ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth,
395     uint64_t nodeid, const char *path)
396 {
397 	int err;
398 	char *product, *chassis;
399 	nvlist_t *fmri;
400 	topo_pgroup_info_t pgi;
401 
402 	/*
403 	 * Set the authority explicitly if specified.
404 	 */
405 	if (auth) {
406 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
407 		    &product) == 0);
408 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
409 		    &chassis) == 0);
410 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
411 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
412 		    &err) != 0 ||
413 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
414 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
415 		    &err) != 0 ||
416 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
417 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
418 		    &err) != 0) {
419 			topo_mod_dprintf(mod, "failed to add authority "
420 			    "properties: %s\n", topo_strerror(err));
421 			return (topo_mod_seterrno(mod, err));
422 		}
423 	}
424 
425 	/*
426 	 * Copy the resource and set that as the FRU.
427 	 */
428 	if (topo_node_resource(tn, &fmri, &err) != 0) {
429 		topo_mod_dprintf(mod,
430 		    "topo_node_resource() failed : %s\n",
431 		    topo_strerror(err));
432 		return (topo_mod_seterrno(mod, err));
433 	}
434 
435 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
436 		topo_mod_dprintf(mod,
437 		    "topo_node_fru_set() failed : %s\n",
438 		    topo_strerror(err));
439 		nvlist_free(fmri);
440 		return (topo_mod_seterrno(mod, err));
441 	}
442 
443 	nvlist_free(fmri);
444 
445 	/*
446 	 * Set the SES-specific properties so that consumers can query
447 	 * additional information about the particular SES element.
448 	 */
449 	pgi.tpi_name = TOPO_PGROUP_SES;
450 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
451 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
452 	pgi.tpi_version = TOPO_VERSION;
453 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
454 		topo_mod_dprintf(mod, "failed to create propgroup "
455 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
456 		return (-1);
457 	}
458 
459 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
460 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
461 	    nodeid, &err) != 0) {
462 		topo_mod_dprintf(mod,
463 		    "failed to create property %s: %s\n",
464 		    TOPO_PROP_NODE_ID, topo_strerror(err));
465 		return (-1);
466 	}
467 
468 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
469 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
470 	    path, &err) != 0) {
471 		topo_mod_dprintf(mod,
472 		    "failed to create property %s: %s\n",
473 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
474 		return (-1);
475 	}
476 
477 	return (0);
478 }
479 
480 /*
481  * Callback to add a disk to a given bay.  We first check the status-code to
482  * determine if a disk is present, ignoring those that aren't in an appropriate
483  * state.  We then scan the parent bay node's SAS address array to determine
484  * possible attached SAS addresses.  We create a disk node if the disk is not
485  * SAS or the SES target does not support the necessary pages for this; if we
486  * find the SAS address, we create a disk node and also correlate it with
487  * the corresponding Solaris device node to fill in the rest of the data.
488  */
489 static int
490 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
491 {
492 	topo_mod_t *mod = sdp->sed_mod;
493 	uint64_t status;
494 	nvlist_t **sas;
495 	uint_t s, nsas;
496 	char **paths;
497 	int err;
498 
499 	/*
500 	 * Skip devices that are not in a present (and possibly damaged) state.
501 	 */
502 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
503 		return (0);
504 
505 	if (status != SES_ESC_OK &&
506 	    status != SES_ESC_CRITICAL &&
507 	    status != SES_ESC_NONCRITICAL &&
508 	    status != SES_ESC_UNRECOVERABLE &&
509 	    status != SES_ESC_NO_ACCESS)
510 		return (0);
511 
512 	topo_mod_dprintf(mod, "found attached disk");
513 
514 	/*
515 	 * Create the disk range.
516 	 */
517 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
518 		topo_mod_dprintf(mod,
519 		    "topo_node_create_range() failed: %s",
520 		    topo_mod_errmsg(mod));
521 		return (-1);
522 	}
523 
524 	/*
525 	 * Look through all SAS addresses and attempt to correlate them to a
526 	 * known Solaris device.  If we don't find a matching node, then we
527 	 * don't enumerate the disk node.
528 	 */
529 	if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
530 	    &sas, &nsas) != 0)
531 		return (0);
532 
533 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
534 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
535 		return (0);
536 
537 	err = 0;
538 
539 	for (s = 0; s < nsas; s++) {
540 		if (disk_declare_addr(mod, pnode,
541 		    &sdp->sed_disks, paths[s]) != 0 &&
542 		    topo_mod_errno(mod) != EMOD_NODE_BOUND) {
543 			err = -1;
544 			break;
545 		}
546 	}
547 
548 	for (s = 0; s < nsas; s++)
549 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
550 	topo_mod_free(mod, paths, nsas * sizeof (char *));
551 
552 	return (err);
553 }
554 
555 static int
556 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
557 {
558 	ses_alt_node_t *ap;
559 	ses_node_t *np;
560 	nvlist_t *props;
561 
562 	nvlist_t **phys;
563 	uint_t i, j, n_phys, all_phys = 0;
564 	char **paths;
565 	uint64_t addr;
566 	size_t len;
567 	int terr, err = -1;
568 
569 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
570 	    ap = topo_list_next(ap)) {
571 		np = ap->san_node;
572 		props = ses_node_props(np);
573 
574 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
575 		    &phys, &n_phys) != 0)
576 			continue;
577 
578 		all_phys += n_phys;
579 	}
580 
581 	if (all_phys == 0)
582 		return (0);
583 
584 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
585 		return (-1);
586 
587 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
588 	    ap = topo_list_next(ap)) {
589 		np = ap->san_node;
590 		props = ses_node_props(np);
591 
592 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
593 		    &phys, &n_phys) != 0)
594 			continue;
595 
596 		for (j = 0; j < n_phys; j++) {
597 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
598 			    &addr) != 0)
599 				continue;
600 
601 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
602 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
603 				goto error;
604 
605 			(void) snprintf(paths[i], len, "%016llx", addr);
606 
607 			++i;
608 		}
609 	}
610 
611 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
612 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
613 	    (const char **)paths, i, &terr);
614 	if (err != 0)
615 		err = topo_mod_seterrno(mod, terr);
616 
617 error:
618 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
619 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
620 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
621 
622 	return (err);
623 }
624 
625 /*
626  * Callback to create a basic node (bay, psu, fan, or controller).
627  */
628 static int
629 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp,
630     tnode_t *pnode, const char *nodename, const char *labelname)
631 {
632 	ses_node_t *np = snp->sen_node;
633 	ses_node_t *parent;
634 	uint64_t instance = snp->sen_instance;
635 	topo_mod_t *mod = sdp->sed_mod;
636 	nvlist_t *props, *aprops;
637 	nvlist_t *auth = NULL, *fmri = NULL;
638 	tnode_t *tn;
639 	char label[128];
640 	int err;
641 	char *part = NULL, *serial = NULL, *revision = NULL;
642 	char *desc;
643 	boolean_t report;
644 
645 	props = ses_node_props(np);
646 
647 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
648 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
649 
650 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
651 
652 	/*
653 	 * Create the node.  The interesting information is all copied from the
654 	 * parent enclosure node, so there is not much to do.
655 	 */
656 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
657 		goto error;
658 
659 	/*
660 	 * We want to report revision information for the controller nodes, but
661 	 * we do not get per-element revision information.  However, we do have
662 	 * revision information for the entire enclosure, and we can use the
663 	 * 'reported-via' property to know that this controller corresponds to
664 	 * the given revision information.  This means we cannot get revision
665 	 * information for targets we are not explicitly connected to, but
666 	 * there is little we can do about the situation.
667 	 */
668 	if (strcmp(nodename, CONTROLLER) == 0 &&
669 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
670 	    report) {
671 		for (parent = ses_node_parent(np); parent != NULL;
672 		    parent = ses_node_parent(parent)) {
673 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
674 				(void) nvlist_lookup_string(
675 				    ses_node_props(parent),
676 				    SES_EN_PROP_REV, &revision);
677 				break;
678 			}
679 		}
680 	}
681 
682 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
683 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
684 	    serial)) == NULL) {
685 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
686 		    topo_mod_errmsg(mod));
687 		goto error;
688 	}
689 
690 	if ((tn = topo_node_bind(mod, pnode, nodename,
691 	    instance, fmri)) == NULL) {
692 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
693 		    topo_mod_errmsg(mod));
694 		goto error;
695 	}
696 
697 	/*
698 	 * For the node label, we look for the following in order:
699 	 *
700 	 * 	<ses-description>
701 	 * 	<ses-class-description> <instance>
702 	 * 	<default-type-label> <instance>
703 	 */
704 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
705 	    desc[0] == '\0') {
706 		parent = ses_node_parent(np);
707 		aprops = ses_node_props(parent);
708 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
709 		    &desc) == 0 && desc[0] != '\0')
710 			labelname = desc;
711 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
712 		    instance);
713 		desc = label;
714 	}
715 
716 	if (topo_node_label_set(tn, desc, &err) != 0)
717 		goto error;
718 
719 	if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np),
720 	    snp->sen_target->set_devpath) != 0)
721 		goto error;
722 
723 	if (strcmp(nodename, "bay") == 0) {
724 		if (ses_add_bay_props(mod, tn, snp) != 0)
725 			goto error;
726 
727 		if (ses_create_disk(sdp, tn, props) != 0)
728 			goto error;
729 
730 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
731 			topo_mod_dprintf(mod,
732 			    "topo_method_register() failed: %s",
733 			    topo_mod_errmsg(mod));
734 			goto error;
735 		}
736 	} else {
737 		/*
738 		 * Only fan, psu, and controller nodes have a 'present' method.
739 		 * Bay nodes are always present, and disk nodes are present by
740 		 * virtue of being enumerated.
741 		 */
742 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
743 			topo_mod_dprintf(mod,
744 			    "topo_method_register() failed: %s",
745 			    topo_mod_errmsg(mod));
746 			goto error;
747 		}
748 
749 	}
750 
751 	snp->sen_target->set_refcount++;
752 	topo_node_setspecific(tn, snp->sen_target);
753 
754 	nvlist_free(auth);
755 	nvlist_free(fmri);
756 	return (0);
757 
758 error:
759 	nvlist_free(auth);
760 	nvlist_free(fmri);
761 	return (-1);
762 }
763 
764 /*
765  * Instantiate any children of a given type.
766  */
767 static int
768 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
769     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
770     boolean_t dorange)
771 {
772 	topo_mod_t *mod = sdp->sed_mod;
773 	boolean_t found;
774 	uint64_t max;
775 	ses_enum_node_t *snp;
776 
777 	/*
778 	 * First go through and count how many matching nodes we have.
779 	 */
780 	max = 0;
781 	found = B_FALSE;
782 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
783 	    snp = topo_list_next(snp)) {
784 		if (snp->sen_type == type) {
785 			found = B_TRUE;
786 			if (snp->sen_instance > max)
787 				max = snp->sen_instance;
788 		}
789 	}
790 
791 	/*
792 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
793 	 * Since we map both of these to 'disk', if an enclosure does this, we
794 	 * just ignore the array elements.
795 	 */
796 	if (!found ||
797 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
798 		return (0);
799 
800 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
801 	    cp->sec_csn, max, nodename);
802 
803 	if (dorange && topo_node_range_create(mod, pnode,
804 	    nodename, 0, max) != 0) {
805 		topo_mod_dprintf(mod,
806 		    "topo_node_create_range() failed: %s",
807 		    topo_mod_errmsg(mod));
808 		return (-1);
809 	}
810 
811 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
812 	    snp = topo_list_next(snp)) {
813 		if (snp->sen_type == type) {
814 			if (ses_create_generic(sdp, snp, pnode,
815 			    nodename, defaultlabel) != 0)
816 				return (-1);
817 		}
818 	}
819 
820 	return (0);
821 }
822 
823 /*
824  * Instantiate a new chassis instance in the topology.
825  */
826 static int
827 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
828 {
829 	topo_mod_t *mod = sdp->sed_mod;
830 	nvlist_t *props;
831 	char *raw_manufacturer, *raw_model, *raw_revision;
832 	char *manufacturer = NULL, *model = NULL, *product = NULL;
833 	char *revision = NULL;
834 	char *serial;
835 	size_t prodlen;
836 	tnode_t *tn;
837 	nvlist_t *fmri = NULL, *auth = NULL;
838 	int ret = -1;
839 	ses_enum_node_t *snp;
840 
841 	/*
842 	 * Ignore any internal enclosures.
843 	 */
844 	if (cp->sec_internal)
845 		return (0);
846 
847 	/*
848 	 * Check to see if there are any devices presennt in the chassis.  If
849 	 * not, ignore the chassis alltogether.  This is most useful for
850 	 * ignoring internal HBAs that present a SES target but don't actually
851 	 * manage any of the devices.
852 	 */
853 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
854 	    snp = topo_list_next(snp)) {
855 		if (snp->sen_type == SES_ET_DEVICE ||
856 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
857 			break;
858 	}
859 
860 	if (snp == NULL)
861 		return (0);
862 
863 	props = ses_node_props(cp->sec_enclosure);
864 
865 	/*
866 	 * We use the following property mappings:
867 	 *
868 	 * 	manufacturer		vendor-id
869 	 * 	model			product-id
870 	 * 	serial-number		libses-chassis-serial
871 	 */
872 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
873 	    &raw_manufacturer) == 0);
874 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
875 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
876 	    &raw_revision) == 0);
877 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
878 
879 	/*
880 	 * To construct the authority information, we 'clean' each string by
881 	 * removing any offensive characters and trimmming whitespace.  For the
882 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
883 	 * also take the numerical serial number and convert it to a string.
884 	 */
885 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
886 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
887 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
888 		goto error;
889 	}
890 
891 	prodlen = strlen(manufacturer) + strlen(model) + 2;
892 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
893 		goto error;
894 
895 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
896 
897 	/*
898 	 * Construct the topo node and bind it to our parent.
899 	 */
900 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
901 		goto error;
902 
903 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
904 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
905 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
906 		goto error;
907 	}
908 
909 	/*
910 	 * We pass NULL for the parent FMRI because there is no resource
911 	 * associated with it.  For the toplevel enclosure, we leave the
912 	 * serial/part/revision portions empty, which are reserved for
913 	 * individual components within the chassis.
914 	 */
915 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
916 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
917 	    model, revision, serial)) == NULL) {
918 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
919 		    topo_mod_errmsg(mod));
920 		goto error;
921 	}
922 
923 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
924 	    cp->sec_instance, fmri)) == NULL) {
925 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
926 		    topo_mod_errmsg(mod));
927 		goto error;
928 	}
929 
930 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
931 		topo_mod_dprintf(mod,
932 		    "topo_method_register() failed: %s",
933 		    topo_mod_errmsg(mod));
934 		goto error;
935 	}
936 
937 	if (ses_set_standard_props(mod, tn, auth,
938 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
939 		goto error;
940 
941 	/*
942 	 * Create the nodes for power supplies, fans, and devices.
943 	 */
944 	if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
945 	    PSU, "PSU", cp, B_TRUE) != 0 ||
946 	    ses_create_children(sdp, tn, SES_ET_COOLING,
947 	    FAN, "FAN", cp, B_TRUE) != 0 ||
948 	    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
949 	    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
950 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
951 	    BAY, "BAY", cp, B_TRUE) != 0 ||
952 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
953 	    BAY, "BAY", cp, B_TRUE) != 0)
954 		goto error;
955 
956 	snp->sen_target->set_refcount++;
957 	topo_node_setspecific(tn, snp->sen_target);
958 
959 	ret = 0;
960 error:
961 	topo_mod_strfree(mod, manufacturer);
962 	topo_mod_strfree(mod, model);
963 	topo_mod_strfree(mod, revision);
964 	topo_mod_strfree(mod, product);
965 
966 	nvlist_free(fmri);
967 	nvlist_free(auth);
968 	return (ret);
969 }
970 
971 /*
972  * Create a bay node explicitly enumerated via XML.
973  */
974 static int
975 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
976 {
977 	topo_mod_t *mod = sdp->sed_mod;
978 	ses_enum_chassis_t *cp;
979 
980 	/*
981 	 * Iterate over chassis looking for an internal enclosure.  This
982 	 * property is set via a vendor-specific plugin, and there should only
983 	 * ever be a single internal chassis in a system.
984 	 */
985 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
986 	    cp = topo_list_next(cp)) {
987 		if (cp->sec_internal)
988 			break;
989 	}
990 
991 	if (cp == NULL) {
992 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
993 		return (-1);
994 	}
995 
996 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
997 	    BAY, "BAY", cp, B_FALSE) != 0 ||
998 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
999 	    BAY, "BAY", cp, B_FALSE) != 0)
1000 		return (-1);
1001 
1002 	return (0);
1003 }
1004 /*
1005  * Gather nodes from the current SES target into our chassis list, merging the
1006  * results if necessary.
1007  */
1008 static ses_walk_action_t
1009 ses_enum_gather(ses_node_t *np, void *data)
1010 {
1011 	nvlist_t *props = ses_node_props(np);
1012 	ses_enum_data_t *sdp = data;
1013 	topo_mod_t *mod = sdp->sed_mod;
1014 	ses_enum_chassis_t *cp;
1015 	ses_enum_node_t *snp;
1016 	ses_alt_node_t *sap;
1017 	char *csn;
1018 	uint64_t instance, type;
1019 	uint64_t prevstatus, status;
1020 	boolean_t report, internal;
1021 
1022 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
1023 		/*
1024 		 * If we have already identified the chassis for this target,
1025 		 * then this is a secondary enclosure and we should ignore it,
1026 		 * along with the rest of the tree (since this is depth-first).
1027 		 */
1028 		if (sdp->sed_current != NULL)
1029 			return (SES_WALK_ACTION_TERMINATE);
1030 
1031 		/*
1032 		 * Go through the list of chassis we have seen so far and see
1033 		 * if this serial number matches one of the known values.
1034 		 */
1035 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
1036 		    &csn) != 0)
1037 			return (SES_WALK_ACTION_TERMINATE);
1038 
1039 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1040 		    cp = topo_list_next(cp)) {
1041 			if (strcmp(cp->sec_csn, csn) == 0) {
1042 				topo_mod_dprintf(mod, "%s: part of already "
1043 				    "known chassis %s", sdp->sed_name, csn);
1044 				break;
1045 			}
1046 		}
1047 
1048 		if (cp == NULL) {
1049 			topo_mod_dprintf(mod, "%s: creating chassis %s",
1050 			    sdp->sed_name, csn);
1051 
1052 			if ((cp = topo_mod_zalloc(mod,
1053 			    sizeof (ses_enum_chassis_t))) == NULL)
1054 				goto error;
1055 
1056 			if (nvlist_lookup_boolean_value(props,
1057 			    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
1058 				cp->sec_internal = internal;
1059 
1060 			cp->sec_csn = csn;
1061 			cp->sec_enclosure = np;
1062 			cp->sec_target = sdp->sed_target;
1063 			cp->sec_instance = sdp->sed_instance++;
1064 			topo_list_append(&sdp->sed_chassis, cp);
1065 		}
1066 
1067 		topo_list_append(&cp->sec_targets, sdp->sed_target);
1068 		sdp->sed_current = cp;
1069 
1070 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
1071 		/*
1072 		 * If we haven't yet seen an enclosure node and identified the
1073 		 * current chassis, something is very wrong; bail out.
1074 		 */
1075 		if (sdp->sed_current == NULL)
1076 			return (SES_WALK_ACTION_TERMINATE);
1077 
1078 		/*
1079 		 * If this isn't one of the element types we care about, then
1080 		 * ignore it.
1081 		 */
1082 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
1083 		    &type) == 0);
1084 		if (type != SES_ET_DEVICE &&
1085 		    type != SES_ET_ARRAY_DEVICE &&
1086 		    type != SES_ET_COOLING &&
1087 		    type != SES_ET_POWER_SUPPLY &&
1088 		    type != SES_ET_ESC_ELECTRONICS)
1089 			return (SES_WALK_ACTION_CONTINUE);
1090 
1091 		/*
1092 		 * Get the current instance number and see if we already know
1093 		 * about this element.  If so, it means we have multiple paths
1094 		 * to the same elements, and we should ignore the current path.
1095 		 */
1096 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
1097 		    &instance) == 0);
1098 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
1099 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
1100 			    &instance);
1101 
1102 		cp = sdp->sed_current;
1103 
1104 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1105 		    snp = topo_list_next(snp)) {
1106 			if (snp->sen_type == type &&
1107 			    snp->sen_instance == instance)
1108 				break;
1109 		}
1110 
1111 		/*
1112 		 * We prefer the new element under the following circumstances:
1113 		 *
1114 		 * - The currently known element's status is unknown or not
1115 		 *   available, but the new element has a known status.  This
1116 		 *   occurs if a given element is only available through a
1117 		 *   particular target.
1118 		 *
1119 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
1120 		 *   property is set.  This allows us to get reliable firmware
1121 		 *   revision information from the enclosure node.
1122 		 */
1123 		if (snp != NULL) {
1124 			if (nvlist_lookup_uint64(
1125 			    ses_node_props(snp->sen_node),
1126 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
1127 				prevstatus = SES_ESC_UNSUPPORTED;
1128 			if (nvlist_lookup_uint64(
1129 			    props, SES_PROP_STATUS_CODE, &status) != 0)
1130 				status = SES_ESC_UNSUPPORTED;
1131 			if (nvlist_lookup_boolean_value(
1132 			    props, SES_PROP_REPORT, &report) != 0)
1133 				report = B_FALSE;
1134 
1135 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
1136 			    !SES_STATUS_UNAVAIL(status)) ||
1137 			    (type == SES_ET_ESC_ELECTRONICS &&
1138 			    report)) {
1139 				snp->sen_node = np;
1140 				snp->sen_target = sdp->sed_target;
1141 			}
1142 
1143 			if ((sap = topo_mod_zalloc(mod,
1144 			    sizeof (ses_alt_node_t))) == NULL)
1145 				goto error;
1146 
1147 			sap->san_node = np;
1148 			topo_list_append(&snp->sen_alt_nodes, sap);
1149 
1150 			return (SES_WALK_ACTION_CONTINUE);
1151 		}
1152 
1153 		if ((snp = topo_mod_zalloc(mod,
1154 		    sizeof (ses_enum_node_t))) == NULL)
1155 			goto error;
1156 
1157 		if ((sap = topo_mod_zalloc(mod,
1158 		    sizeof (ses_alt_node_t))) == NULL) {
1159 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
1160 			goto error;
1161 		}
1162 
1163 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
1164 		    sdp->sed_name, type, instance);
1165 		snp->sen_node = np;
1166 		snp->sen_type = type;
1167 		snp->sen_instance = instance;
1168 		snp->sen_target = sdp->sed_target;
1169 		sap->san_node = np;
1170 		topo_list_append(&snp->sen_alt_nodes, sap);
1171 		topo_list_append(&cp->sec_nodes, snp);
1172 
1173 		if (type == SES_ET_DEVICE)
1174 			cp->sec_hasdev = B_TRUE;
1175 	}
1176 
1177 	return (SES_WALK_ACTION_CONTINUE);
1178 
1179 error:
1180 	sdp->sed_errno = -1;
1181 	return (SES_WALK_ACTION_TERMINATE);
1182 }
1183 
1184 static int
1185 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
1186 {
1187 	topo_mod_t *mod = sdp->sed_mod;
1188 	DIR *dir;
1189 	struct dirent *dp;
1190 	char path[PATH_MAX];
1191 	ses_enum_target_t *stp;
1192 	int err = -1;
1193 
1194 	/*
1195 	 * Open the SES target directory and iterate over any available
1196 	 * targets.
1197 	 */
1198 	if ((dir = opendir(dirpath)) == NULL) {
1199 		/*
1200 		 * If the SES target directory does not exist, then return as if
1201 		 * there are no active targets.
1202 		 */
1203 		topo_mod_dprintf(mod, "failed to open ses "
1204 		    "directory '%s'", dirpath);
1205 		return (0);
1206 	}
1207 
1208 	while ((dp = readdir(dir)) != NULL) {
1209 		if (strcmp(dp->d_name, ".") == 0 ||
1210 		    strcmp(dp->d_name, "..") == 0)
1211 			continue;
1212 
1213 		/*
1214 		 * Create a new target instance and take a snapshot.
1215 		 */
1216 		if ((stp = topo_mod_zalloc(mod,
1217 		    sizeof (ses_enum_target_t))) == NULL)
1218 			goto error;
1219 
1220 		(void) pthread_mutex_init(&stp->set_lock, NULL);
1221 
1222 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
1223 		    dp->d_name);
1224 
1225 		/*
1226 		 * We keep track of the SES device path and export it on a
1227 		 * per-node basis to allow higher level software to get to the
1228 		 * corresponding SES state.
1229 		 */
1230 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
1231 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1232 			goto error;
1233 		}
1234 
1235 		if ((stp->set_target =
1236 		    ses_open(LIBSES_VERSION, path)) == NULL) {
1237 			topo_mod_dprintf(mod, "failed to open ses target "
1238 			    "'%s': %s", dp->d_name, ses_errmsg());
1239 			topo_mod_strfree(mod, stp->set_devpath);
1240 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1241 			continue;
1242 		}
1243 
1244 		stp->set_refcount = 1;
1245 		sdp->sed_target = stp;
1246 		stp->set_snap = ses_snap_hold(stp->set_target);
1247 		if (gettimeofday(&stp->set_snaptime, NULL) != 0)
1248 			stp->set_snaptime.tv_sec = time(NULL);
1249 
1250 		/*
1251 		 * Enumerate over all SES elements and merge them into the
1252 		 * correct ses_enum_chassis_t.
1253 		 */
1254 		sdp->sed_current = NULL;
1255 		sdp->sed_errno = 0;
1256 		sdp->sed_name = dp->d_name;
1257 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
1258 
1259 		if (sdp->sed_errno != 0)
1260 			goto error;
1261 	}
1262 
1263 	err = 0;
1264 error:
1265 	closedir(dir);
1266 	return (err);
1267 }
1268 
1269 static void
1270 ses_release(topo_mod_t *mod, tnode_t *tn)
1271 {
1272 	ses_enum_target_t *stp;
1273 
1274 	if ((stp = topo_node_getspecific(tn)) != NULL)
1275 		ses_target_free(mod, stp);
1276 }
1277 
1278 /*ARGSUSED*/
1279 static int
1280 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
1281     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
1282 {
1283 	ses_enum_chassis_t *cp;
1284 	ses_enum_data_t *data;
1285 
1286 	/*
1287 	 * Check to make sure we're being invoked sensibly, and that we're not
1288 	 * being invoked as part of a post-processing step.
1289 	 */
1290 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
1291 		return (0);
1292 
1293 	/*
1294 	 * If this is the first time we've called our enumeration method, then
1295 	 * gather information about any available enclosures.
1296 	 */
1297 	if ((data = topo_mod_getspecific(mod)) == NULL) {
1298 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
1299 		    NULL)
1300 			return (-1);
1301 
1302 		data->sed_mod = mod;
1303 		topo_mod_setspecific(mod, data);
1304 
1305 		if (disk_list_gather(mod, &data->sed_disks) != 0)
1306 			goto error;
1307 
1308 		/*
1309 		 * We search both the ses(7D) and sgen(7D) locations, so we are
1310 		 * independent of any particular driver class bindings.
1311 		 */
1312 		if (ses_process_dir("/dev/es", data) != 0 ||
1313 		    ses_process_dir("/dev/scsi/ses", data) != 0)
1314 			goto error;
1315 	}
1316 
1317 	if (strcmp(name, SES_ENCLOSURE) == 0) {
1318 		/*
1319 		 * This is a request to enumerate external enclosures.  Go
1320 		 * through all the targets and create chassis nodes where
1321 		 * necessary.
1322 		 */
1323 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
1324 		    cp = topo_list_next(cp)) {
1325 			if (ses_create_chassis(data, rnode, cp) != 0)
1326 				goto error;
1327 		}
1328 	} else {
1329 		/*
1330 		 * This is a request to enumerate a specific bay underneath the
1331 		 * root chassis (for internal disks).
1332 		 */
1333 		if (ses_create_bays(data, rnode) != 0)
1334 			goto error;
1335 	}
1336 
1337 	/*
1338 	 * This is a bit of a kludge.  In order to allow internal disks to be
1339 	 * enumerated and share snapshot-specific information with the external
1340 	 * enclosure enumeration, we rely on the fact that we will be invoked
1341 	 * for the 'ses-enclosure' node last.
1342 	 */
1343 	if (strcmp(name, SES_ENCLOSURE) == 0) {
1344 		ses_data_free(data);
1345 		topo_mod_setspecific(mod, NULL);
1346 	}
1347 	return (0);
1348 
1349 error:
1350 	ses_data_free(data);
1351 	topo_mod_setspecific(mod, NULL);
1352 	return (-1);
1353 }
1354 
1355 static const topo_modops_t ses_ops =
1356 	{ ses_enum, ses_release };
1357 
1358 static topo_modinfo_t ses_info =
1359 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
1360 
1361 /*ARGSUSED*/
1362 int
1363 _topo_init(topo_mod_t *mod, topo_version_t version)
1364 {
1365 	if (getenv("TOPOSESDEBUG") != NULL)
1366 		topo_mod_setdebug(mod);
1367 
1368 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
1369 	    SES_ENCLOSURE);
1370 
1371 	return (topo_mod_register(mod, &ses_info, TOPO_VERSION));
1372 }
1373 
1374 void
1375 _topo_fini(topo_mod_t *mod)
1376 {
1377 	topo_mod_unregister(mod);
1378 }
1379