Home | History | Annotate | Download | only in ses
      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