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