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