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