xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ses/ses.c (revision 826ac02a0def83e0a41b29321470d299c7389aab)
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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2012 Milan Jurik. All rights reserved.
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 <string.h>
35 #include <unistd.h>
36 #include <sys/dkio.h>
37 #include <sys/fm/protocol.h>
38 #include <sys/libdevid.h>
39 #include <sys/scsi/scsi_types.h>
40 #include <sys/byteorder.h>
41 #include <pthread.h>
42 #include <signal.h>
43 #include <fcntl.h>
44 #include <sys/ctfs.h>
45 #include <libcontract.h>
46 #include <poll.h>
47 #include <sys/contract/device.h>
48 #include <libsysevent.h>
49 #include <sys/sysevent/eventdefs.h>
50 #include <scsi/plugins/ses/vendor/sun.h>
51 
52 #include "disk.h"
53 #include "ses.h"
54 
55 #define	SES_VERSION	1
56 
57 #define	SES_STARTING_SUBCHASSIS 256	/* valid subchassis IDs are uint8_t */
58 #define	NO_SUBCHASSIS	((uint64_t)-1)
59 
60 static int ses_snap_freq = 250;		/* in milliseconds */
61 
62 #define	SES_STATUS_UNAVAIL(s)	\
63 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
64 
65 #define	HR_SECOND   1000000000
66 
67 /*
68  * Because multiple SES targets can be part of a single chassis, we construct
69  * our own hierarchy that takes this into account.  These SES targets may refer
70  * to the same devices (multiple paths) or to different devices (managing
71  * different portions of the space).  We arrange things into a
72  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
73  * nodes found so far.
74  */
75 typedef struct ses_alt_node {
76 	topo_list_t		san_link;
77 	ses_node_t		*san_node;
78 } ses_alt_node_t;
79 
80 typedef struct ses_enum_node {
81 	topo_list_t		sen_link;
82 	ses_node_t		*sen_node;
83 	topo_list_t		sen_alt_nodes;
84 	uint64_t		sen_type;
85 	uint64_t		sen_instance;
86 	ses_enum_target_t	*sen_target;
87 } ses_enum_node_t;
88 
89 typedef struct ses_enum_chassis {
90 	topo_list_t		sec_link;
91 	topo_list_t		sec_subchassis;
92 	topo_list_t		sec_nodes;
93 	topo_list_t		sec_targets;
94 	const char		*sec_csn;
95 	ses_node_t		*sec_enclosure;
96 	ses_enum_target_t	*sec_target;
97 	topo_instance_t		sec_instance;
98 	topo_instance_t		sec_scinstance;
99 	topo_instance_t		sec_maxinstance;
100 	boolean_t		sec_hasdev;
101 	boolean_t		sec_internal;
102 } ses_enum_chassis_t;
103 
104 typedef struct ses_enum_data {
105 	topo_list_t		sed_devs;
106 	topo_list_t		sed_chassis;
107 	ses_enum_chassis_t	*sed_current;
108 	ses_enum_target_t	*sed_target;
109 	int			sed_errno;
110 	char			*sed_name;
111 	topo_mod_t		*sed_mod;
112 	topo_instance_t		sed_instance;
113 } ses_enum_data_t;
114 
115 typedef struct sas_connector_phy_data {
116 	uint64_t    scpd_index;
117 	uint64_t    scpd_pm;
118 } sas_connector_phy_data_t;
119 
120 typedef struct sas_connector_type {
121 	uint64_t    sct_type;
122 	char	    *sct_name;
123 } sas_connector_type_t;
124 
125 static const sas_connector_type_t sas_connector_type_list[] = {
126 	{   0x0, "Information unknown"  },
127 	{   0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)"	},
128 	{   0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
129 	{   0xF, "Vendor-specific external connector"	},
130 	{   0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)"	},
131 	{   0x11,
132 	"Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)"	},
133 	{   0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)"  },
134 	{   0x21, "Internal SATA host plug (see SAS-2 and SATA-2)"  },
135 	{   0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)"	},
136 	{   0x23, "Internal SATA device plug (see SAS-2 and SATA-2)"	},
137 	{   0x2F, "Internal SAS virtual connector"  },
138 	{   0x3F, "Vendor-specific internal connector"	},
139 	{   0x70, "Other Vendor-specific connector"	},
140 	{   0x71, "Other Vendor-specific connector"	},
141 	{   0x72, "Other Vendor-specific connector"	},
142 	{   0x73, "Other Vendor-specific connector"	},
143 	{   0x74, "Other Vendor-specific connector"	},
144 	{   0x75, "Other Vendor-specific connector"	},
145 	{   0x76, "Other Vendor-specific connector"	},
146 	{   0x77, "Other Vendor-specific connector"	},
147 	{   0x78, "Other Vendor-specific connector"	},
148 	{   0x79, "Other Vendor-specific connector"	},
149 	{   0x7A, "Other Vendor-specific connector"	},
150 	{   0x7B, "Other Vendor-specific connector"	},
151 	{   0x7C, "Other Vendor-specific connector"	},
152 	{   0x7D, "Other Vendor-specific connector"	},
153 	{   0x7E, "Other Vendor-specific connector"	},
154 	{   0x7F, "Other Vendor-specific connector"	},
155 	{   0x80, "Not Defined"	}
156 };
157 
158 #define	SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED  0x80
159 #define	SAS_CONNECTOR_TYPE_NOT_DEFINED \
160 	"Connector type not definedi by SES-2 standard"
161 #define	SAS_CONNECTOR_TYPE_RESERVED \
162 	"Connector type reserved by SES-2 standard"
163 
164 typedef struct phys_enum_type {
165 	uint64_t    pet_type;
166 	char	    *pet_nodename;
167 	char	    *pet_defaultlabel;
168 	boolean_t   pet_dorange;
169 } phys_enum_type_t;
170 
171 static const phys_enum_type_t phys_enum_type_list[] = {
172 	{   SES_ET_ARRAY_DEVICE, BAY, "BAY", B_TRUE  },
173 	{   SES_ET_COOLING, FAN, "FAN", B_TRUE  },
174 	{   SES_ET_DEVICE, BAY, "BAY", B_TRUE  },
175 	{   SES_ET_ESC_ELECTRONICS, CONTROLLER, "CONTROLLER", B_TRUE  },
176 	{   SES_ET_POWER_SUPPLY, PSU, "PSU", B_TRUE  },
177 	{   SES_ET_SUNW_FANBOARD, FANBOARD, "FANBOARD", B_TRUE  },
178 	{   SES_ET_SUNW_FANMODULE, FANMODULE, "FANMODULE", B_TRUE  },
179 	{   SES_ET_SUNW_POWERBOARD, POWERBOARD, "POWERBOARD", B_TRUE  },
180 	{   SES_ET_SUNW_POWERMODULE, POWERMODULE, "POWERMODULE", B_TRUE  }
181 };
182 
183 #define	N_PHYS_ENUM_TYPES (sizeof (phys_enum_type_list) / \
184 	sizeof (phys_enum_type_list[0]))
185 
186 /*
187  * Structure for the hierarchical tree for element nodes.
188  */
189 typedef struct ses_phys_tree {
190     ses_node_t	*spt_snode;
191     ses_enum_node_t	*spt_senumnode;
192     boolean_t	spt_isfru;
193     uint64_t	spt_eonlyindex;
194     uint64_t	spt_cindex;
195     uint64_t	spt_pindex;
196     uint64_t	spt_maxinst;
197     struct ses_phys_tree    *spt_parent;
198     struct ses_phys_tree    *spt_child;
199     struct ses_phys_tree    *spt_sibling;
200     tnode_t	*spt_tnode;
201 } ses_phys_tree_t;
202 
203 typedef enum {
204 	SES_NEW_CHASSIS		= 0x1,
205 	SES_NEW_SUBCHASSIS	= 0x2,
206 	SES_DUP_CHASSIS		= 0x4,
207 	SES_DUP_SUBCHASSIS	= 0x8
208 } ses_chassis_type_e;
209 
210 
211 static const topo_pgroup_info_t storage_pgroup = {
212 	TOPO_PGROUP_STORAGE,
213 	TOPO_STABILITY_PRIVATE,
214 	TOPO_STABILITY_PRIVATE,
215 	1
216 };
217 
218 static const topo_pgroup_info_t smp_pgroup = {
219 	TOPO_PGROUP_SMP,
220 	TOPO_STABILITY_PRIVATE,
221 	TOPO_STABILITY_PRIVATE,
222 	1
223 };
224 
225 static const topo_pgroup_info_t ses_pgroup = {
226 	TOPO_PGROUP_SES,
227 	TOPO_STABILITY_PRIVATE,
228 	TOPO_STABILITY_PRIVATE,
229 	1
230 };
231 
232 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
233     nvlist_t **);
234 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
235     nvlist_t **);
236 
237 static const topo_method_t ses_component_methods[] = {
238 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
239 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
240 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
241 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
242 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
243 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
244 	    topo_method_sensor_failure },
245 	{ NULL }
246 };
247 
248 static const topo_method_t ses_bay_methods[] = {
249 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
250 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
251 	{ NULL }
252 };
253 
254 static const topo_method_t ses_enclosure_methods[] = {
255 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
256 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
257 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
258 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
259 	{ NULL }
260 };
261 
262 /*
263  * Functions for tracking ses devices which we were unable to open. We retry
264  * these at regular intervals using ses_recheck_dir() and if we find that we
265  * can now open any of them then we send a sysevent to indicate that a new topo
266  * snapshot should be taken.
267  */
268 typedef struct ses_open_fail_list {
269 	struct ses_open_fail_list	*sof_next;
270 	char				*sof_path;
271 } ses_open_fail_list_t;
272 
273 static ses_open_fail_list_t *ses_sofh;
274 static pthread_mutex_t ses_sofmt;
275 static void ses_ct_print(char *ptr);
276 
277 static void
278 ses_recheck_dir()
279 {
280 	ses_target_t *target;
281 	sysevent_id_t eid;
282 	char buf[80];
283 	ses_open_fail_list_t *sof;
284 
285 	/*
286 	 * check list of "unable to open" devices
287 	 */
288 	(void) pthread_mutex_lock(&ses_sofmt);
289 	for (sof = ses_sofh; sof != NULL; sof = sof->sof_next) {
290 		/*
291 		 * see if we can open it now
292 		 */
293 		if ((target = ses_open(LIBSES_VERSION,
294 		    sof->sof_path)) == NULL) {
295 			(void) snprintf(buf, sizeof (buf),
296 			    "recheck_dir - still can't open %s", sof->sof_path);
297 			ses_ct_print(buf);
298 			continue;
299 		}
300 
301 		/*
302 		 * ok - better force a new snapshot
303 		 */
304 		(void) snprintf(buf, sizeof (buf),
305 		    "recheck_dir - can now open %s", sof->sof_path);
306 		ses_ct_print(buf);
307 		(void) sysevent_post_event(EC_PLATFORM, ESC_PLATFORM_SP_RESET,
308 		    SUNW_VENDOR, "fmd", NULL, &eid);
309 		ses_close(target);
310 		break;
311 	}
312 	(void) pthread_mutex_unlock(&ses_sofmt);
313 }
314 
315 static void
316 ses_sof_alloc(topo_mod_t *mod, char *path)
317 {
318 	ses_open_fail_list_t *sof;
319 
320 	(void) pthread_mutex_lock(&ses_sofmt);
321 	sof = topo_mod_zalloc(mod, sizeof (*sof));
322 	topo_mod_dprintf(mod, "sof_alloc %s", path);
323 	sof->sof_path = path;
324 	sof->sof_next = ses_sofh;
325 	ses_sofh = sof;
326 	(void) pthread_mutex_unlock(&ses_sofmt);
327 }
328 
329 static void
330 ses_sof_freeall(topo_mod_t *mod)
331 {
332 	ses_open_fail_list_t *sof, *next_sof;
333 
334 	(void) pthread_mutex_lock(&ses_sofmt);
335 	for (sof = ses_sofh; sof != NULL; sof = next_sof) {
336 		next_sof = sof->sof_next;
337 		topo_mod_dprintf(mod, "sof_freeall %s", sof->sof_path);
338 		topo_mod_strfree(mod, sof->sof_path);
339 		topo_mod_free(mod, sof, sizeof (*sof));
340 	}
341 	ses_sofh = NULL;
342 	(void) pthread_mutex_unlock(&ses_sofmt);
343 }
344 
345 /*
346  * functions for verifying that the ses_enum_target_t held in a device
347  * contract's cookie field is still valid (it may have been freed by
348  * ses_release()).
349  */
350 typedef struct ses_stp_list {
351 	struct ses_stp_list	*ssl_next;
352 	ses_enum_target_t	*ssl_tgt;
353 } ses_stp_list_t;
354 
355 static ses_stp_list_t *ses_sslh;
356 static pthread_mutex_t ses_sslmt;
357 
358 static void
359 ses_ssl_alloc(topo_mod_t *mod, ses_enum_target_t *stp)
360 {
361 	ses_stp_list_t *ssl;
362 
363 	(void) pthread_mutex_lock(&ses_sslmt);
364 	ssl = topo_mod_zalloc(mod, sizeof (*ssl));
365 	topo_mod_dprintf(mod, "ssl_alloc %p", stp);
366 	ssl->ssl_tgt = stp;
367 	ssl->ssl_next = ses_sslh;
368 	ses_sslh = ssl;
369 	(void) pthread_mutex_unlock(&ses_sslmt);
370 }
371 
372 static void
373 ses_ssl_free(topo_mod_t *mod, ses_enum_target_t *stp)
374 {
375 	ses_stp_list_t *ssl, *prev_ssl;
376 
377 	(void) pthread_mutex_lock(&ses_sslmt);
378 	prev_ssl = NULL;
379 	for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next) {
380 		if (ssl->ssl_tgt == stp) {
381 			topo_mod_dprintf(mod, "ssl_free %p", ssl->ssl_tgt);
382 			if (prev_ssl == NULL)
383 				ses_sslh = ssl->ssl_next;
384 			else
385 				prev_ssl->ssl_next = ssl->ssl_next;
386 			topo_mod_free(mod, ssl, sizeof (*ssl));
387 			break;
388 		}
389 		prev_ssl = ssl;
390 	}
391 	(void) pthread_mutex_unlock(&ses_sslmt);
392 }
393 
394 static int
395 ses_ssl_valid(ses_enum_target_t *stp)
396 {
397 	ses_stp_list_t *ssl;
398 
399 	for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next)
400 		if (ssl->ssl_tgt == stp)
401 			return (1);
402 	return (0);
403 }
404 
405 /*
406  * Functions for creating and destroying a background thread
407  * (ses_contract_thread) used for detecting when ses devices have been
408  * retired/unretired.
409  */
410 static struct ses_thread_s {
411 	pthread_mutex_t mt;
412 	pthread_t tid;
413 	int thr_sig;
414 	int doexit;
415 	int count;
416 } sesthread = {
417 	PTHREAD_MUTEX_INITIALIZER,
418 	0,
419 	SIGTERM,
420 	0,
421 	0
422 };
423 
424 typedef struct ses_mod_list {
425 	struct ses_mod_list	*smod_next;
426 	topo_mod_t		*smod_mod;
427 } ses_mod_list_t;
428 
429 static ses_mod_list_t *ses_smod;
430 
431 static void
432 ses_ct_print(char *ptr)
433 {
434 	(void) pthread_mutex_lock(&sesthread.mt);
435 	if (ses_smod != NULL && ses_smod->smod_mod != NULL)
436 		topo_mod_dprintf(ses_smod->smod_mod, ptr);
437 	(void) pthread_mutex_unlock(&sesthread.mt);
438 }
439 
440 /*ARGSUSED*/
441 static void *
442 ses_contract_thread(void *arg)
443 {
444 	int efd, ctlfd, statfd;
445 	ct_evthdl_t ev;
446 	ctevid_t evid;
447 	uint_t event;
448 	char path[PATH_MAX];
449 	char buf[80];
450 	ses_enum_target_t *stp;
451 	ct_stathdl_t stathdl;
452 	ctid_t ctid;
453 	struct pollfd fds;
454 	int pollret;
455 
456 	ses_ct_print("start contract event thread");
457 	efd = open64(CTFS_ROOT "/device/pbundle", O_RDONLY);
458 	fds.fd = efd;
459 	fds.events = POLLIN;
460 	fds.revents = 0;
461 	for (;;) {
462 		/* check if we've been asked to exit */
463 		(void) pthread_mutex_lock(&sesthread.mt);
464 		if (sesthread.doexit) {
465 			(void) pthread_mutex_unlock(&sesthread.mt);
466 			break;
467 		}
468 		(void) pthread_mutex_unlock(&sesthread.mt);
469 
470 		/* poll until an event arrives */
471 		if ((pollret = poll(&fds, 1, 10000)) <= 0) {
472 			if (pollret == 0)
473 				ses_recheck_dir();
474 			continue;
475 		}
476 
477 		/* read the event */
478 		(void) pthread_mutex_lock(&ses_sslmt);
479 		ses_ct_print("read contract event");
480 		if (ct_event_read(efd, &ev) != 0) {
481 			(void) pthread_mutex_unlock(&ses_sslmt);
482 			continue;
483 		}
484 
485 		/* see if it is an event we are expecting */
486 		ctid = ct_event_get_ctid(ev);
487 		(void) snprintf(buf, sizeof (buf),
488 		    "got contract event ctid=%d", ctid);
489 		ses_ct_print(buf);
490 		event = ct_event_get_type(ev);
491 		if (event != CT_DEV_EV_OFFLINE && event != CT_EV_NEGEND) {
492 			(void) snprintf(buf, sizeof (buf),
493 			    "bad contract event %x", event);
494 			ses_ct_print(buf);
495 			ct_event_free(ev);
496 			(void) pthread_mutex_unlock(&ses_sslmt);
497 			continue;
498 		}
499 
500 		/* find target pointer saved in cookie */
501 		evid = ct_event_get_evid(ev);
502 		(void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/status",
503 		    ctid);
504 		statfd = open64(path, O_RDONLY);
505 		(void) ct_status_read(statfd, CTD_COMMON, &stathdl);
506 		stp = (ses_enum_target_t *)(uintptr_t)
507 		    ct_status_get_cookie(stathdl);
508 		ct_status_free(stathdl);
509 		(void) close(statfd);
510 
511 		/* check if target pointer is still valid */
512 		if (ses_ssl_valid(stp) == 0) {
513 			(void) snprintf(buf, sizeof (buf),
514 			    "contract already abandoned %x", event);
515 			ses_ct_print(buf);
516 			(void) snprintf(path, PATH_MAX,
517 			    CTFS_ROOT "/device/%ld/ctl", ctid);
518 			ctlfd = open64(path, O_WRONLY);
519 			if (event != CT_EV_NEGEND)
520 				(void) ct_ctl_ack(ctlfd, evid);
521 			else
522 				(void) ct_ctl_abandon(ctlfd);
523 			(void) close(ctlfd);
524 			ct_event_free(ev);
525 			(void) pthread_mutex_unlock(&ses_sslmt);
526 			continue;
527 		}
528 
529 		/* find control device for ack/abandon */
530 		(void) pthread_mutex_lock(&stp->set_lock);
531 		(void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/ctl",
532 		    ctid);
533 		ctlfd = open64(path, O_WRONLY);
534 		if (event != CT_EV_NEGEND) {
535 			/* if this is an offline event, do the offline */
536 			ses_ct_print("got contract offline event");
537 			if (stp->set_target) {
538 				ses_ct_print("contract thread rele");
539 				ses_snap_rele(stp->set_snap);
540 				ses_close(stp->set_target);
541 				stp->set_target = NULL;
542 			}
543 			(void) ct_ctl_ack(ctlfd, evid);
544 		} else {
545 			/* if this is the negend, then abandon the contract */
546 			ses_ct_print("got contract negend");
547 			if (stp->set_ctid) {
548 				(void) snprintf(buf, sizeof (buf),
549 				    "abandon old contract %d", stp->set_ctid);
550 				ses_ct_print(buf);
551 				stp->set_ctid = NULL;
552 			}
553 			(void) ct_ctl_abandon(ctlfd);
554 		}
555 		(void) close(ctlfd);
556 		(void) pthread_mutex_unlock(&stp->set_lock);
557 		ct_event_free(ev);
558 		(void) pthread_mutex_unlock(&ses_sslmt);
559 	}
560 	(void) close(efd);
561 	return (NULL);
562 }
563 
564 int
565 find_thr_sig(void)
566 {
567 	int i;
568 	sigset_t oset, rset;
569 	int sig[] = {SIGTERM, SIGUSR1, SIGUSR2};
570 	int sig_sz = sizeof (sig) / sizeof (int);
571 	int rc = SIGTERM;
572 
573 	/* prefered set of signals that are likely used to terminate threads */
574 	(void) sigemptyset(&oset);
575 	(void) pthread_sigmask(SIG_SETMASK, NULL, &oset);
576 	for (i = 0; i < sig_sz; i++) {
577 		if (sigismember(&oset, sig[i]) == 0) {
578 			return (sig[i]);
579 		}
580 	}
581 
582 	/* reserved set of signals that are not allowed to terminate thread */
583 	(void) sigemptyset(&rset);
584 	(void) sigaddset(&rset, SIGABRT);
585 	(void) sigaddset(&rset, SIGKILL);
586 	(void) sigaddset(&rset, SIGSTOP);
587 	(void) sigaddset(&rset, SIGCANCEL);
588 
589 	/* Find signal that is not masked and not in the reserved list. */
590 	for (i = 1; i < MAXSIG; i++) {
591 		if (sigismember(&rset, i) == 1) {
592 			continue;
593 		}
594 		if (sigismember(&oset, i) == 0) {
595 			return (i);
596 		}
597 	}
598 
599 	return (rc);
600 }
601 
602 /*ARGSUSED*/
603 static void
604 ses_handler(int sig)
605 {
606 }
607 
608 static void
609 ses_thread_init(topo_mod_t *mod)
610 {
611 	pthread_attr_t *attr = NULL;
612 	struct sigaction act;
613 	ses_mod_list_t *smod;
614 
615 	(void) pthread_mutex_lock(&sesthread.mt);
616 	sesthread.count++;
617 	smod = topo_mod_zalloc(mod, sizeof (*smod));
618 	smod->smod_mod = mod;
619 	smod->smod_next = ses_smod;
620 	ses_smod = smod;
621 	if (sesthread.tid == 0) {
622 		/* find a suitable signal to use for killing the thread below */
623 		sesthread.thr_sig = find_thr_sig();
624 
625 		/* if don't have a handler for this signal, create one */
626 		(void) sigaction(sesthread.thr_sig, NULL, &act);
627 		if (act.sa_handler == SIG_DFL || act.sa_handler == SIG_IGN)
628 			act.sa_handler = ses_handler;
629 		(void) sigaction(sesthread.thr_sig, &act, NULL);
630 
631 		/* create a thread to listen for offline events */
632 		(void) pthread_create(&sesthread.tid,
633 		    attr, ses_contract_thread, NULL);
634 	}
635 	(void) pthread_mutex_unlock(&sesthread.mt);
636 }
637 
638 static void
639 ses_thread_fini(topo_mod_t *mod)
640 {
641 	ses_mod_list_t *smod, *prev_smod;
642 
643 	(void) pthread_mutex_lock(&sesthread.mt);
644 	prev_smod = NULL;
645 	for (smod = ses_smod; smod != NULL; smod = smod->smod_next) {
646 		if (smod->smod_mod == mod) {
647 			if (prev_smod == NULL)
648 				ses_smod = smod->smod_next;
649 			else
650 				prev_smod->smod_next = smod->smod_next;
651 			topo_mod_free(mod, smod, sizeof (*smod));
652 			break;
653 		}
654 		prev_smod = smod;
655 	}
656 	if (--sesthread.count > 0) {
657 		(void) pthread_mutex_unlock(&sesthread.mt);
658 		return;
659 	}
660 	sesthread.doexit = 1;
661 	(void) pthread_mutex_unlock(&sesthread.mt);
662 	(void) pthread_kill(sesthread.tid, sesthread.thr_sig);
663 	(void) pthread_join(sesthread.tid, NULL);
664 	sesthread.tid = 0;
665 }
666 
667 static void
668 ses_create_contract(topo_mod_t *mod, ses_enum_target_t *stp)
669 {
670 	int tfd, len, rval;
671 	char link_path[PATH_MAX];
672 
673 	stp->set_ctid = NULL;
674 
675 	/* convert "/dev" path into "/devices" path */
676 	if ((len = readlink(stp->set_devpath, link_path, PATH_MAX)) < 0) {
677 		topo_mod_dprintf(mod, "readlink failed");
678 		return;
679 	}
680 	link_path[len] = '\0';
681 
682 	/* set up template to create new contract */
683 	tfd = open64(CTFS_ROOT "/device/template", O_RDWR);
684 	(void) ct_tmpl_set_critical(tfd, CT_DEV_EV_OFFLINE);
685 	(void) ct_tmpl_set_cookie(tfd, (uint64_t)(uintptr_t)stp);
686 
687 	/* strip "../../devices" off the front and create the contract */
688 	if ((rval = ct_dev_tmpl_set_minor(tfd, &link_path[13])) != 0)
689 		topo_mod_dprintf(mod, "failed to set minor %s rval = %d",
690 		    &link_path[13], rval);
691 	else if ((rval = ct_tmpl_create(tfd, &stp->set_ctid)) != 0)
692 		topo_mod_dprintf(mod, "failed to create ctid rval = %d", rval);
693 	else
694 		topo_mod_dprintf(mod, "created ctid=%d", stp->set_ctid);
695 	(void) close(tfd);
696 }
697 
698 static void
699 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
700 {
701 	if (--stp->set_refcount == 0) {
702 		/* check if already closed due to contract offline request */
703 		(void) pthread_mutex_lock(&stp->set_lock);
704 		if (stp->set_target) {
705 			ses_snap_rele(stp->set_snap);
706 			ses_close(stp->set_target);
707 			stp->set_target = NULL;
708 		}
709 		if (stp->set_ctid) {
710 			int ctlfd;
711 			char path[PATH_MAX];
712 
713 			topo_mod_dprintf(mod, "abandon old contract %d",
714 			    stp->set_ctid);
715 			(void) snprintf(path, PATH_MAX,
716 			    CTFS_ROOT "/device/%ld/ctl", stp->set_ctid);
717 			ctlfd = open64(path, O_WRONLY);
718 			(void) ct_ctl_abandon(ctlfd);
719 			(void) close(ctlfd);
720 			stp->set_ctid = NULL;
721 		}
722 		(void) pthread_mutex_unlock(&stp->set_lock);
723 		ses_ssl_free(mod, stp);
724 		topo_mod_strfree(mod, stp->set_devpath);
725 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
726 	}
727 }
728 
729 static void
730 ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
731 {
732 	topo_mod_t *mod = sdp->sed_mod;
733 	ses_enum_chassis_t *cp;
734 	ses_enum_node_t *np;
735 	ses_enum_target_t *tp;
736 	ses_alt_node_t *ap;
737 	topo_list_t *cpl;
738 
739 
740 	if (pcp != NULL)
741 		cpl = &pcp->sec_subchassis;
742 	else
743 		cpl = &sdp->sed_chassis;
744 
745 	while ((cp = topo_list_next(cpl)) != NULL) {
746 		topo_list_delete(cpl, cp);
747 
748 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
749 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
750 			    NULL) {
751 				topo_list_delete(&np->sen_alt_nodes, ap);
752 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
753 			}
754 			topo_list_delete(&cp->sec_nodes, np);
755 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
756 		}
757 
758 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
759 			topo_list_delete(&cp->sec_targets, tp);
760 			ses_target_free(mod, tp);
761 		}
762 
763 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
764 	}
765 
766 	if (pcp == NULL) {
767 		dev_list_free(mod, &sdp->sed_devs);
768 		topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
769 	}
770 }
771 
772 /*
773  * For enclosure nodes, we have a special contains method.  By default, the hc
774  * walker will compare the node name and instance number to determine if an
775  * FMRI matches.  For enclosures where the enumeration order is impossible to
776  * predict, we instead use the chassis-id as a unique identifier, and ignore
777  * the instance number.
778  */
779 static int
780 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
781 {
782 	uint8_t v1, v2;
783 	nvlist_t **hcp1, **hcp2;
784 	int err, i;
785 	uint_t nhcp1, nhcp2;
786 	nvlist_t *a1, *a2;
787 	char *c1, *c2;
788 	int mindepth;
789 
790 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
791 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
792 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
793 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
794 
795 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
796 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
797 	if (err != 0)
798 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
799 
800 	/*
801 	 * If the chassis-id doesn't match, then these FMRIs are not
802 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
803 	 * have no choice but to fall back to the instance ID.
804 	 */
805 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
806 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
807 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
808 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
809 		if (strcmp(c1, c2) != 0)
810 			return (0);
811 
812 		mindepth = 1;
813 	} else {
814 		mindepth = 0;
815 	}
816 
817 	if (nhcp2 < nhcp1)
818 		return (0);
819 
820 	for (i = 0; i < nhcp1; i++) {
821 		char *nm1 = NULL;
822 		char *nm2 = NULL;
823 		char *id1 = NULL;
824 		char *id2 = NULL;
825 
826 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
827 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
828 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
829 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
830 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
831 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
832 
833 		if (strcmp(nm1, nm2) == 0 &&
834 		    (i < mindepth || strcmp(id1, id2) == 0))
835 			continue;
836 
837 		return (0);
838 	}
839 
840 	return (1);
841 }
842 
843 /*ARGSUSED*/
844 static int
845 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
846     nvlist_t *in, nvlist_t **out)
847 {
848 	int ret;
849 	nvlist_t *nv1, *nv2;
850 
851 	if (version > TOPO_METH_CONTAINS_VERSION)
852 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
853 
854 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
855 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
856 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
857 
858 	ret = fmri_contains(mod, nv1, nv2);
859 	if (ret < 0)
860 		return (-1);
861 
862 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
863 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
864 		    ret) == 0)
865 			return (0);
866 		else
867 			nvlist_free(*out);
868 	}
869 
870 	return (-1);
871 
872 }
873 
874 /*
875  * Return a current instance of the node.  This is somewhat complicated because
876  * we need to take a new snapshot in order to get the new data, but we don't
877  * want to be constantly taking SES snapshots if the consumer is going to do a
878  * series of queries.  So we adopt the strategy of assuming that the SES state
879  * is not going to be rapidly changing, and limit our snapshot frequency to
880  * some defined bounds.
881  */
882 ses_node_t *
883 ses_node_lock(topo_mod_t *mod, tnode_t *tn)
884 {
885 	ses_enum_target_t *tp = topo_node_getspecific(tn);
886 	hrtime_t now;
887 	ses_snap_t *snap;
888 	int err;
889 	uint64_t nodeid;
890 	ses_node_t *np;
891 
892 	if (tp == NULL) {
893 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
894 		return (NULL);
895 	}
896 
897 	(void) pthread_mutex_lock(&tp->set_lock);
898 
899 	/*
900 	 * Determine if we need to take a new snapshot.
901 	 */
902 	now = gethrtime();
903 
904 	if (tp->set_target == NULL) {
905 		/*
906 		 * We may have closed the device but not yet abandoned the
907 		 * contract (ie we've had the offline event but not yet the
908 		 * negend). If so, just return failure.
909 		 */
910 		if (tp->set_ctid != NULL) {
911 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
912 			(void) pthread_mutex_unlock(&tp->set_lock);
913 			return (NULL);
914 		}
915 
916 		/*
917 		 * The device has been closed due to a contract offline
918 		 * request, then we need to reopen it and create a new contract.
919 		 */
920 		if ((tp->set_target =
921 		    ses_open(LIBSES_VERSION, tp->set_devpath)) == NULL) {
922 			sysevent_id_t eid;
923 
924 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
925 			(void) pthread_mutex_unlock(&tp->set_lock);
926 			topo_mod_dprintf(mod, "recheck_dir - "
927 			    "can no longer open %s", tp->set_devpath);
928 			(void) sysevent_post_event(EC_PLATFORM,
929 			    ESC_PLATFORM_SP_RESET, SUNW_VENDOR, "fmd", NULL,
930 			    &eid);
931 			return (NULL);
932 		}
933 		topo_mod_dprintf(mod, "reopen contract");
934 		ses_create_contract(mod, tp);
935 		tp->set_snap = ses_snap_hold(tp->set_target);
936 		tp->set_snaptime = gethrtime();
937 	} else if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
938 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
939 		if (ses_snap_generation(snap) !=
940 		    ses_snap_generation(tp->set_snap)) {
941 			/*
942 			 * If we find ourselves in this situation, we're in
943 			 * trouble.  The generation count has changed, which
944 			 * indicates that our current topology is out of date.
945 			 * But we need to consult the new topology in order to
946 			 * determine presence at this moment in time.  We can't
947 			 * go back and change the topo snapshot in situ, so
948 			 * we'll just have to fail the call in this unlikely
949 			 * scenario.
950 			 */
951 			ses_snap_rele(snap);
952 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
953 			(void) pthread_mutex_unlock(&tp->set_lock);
954 			return (NULL);
955 		} else {
956 			ses_snap_rele(tp->set_snap);
957 			tp->set_snap = snap;
958 		}
959 		tp->set_snaptime = gethrtime();
960 	}
961 
962 	snap = tp->set_snap;
963 
964 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
965 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
966 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
967 
968 	return (np);
969 }
970 
971 /*ARGSUSED*/
972 void
973 ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
974 {
975 	ses_enum_target_t *tp = topo_node_getspecific(tn);
976 
977 	verify(tp != NULL);
978 
979 	(void) pthread_mutex_unlock(&tp->set_lock);
980 }
981 
982 /*
983  * Determine if the element is present.
984  */
985 /*ARGSUSED*/
986 static int
987 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
988     nvlist_t *in, nvlist_t **out)
989 {
990 	boolean_t present;
991 	ses_node_t *np;
992 	nvlist_t *props, *nvl;
993 	uint64_t status;
994 
995 	if ((np = ses_node_lock(mod, tn)) == NULL)
996 		return (-1);
997 
998 	verify((props = ses_node_props(np)) != NULL);
999 	verify(nvlist_lookup_uint64(props,
1000 	    SES_PROP_STATUS_CODE, &status) == 0);
1001 
1002 	ses_node_unlock(mod, tn);
1003 
1004 	present = (status != SES_ESC_NOT_INSTALLED);
1005 
1006 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
1007 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1008 
1009 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
1010 	    present) != 0) {
1011 		nvlist_free(nvl);
1012 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1013 	}
1014 
1015 	*out = nvl;
1016 
1017 	return (0);
1018 }
1019 
1020 /*
1021  * Sets standard properties for a ses node (enclosure, bay, controller
1022  * or expander).
1023  * This includes setting the FRU, as well as setting the
1024  * authority information.  When  the fru topo node(frutn) is not NULL
1025  * its resouce should be used as FRU.
1026  */
1027 static int
1028 ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn,
1029     nvlist_t *auth, uint64_t nodeid, const char *path)
1030 {
1031 	int err;
1032 	char *product, *chassis;
1033 	nvlist_t *fmri;
1034 
1035 	/*
1036 	 * Set the authority explicitly if specified.
1037 	 */
1038 	if (auth) {
1039 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
1040 		    &product) == 0);
1041 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
1042 		    &chassis) == 0);
1043 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1044 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
1045 		    &err) != 0 ||
1046 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1047 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
1048 		    &err) != 0 ||
1049 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1050 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
1051 		    &err) != 0) {
1052 			topo_mod_dprintf(mod, "failed to add authority "
1053 			    "properties: %s\n", topo_strerror(err));
1054 			return (topo_mod_seterrno(mod, err));
1055 		}
1056 	}
1057 
1058 	/*
1059 	 * Copy the resource and set that as the FRU.
1060 	 */
1061 	if (frutn != NULL) {
1062 		if (topo_node_resource(frutn, &fmri, &err) != 0) {
1063 			topo_mod_dprintf(mod,
1064 			    "topo_node_resource() failed : %s\n",
1065 			    topo_strerror(err));
1066 			return (topo_mod_seterrno(mod, err));
1067 		}
1068 	} else {
1069 		if (topo_node_resource(tn, &fmri, &err) != 0) {
1070 			topo_mod_dprintf(mod,
1071 			    "topo_node_resource() failed : %s\n",
1072 			    topo_strerror(err));
1073 			return (topo_mod_seterrno(mod, err));
1074 		}
1075 	}
1076 
1077 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
1078 		topo_mod_dprintf(mod,
1079 		    "topo_node_fru_set() failed : %s\n",
1080 		    topo_strerror(err));
1081 		nvlist_free(fmri);
1082 		return (topo_mod_seterrno(mod, err));
1083 	}
1084 
1085 	nvlist_free(fmri);
1086 
1087 	/*
1088 	 * Set the SES-specific properties so that consumers can query
1089 	 * additional information about the particular SES element.
1090 	 */
1091 	if (topo_pgroup_create(tn, &ses_pgroup, &err) != 0) {
1092 		topo_mod_dprintf(mod, "failed to create propgroup "
1093 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
1094 		return (-1);
1095 	}
1096 
1097 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
1098 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
1099 	    nodeid, &err) != 0) {
1100 		topo_mod_dprintf(mod,
1101 		    "failed to create property %s: %s\n",
1102 		    TOPO_PROP_NODE_ID, topo_strerror(err));
1103 		return (-1);
1104 	}
1105 
1106 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
1107 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
1108 	    path, &err) != 0) {
1109 		topo_mod_dprintf(mod,
1110 		    "failed to create property %s: %s\n",
1111 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
1112 		return (-1);
1113 	}
1114 
1115 	return (0);
1116 }
1117 
1118 /*
1119  * Callback to add a disk to a given bay.  We first check the status-code to
1120  * determine if a disk is present, ignoring those that aren't in an appropriate
1121  * state.  We then scan the parent bay node's SAS address array to determine
1122  * possible attached SAS addresses.  We create a disk node if the disk is not
1123  * SAS or the SES target does not support the necessary pages for this; if we
1124  * find the SAS address, we create a disk node and also correlate it with
1125  * the corresponding Solaris device node to fill in the rest of the data.
1126  */
1127 static int
1128 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
1129 {
1130 	topo_mod_t *mod = sdp->sed_mod;
1131 	uint64_t status;
1132 	uint_t s, nsas;
1133 	char **paths;
1134 	int err, ret;
1135 	tnode_t *child = NULL;
1136 
1137 	/*
1138 	 * Skip devices that are not in a present (and possibly damaged) state.
1139 	 */
1140 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
1141 		return (0);
1142 
1143 	if (status != SES_ESC_UNSUPPORTED &&
1144 	    status != SES_ESC_OK &&
1145 	    status != SES_ESC_CRITICAL &&
1146 	    status != SES_ESC_NONCRITICAL &&
1147 	    status != SES_ESC_UNRECOVERABLE &&
1148 	    status != SES_ESC_NO_ACCESS)
1149 		return (0);
1150 
1151 	topo_mod_dprintf(mod, "found attached disk");
1152 
1153 	/*
1154 	 * Create the disk range.
1155 	 */
1156 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
1157 		topo_mod_dprintf(mod,
1158 		    "topo_node_create_range() failed: %s",
1159 		    topo_mod_errmsg(mod));
1160 		return (-1);
1161 	}
1162 
1163 	/*
1164 	 * Look through all SAS addresses and attempt to correlate them to a
1165 	 * known Solaris device.  If we don't find a matching node, then we
1166 	 * don't enumerate the disk node.
1167 	 * Note that TOPO_PROP_SAS_ADDR prop includes SAS address from
1168 	 * alternate elements that represent the same device.
1169 	 */
1170 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
1171 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
1172 		return (0);
1173 
1174 	err = 0;
1175 
1176 	for (s = 0; s < nsas; s++) {
1177 		ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s],
1178 		    &child);
1179 		if (ret == 0) {
1180 			break;
1181 		} else if (ret < 0) {
1182 			err = -1;
1183 			break;
1184 		}
1185 	}
1186 
1187 	if (s == nsas)
1188 		(void) disk_declare_non_enumerated(mod, pnode, &child);
1189 
1190 	/* copy sas_addresses (target-ports) from parent (with 'w'added) */
1191 	if (child != NULL) {
1192 		int i;
1193 		char **tports;
1194 		uint64_t wwn;
1195 
1196 		tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
1197 		if (tports != NULL) {
1198 			for (i = 0; i < nsas; i++) {
1199 				if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
1200 				    DDI_SUCCESS)
1201 					break;
1202 				tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
1203 				if (tports[i] == NULL)
1204 					break;
1205 			}
1206 			/* if they all worked then create the property */
1207 			if (i == nsas)
1208 				(void) topo_prop_set_string_array(child,
1209 				    TOPO_PGROUP_STORAGE,
1210 				    TOPO_STORAGE_TARGET_PORT_L0IDS,
1211 				    TOPO_PROP_IMMUTABLE, (const char **)tports,
1212 				    nsas, &err);
1213 
1214 			for (i = 0; i < nsas; i++)
1215 				if (tports[i] != NULL)
1216 					scsi_free_wwnstr(tports[i]);
1217 			topo_mod_free(mod, tports, sizeof (char *) * nsas);
1218 		}
1219 	}
1220 
1221 	for (s = 0; s < nsas; s++)
1222 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
1223 	topo_mod_free(mod, paths, nsas * sizeof (char *));
1224 
1225 	return (err);
1226 }
1227 
1228 static int
1229 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
1230 {
1231 	ses_alt_node_t *ap;
1232 	ses_node_t *np;
1233 	nvlist_t *props;
1234 
1235 	nvlist_t **phys;
1236 	uint_t i, j, n_phys, all_phys = 0;
1237 	char **paths;
1238 	uint64_t addr;
1239 	size_t len;
1240 	int terr, err = -1;
1241 
1242 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1243 	    ap = topo_list_next(ap)) {
1244 		np = ap->san_node;
1245 		props = ses_node_props(np);
1246 
1247 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1248 		    &phys, &n_phys) != 0)
1249 			continue;
1250 
1251 		all_phys += n_phys;
1252 	}
1253 
1254 	if (all_phys == 0)
1255 		return (0);
1256 
1257 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
1258 		return (-1);
1259 
1260 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1261 	    ap = topo_list_next(ap)) {
1262 		np = ap->san_node;
1263 		props = ses_node_props(np);
1264 
1265 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1266 		    &phys, &n_phys) != 0)
1267 			continue;
1268 
1269 		for (j = 0; j < n_phys; j++) {
1270 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
1271 			    &addr) != 0)
1272 				continue;
1273 
1274 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
1275 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
1276 				goto error;
1277 
1278 			(void) snprintf(paths[i], len, "%016llx", addr);
1279 
1280 			++i;
1281 		}
1282 	}
1283 
1284 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
1285 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
1286 	    (const char **)paths, i, &terr);
1287 	if (err != 0)
1288 		err = topo_mod_seterrno(mod, terr);
1289 
1290 error:
1291 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
1292 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
1293 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
1294 
1295 	return (err);
1296 }
1297 
1298 /*
1299  * Callback to create a basic node (bay, psu, fan, or controller and expander).
1300  */
1301 static int
1302 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, tnode_t *pnode,
1303     tnode_t *frutn, const char *nodename, const char *labelname,
1304     tnode_t **node)
1305 {
1306 	ses_node_t *np = snp->sen_node;
1307 	ses_node_t *parent;
1308 	uint64_t instance = snp->sen_instance;
1309 	topo_mod_t *mod = sdp->sed_mod;
1310 	nvlist_t *props, *aprops;
1311 	nvlist_t *auth = NULL, *fmri = NULL;
1312 	tnode_t *tn = NULL;
1313 	char label[128];
1314 	int err;
1315 	char *part = NULL, *serial = NULL, *revision = NULL;
1316 	char *desc;
1317 	boolean_t report;
1318 
1319 	props = ses_node_props(np);
1320 
1321 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
1322 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
1323 
1324 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
1325 
1326 	/*
1327 	 * Create the node.  The interesting information is all copied from the
1328 	 * parent enclosure node, so there is not much to do.
1329 	 */
1330 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
1331 		goto error;
1332 
1333 	/*
1334 	 * We want to report revision information for the controller nodes, but
1335 	 * we do not get per-element revision information.  However, we do have
1336 	 * revision information for the entire enclosure, and we can use the
1337 	 * 'reported-via' property to know that this controller corresponds to
1338 	 * the given revision information.  This means we cannot get revision
1339 	 * information for targets we are not explicitly connected to, but
1340 	 * there is little we can do about the situation.
1341 	 */
1342 	if (strcmp(nodename, CONTROLLER) == 0 &&
1343 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
1344 	    report) {
1345 		for (parent = ses_node_parent(np); parent != NULL;
1346 		    parent = ses_node_parent(parent)) {
1347 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
1348 				(void) nvlist_lookup_string(
1349 				    ses_node_props(parent),
1350 				    SES_EN_PROP_REV, &revision);
1351 				break;
1352 			}
1353 		}
1354 	}
1355 
1356 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
1357 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
1358 	    serial)) == NULL) {
1359 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1360 		    topo_mod_errmsg(mod));
1361 		goto error;
1362 	}
1363 
1364 	if ((tn = topo_node_bind(mod, pnode, nodename,
1365 	    instance, fmri)) == NULL) {
1366 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1367 		    topo_mod_errmsg(mod));
1368 		goto error;
1369 	}
1370 
1371 	/*
1372 	 * For the node label, we look for the following in order:
1373 	 *
1374 	 * 	<ses-description>
1375 	 * 	<ses-class-description> <instance>
1376 	 * 	<default-type-label> <instance>
1377 	 */
1378 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
1379 	    desc[0] == '\0') {
1380 		parent = ses_node_parent(np);
1381 		aprops = ses_node_props(parent);
1382 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
1383 		    &desc) != 0 || desc[0] == '\0')
1384 			desc = (char *)labelname;
1385 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
1386 		    instance);
1387 		desc = label;
1388 	}
1389 
1390 	if (topo_node_label_set(tn, desc, &err) != 0)
1391 		goto error;
1392 
1393 	if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np),
1394 	    snp->sen_target->set_devpath) != 0)
1395 		goto error;
1396 
1397 	if (strcmp(nodename, BAY) == 0) {
1398 		if (ses_add_bay_props(mod, tn, snp) != 0)
1399 			goto error;
1400 
1401 		if (ses_create_disk(sdp, tn, props) != 0)
1402 			goto error;
1403 
1404 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
1405 			topo_mod_dprintf(mod,
1406 			    "topo_method_register() failed: %s",
1407 			    topo_mod_errmsg(mod));
1408 			goto error;
1409 		}
1410 	} else if ((strcmp(nodename, FAN) == 0) ||
1411 	    (strcmp(nodename, PSU) == 0) ||
1412 	    (strcmp(nodename, CONTROLLER) == 0)) {
1413 		/*
1414 		 * Only fan, psu, and controller nodes have a 'present' method.
1415 		 * Bay nodes are always present, and disk nodes are present by
1416 		 * virtue of being enumerated and SAS expander nodes and
1417 		 * SAS connector nodes are also always present once
1418 		 * the parent controller is found.
1419 		 */
1420 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
1421 			topo_mod_dprintf(mod,
1422 			    "topo_method_register() failed: %s",
1423 			    topo_mod_errmsg(mod));
1424 			goto error;
1425 		}
1426 
1427 	}
1428 
1429 	snp->sen_target->set_refcount++;
1430 	topo_node_setspecific(tn, snp->sen_target);
1431 
1432 	nvlist_free(auth);
1433 	nvlist_free(fmri);
1434 	if (node != NULL) *node = tn;
1435 	return (0);
1436 
1437 error:
1438 	nvlist_free(auth);
1439 	nvlist_free(fmri);
1440 	return (-1);
1441 }
1442 
1443 /*
1444  * Create SAS expander specific props.
1445  */
1446 /*ARGSUSED*/
1447 static int
1448 ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1449     tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist)
1450 {
1451 	ses_node_t *np = snp->sen_node;
1452 	topo_mod_t *mod = sdp->sed_mod;
1453 	nvlist_t *auth = NULL, *fmri = NULL;
1454 	nvlist_t *props, **phylist;
1455 	int err, i;
1456 	uint_t pcount;
1457 	uint64_t sasaddr, connidx;
1458 	char sasaddr_str[17];
1459 	boolean_t found = B_FALSE, ses_found = B_FALSE;
1460 	dev_di_node_t *dnode, *sesdnode;
1461 
1462 	props = ses_node_props(np);
1463 
1464 	/*
1465 	 * the uninstalled expander is not enumerated by checking
1466 	 * the element status code.  No present present' method provided.
1467 	 */
1468 	/*
1469 	 * Get the Expander SAS address.  It should exist.
1470 	 */
1471 	if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR,
1472 	    &sasaddr) != 0) {
1473 		topo_mod_dprintf(mod,
1474 		    "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
1475 		goto error;
1476 	}
1477 
1478 	(void) sprintf(sasaddr_str, "%llx", sasaddr);
1479 
1480 	/* search matching dev_di_node. */
1481 	for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL;
1482 	    dnode = topo_list_next(dnode)) {
1483 		for (i = 0; i < dnode->ddn_ppath_count; i++) {
1484 			if ((dnode->ddn_target_port[i] != NULL) &&
1485 			    (strstr(dnode->ddn_target_port[i],
1486 			    sasaddr_str) != NULL)) {
1487 				found = B_TRUE;
1488 				break;
1489 			}
1490 		}
1491 		if (found)
1492 			break;
1493 	}
1494 
1495 	if (!found) {
1496 		topo_mod_dprintf(mod,
1497 		    "ses_set_expander_props: Failed to find matching "
1498 		    "devinfo node for Exapnder SAS address %s",
1499 		    SES_EXP_PROP_SAS_ADDR);
1500 		/* continue on to get storage group props. */
1501 	} else {
1502 		/* create/set the devfs-path and devid in the smp group */
1503 		if (topo_pgroup_create(tnode, &smp_pgroup, &err) != 0) {
1504 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1505 			    "failed to create smp property group %s\n",
1506 			    topo_strerror(err));
1507 			goto error;
1508 		} else {
1509 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1510 			    TOPO_PROP_SMP_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1511 			    dnode->ddn_target_port[i], &err) != 0) {
1512 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1513 				    "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1514 				    topo_strerror(err));
1515 			}
1516 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1517 			    TOPO_PROP_SMP_DEV_PATH, TOPO_PROP_IMMUTABLE,
1518 			    dnode->ddn_dpath, &err) != 0) {
1519 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1520 				    "set dev error %s\n", topo_strerror(err));
1521 			}
1522 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1523 			    TOPO_PROP_SMP_DEVID, TOPO_PROP_IMMUTABLE,
1524 			    dnode->ddn_devid, &err) != 0) {
1525 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1526 				    "set devid error %s\n", topo_strerror(err));
1527 			}
1528 			if (dnode->ddn_ppath_count != 0 &&
1529 			    topo_prop_set_string_array(tnode, TOPO_PGROUP_SMP,
1530 			    TOPO_PROP_SMP_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1531 			    (const char **)dnode->ddn_ppath,
1532 			    dnode->ddn_ppath_count, &err) != 0) {
1533 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1534 				    "set phys-path error %s\n",
1535 				    topo_strerror(err));
1536 			}
1537 		}
1538 	}
1539 
1540 	/* update the ses property group with SES target info */
1541 	if ((topo_pgroup_create(tnode, &ses_pgroup, &err) != 0) &&
1542 	    (err != ETOPO_PROP_DEFD)) {
1543 		/* SES prop group doesn't exist but failed to be created. */
1544 		topo_mod_dprintf(mod, "ses_set_expander_props: "
1545 		    "ses pgroup create error %s\n", topo_strerror(err));
1546 		goto error;
1547 	} else {
1548 		/* locate assciated enclosure dev_di_node. */
1549 		for (sesdnode = topo_list_next(&sdp->sed_devs);
1550 		    sesdnode != NULL; sesdnode = topo_list_next(sesdnode)) {
1551 			for (i = 0; i < sesdnode->ddn_ppath_count; i++) {
1552 				/*
1553 				 * check if attached port exists and
1554 				 * its node type is enclosure and
1555 				 * attached port is same as sas address of
1556 				 * the expander and
1557 				 * bridge port for virtual phy indication
1558 				 * exist.
1559 				 */
1560 				if ((sesdnode->ddn_attached_port[i] != NULL) &&
1561 				    (sesdnode->ddn_dtype == DTYPE_ESI) &&
1562 				    (strstr(sesdnode->ddn_attached_port[i],
1563 				    sasaddr_str) != NULL) &&
1564 				    (sesdnode->ddn_bridge_port[i] != NULL)) {
1565 					ses_found = B_TRUE;
1566 					break;
1567 				}
1568 			}
1569 			if (ses_found) break;
1570 		}
1571 
1572 		if (ses_found) {
1573 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1574 			    TOPO_PROP_SES_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1575 			    sesdnode->ddn_target_port[i], &err) != 0) {
1576 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1577 				    "set ses %S error %s\n", TOPO_PROP_SAS_ADDR,
1578 				    topo_strerror(err));
1579 			}
1580 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1581 			    TOPO_PROP_SES_DEV_PATH, TOPO_PROP_IMMUTABLE,
1582 			    sesdnode->ddn_dpath, &err) != 0) {
1583 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1584 				    "set ses dev error %s\n",
1585 				    topo_strerror(err));
1586 			}
1587 			if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1588 			    TOPO_PROP_SES_DEVID, TOPO_PROP_IMMUTABLE,
1589 			    sesdnode->ddn_devid, &err) != 0) {
1590 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1591 				    "set ses devid error %s\n",
1592 				    topo_strerror(err));
1593 			}
1594 			if (sesdnode->ddn_ppath_count != 0 &&
1595 			    topo_prop_set_string_array(tnode, TOPO_PGROUP_SES,
1596 			    TOPO_PROP_SES_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1597 			    (const char **)sesdnode->ddn_ppath,
1598 			    sesdnode->ddn_ppath_count, &err) != 0) {
1599 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1600 				    "set ses phys-path error %s\n",
1601 				    topo_strerror(err));
1602 			}
1603 
1604 		}
1605 	}
1606 
1607 	/* create the storage group */
1608 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1609 		topo_mod_dprintf(mod, "ses_set_expander_props: "
1610 		    "create storage error %s\n", topo_strerror(err));
1611 		goto error;
1612 	} else {
1613 		/* set the SAS address prop out of expander element status. */
1614 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1615 		    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str,
1616 		    &err) != 0) {
1617 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1618 			    "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1619 			    topo_strerror(err));
1620 		}
1621 
1622 		/* Get the phy information for the expander */
1623 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1624 		    &phylist, &pcount) != 0) {
1625 			topo_mod_dprintf(mod,
1626 			    "Failed to get prop %s.", SES_SAS_PROP_PHYS);
1627 		} else {
1628 			/*
1629 			 * For each phy, get the connector element index and
1630 			 * stores into connector element index array.
1631 			 */
1632 			*phycount = pcount;
1633 			for (i = 0; i < pcount; i++) {
1634 				if (nvlist_lookup_uint64(phylist[i],
1635 				    SES_PROP_CE_IDX, &connidx) == 0) {
1636 					if (connidx != 0xff) {
1637 						connlist[i] = connidx;
1638 					} else {
1639 						connlist[i] = -1;
1640 					}
1641 				} else {
1642 					/* Fail to get the index. set to -1. */
1643 					connlist[i] = -1;
1644 				}
1645 			}
1646 
1647 			/* set the phy count prop of the expander. */
1648 			if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE,
1649 			    TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount,
1650 			    &err) != 0) {
1651 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1652 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1653 				    topo_strerror(err));
1654 			}
1655 
1656 			/*
1657 			 * set the connector element index of
1658 			 * the expander phys.
1659 			 */
1660 		}
1661 
1662 		/* populate other misc storage group properties */
1663 		if (found) {
1664 			if (dnode->ddn_mfg && (topo_prop_set_string(tnode,
1665 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER,
1666 			    TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) {
1667 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1668 				    "set mfg error %s\n", topo_strerror(err));
1669 			}
1670 
1671 			if (dnode->ddn_model && (topo_prop_set_string(tnode,
1672 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL,
1673 			    TOPO_PROP_IMMUTABLE,
1674 			    dnode->ddn_model, &err) != 0)) {
1675 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1676 				    "set model error %s\n", topo_strerror(err));
1677 			}
1678 
1679 			if (dnode->ddn_serial && (topo_prop_set_string(tnode,
1680 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM,
1681 			    TOPO_PROP_IMMUTABLE,
1682 			    dnode->ddn_serial, &err) != 0)) {
1683 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1684 				    "set serial error %s\n",
1685 				    topo_strerror(err));
1686 			}
1687 
1688 			if (dnode->ddn_firm && (topo_prop_set_string(tnode,
1689 			    TOPO_PGROUP_STORAGE,
1690 			    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
1691 			    dnode->ddn_firm, &err) != 0)) {
1692 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1693 				    "set firm error %s\n", topo_strerror(err));
1694 			}
1695 		}
1696 	}
1697 
1698 	return (0);
1699 
1700 error:
1701 	nvlist_free(auth);
1702 	nvlist_free(fmri);
1703 	return (-1);
1704 }
1705 
1706 /*
1707  * Create SAS expander specific props.
1708  */
1709 /*ARGSUSED*/
1710 static int
1711 ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1712     tnode_t *tnode, int64_t phy_mask)
1713 {
1714 	ses_node_t *np = snp->sen_node;
1715 	topo_mod_t *mod = sdp->sed_mod;
1716 	nvlist_t *props;
1717 	int err, i;
1718 	uint64_t conntype;
1719 	char phymask_str[17], *conntype_str;
1720 	boolean_t   found;
1721 
1722 	props = ses_node_props(np);
1723 
1724 	/*
1725 	 * convert phy mask to string.
1726 	 */
1727 	(void) snprintf(phymask_str, 17, "%llx", phy_mask);
1728 
1729 	/* create the storage group */
1730 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1731 		topo_mod_dprintf(mod, "ses_set_expander_props: "
1732 		    "create storage error %s\n", topo_strerror(err));
1733 		return (-1);
1734 	} else {
1735 		/* set the SAS address prop of the expander. */
1736 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1737 		    TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE,
1738 		    phymask_str, &err) != 0) {
1739 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1740 			    "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
1741 			    topo_strerror(err));
1742 		}
1743 
1744 		/* Get the connector type information for the expander */
1745 		if (nvlist_lookup_uint64(props,
1746 		    SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
1747 			topo_mod_dprintf(mod, "Failed to get prop %s.",
1748 			    TOPO_STORAGE_SAS_PHY_MASK);
1749 		} else {
1750 			found = B_FALSE;
1751 			for (i = 0; ; i++) {
1752 				if (sas_connector_type_list[i].sct_type ==
1753 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1754 					break;
1755 				}
1756 				if (sas_connector_type_list[i].sct_type ==
1757 				    conntype) {
1758 					conntype_str =
1759 					    sas_connector_type_list[i].sct_name;
1760 					found = B_TRUE;
1761 					break;
1762 				}
1763 			}
1764 
1765 			if (!found) {
1766 				if (conntype <
1767 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1768 					conntype_str =
1769 					    SAS_CONNECTOR_TYPE_RESERVED;
1770 				} else {
1771 					conntype_str =
1772 					    SAS_CONNECTOR_TYPE_NOT_DEFINED;
1773 				}
1774 			}
1775 
1776 			/* set the phy count prop of the expander. */
1777 			if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1778 			    TOPO_STORAGE_SAS_CONNECTOR_TYPE,
1779 			    TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) {
1780 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1781 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1782 				    topo_strerror(err));
1783 			}
1784 		}
1785 	}
1786 
1787 	return (0);
1788 }
1789 
1790 /*
1791  * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1792  * nodes.
1793  */
1794 /*ARGSUSED*/
1795 static int
1796 ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1797     tnode_t *pnode, ses_enum_chassis_t *cp,
1798     boolean_t dorange)
1799 {
1800 	topo_mod_t *mod = sdp->sed_mod;
1801 	tnode_t	*exptn, *contn;
1802 	boolean_t found;
1803 	sas_connector_phy_data_t connectors[64] = {NULL};
1804 	uint64_t max;
1805 	ses_enum_node_t *ctlsnp, *xsnp, *consnp;
1806 	ses_node_t *np = snp->sen_node;
1807 	nvlist_t *props, *psprops;
1808 	uint64_t index, psindex, conindex, psstatus, i, j, count;
1809 	int64_t cidxlist[256] = {NULL};
1810 	int phycount;
1811 
1812 	props = ses_node_props(np);
1813 
1814 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
1815 	    &index) != 0)
1816 		return (-1);
1817 
1818 	/*
1819 	 * For SES constroller node, check to see if there are
1820 	 * associated SAS expanders.
1821 	 */
1822 	found = B_FALSE;
1823 	max = 0;
1824 	for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL;
1825 	    ctlsnp = topo_list_next(ctlsnp)) {
1826 		if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) {
1827 			found = B_TRUE;
1828 			if (ctlsnp->sen_instance > max)
1829 				max = ctlsnp->sen_instance;
1830 		}
1831 	}
1832 
1833 	/*
1834 	 * No SAS expander found notthing to process.
1835 	 */
1836 	if (!found)
1837 		return (0);
1838 
1839 	topo_mod_dprintf(mod, "%s Controller %d: creating "
1840 	    "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER);
1841 
1842 	/*
1843 	 * The max number represent the number of elements
1844 	 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1845 	 * of SET_ET_SAS_EXPANDER type element.
1846 	 *
1847 	 * There may be multiple ESC Electronics element(controllers)
1848 	 * within JBOD(typicall two for redundancy) and SAS expander
1849 	 * elements are associated with only one of them.  We are
1850 	 * still creating the range based max number here.
1851 	 * That will cover the case that all expanders are associated
1852 	 * with one SES controller.
1853 	 */
1854 	if (dorange && topo_node_range_create(mod, pnode,
1855 	    SASEXPANDER, 0, max) != 0) {
1856 		topo_mod_dprintf(mod,
1857 		    "topo_node_create_range() failed: %s",
1858 		    topo_mod_errmsg(mod));
1859 		return (-1);
1860 	}
1861 
1862 	/*
1863 	 * Search exapnders with the parent index matching with
1864 	 * ESC Electronics element index.
1865 	 * Note the index used here is a global index across
1866 	 * SES elements.
1867 	 */
1868 	for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL;
1869 	    xsnp = topo_list_next(xsnp)) {
1870 		if (xsnp->sen_type == SES_ET_SAS_EXPANDER) {
1871 			/*
1872 			 * get the parent ESC controller.
1873 			 */
1874 			psprops = ses_node_props(xsnp->sen_node);
1875 			if (nvlist_lookup_uint64(psprops,
1876 			    SES_PROP_STATUS_CODE, &psstatus) == 0) {
1877 				if (psstatus == SES_ESC_NOT_INSTALLED) {
1878 					/*
1879 					 * Not installed.
1880 					 * Don't create a ndoe.
1881 					 */
1882 					continue;
1883 				}
1884 			} else {
1885 				/*
1886 				 * The element should have status code.
1887 				 * If not there is no way to find
1888 				 * out if the expander element exist or
1889 				 * not.
1890 				 */
1891 				continue;
1892 			}
1893 
1894 			/* Get the physical parent index to compare. */
1895 			if (nvlist_lookup_uint64(psprops,
1896 			    LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
1897 				if (index == psindex) {
1898 		/* indentation moved forward */
1899 		/*
1900 		 * Handle basic node information of SAS expander
1901 		 * element - binding to parent node and
1902 		 * allocating FMRI...
1903 		 */
1904 		if (ses_create_generic(sdp, xsnp, pnode, pnode, SASEXPANDER,
1905 		    "SAS-EXPANDER", &exptn) != 0)
1906 			continue;
1907 		/*
1908 		 * Now handle SAS expander unique portion of node creation.
1909 		 * The max nubmer of the phy count is 256 since SES-2
1910 		 * defines as 1 byte field.  The cidxlist has the same
1911 		 * number of elements.
1912 		 *
1913 		 * We use size 64 array to store the connectors.
1914 		 * Typically a connectors associated with 4 phys so that
1915 		 * matches with the max number of connecters associated
1916 		 * with an expander.
1917 		 * The phy count goes up to 38 for Sun supported
1918 		 * JBOD.
1919 		 */
1920 		(void) memset(cidxlist, 0, sizeof (int64_t) * 64);
1921 		if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount,
1922 		    cidxlist) != 0) {
1923 			/*
1924 			 * error on getting specific prop failed.
1925 			 * continue on.  Note that the node is
1926 			 * left bound.
1927 			 */
1928 			continue;
1929 		}
1930 
1931 		/*
1932 		 * count represetns the number of connectors discovered so far.
1933 		 */
1934 		count = 0;
1935 		(void) memset(connectors, 0,
1936 		    sizeof (sas_connector_phy_data_t) * 64);
1937 		for (i = 0; i < phycount; i++) {
1938 			if (cidxlist[i] != -1) {
1939 				/* connector index is valid. */
1940 				for (j = 0; j < count; j++) {
1941 					if (connectors[j].scpd_index ==
1942 					    cidxlist[i]) {
1943 						/*
1944 						 * Just update phy mask.
1945 						 * The postion for connector
1946 						 * index lists(cidxlist index)
1947 						 * is set.
1948 						 */
1949 						connectors[j].scpd_pm =
1950 						    connectors[j].scpd_pm |
1951 						    (1ULL << i);
1952 						break;
1953 					}
1954 				}
1955 				/*
1956 				 * If j and count matche a  new connector
1957 				 * index is found.
1958 				 */
1959 				if (j == count) {
1960 					/* add a new index and phy mask. */
1961 					connectors[count].scpd_index =
1962 					    cidxlist[i];
1963 					connectors[count].scpd_pm =
1964 					    connectors[count].scpd_pm |
1965 					    (1ULL << i);
1966 					count++;
1967 				}
1968 			}
1969 		}
1970 
1971 		/*
1972 		 * create range for the connector nodes.
1973 		 * The class index of the ses connector element
1974 		 * is set as the instance nubmer for the node.
1975 		 * Even though one expander may not have all connectors
1976 		 * are associated with we are creating the range with
1977 		 * max possible instance number.
1978 		 */
1979 		found = B_FALSE;
1980 		max = 0;
1981 		for (consnp = topo_list_next(&cp->sec_nodes);
1982 		    consnp != NULL; consnp = topo_list_next(consnp)) {
1983 			if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1984 				psprops = ses_node_props(consnp->sen_node);
1985 				found = B_TRUE;
1986 				if (consnp->sen_instance > max)
1987 					max = consnp->sen_instance;
1988 			}
1989 		}
1990 
1991 		/*
1992 		 * No SAS connector found nothing to process.
1993 		 */
1994 		if (!found)
1995 			return (0);
1996 
1997 		if (dorange && topo_node_range_create(mod, exptn,
1998 		    RECEPTACLE, 0, max) != 0) {
1999 			topo_mod_dprintf(mod,
2000 			    "topo_node_create_range() failed: %s",
2001 			    topo_mod_errmsg(mod));
2002 			return (-1);
2003 		}
2004 
2005 		/* search matching connector element using the index. */
2006 		for (i = 0; i < count; i++) {
2007 			found = B_FALSE;
2008 			for (consnp = topo_list_next(&cp->sec_nodes);
2009 			    consnp != NULL; consnp = topo_list_next(consnp)) {
2010 				if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
2011 					psprops = ses_node_props(
2012 					    consnp->sen_node);
2013 					/*
2014 					 * Get the physical parent index to
2015 					 * compare.
2016 					 * The connector elements are children
2017 					 * of ESC Electronics element even
2018 					 * though we enumerate them under
2019 					 * an expander in libtopo.
2020 					 */
2021 					if (nvlist_lookup_uint64(psprops,
2022 					    SES_PROP_ELEMENT_ONLY_INDEX,
2023 					    &conindex) == 0) {
2024 						if (conindex ==
2025 						    connectors[i].scpd_index) {
2026 							found = B_TRUE;
2027 							break;
2028 						}
2029 					}
2030 				}
2031 			}
2032 
2033 			/* now create a libtopo node. */
2034 			if (found) {
2035 				/* Create generic props. */
2036 				if (ses_create_generic(sdp, consnp, exptn,
2037 				    topo_node_parent(exptn),
2038 				    RECEPTACLE, "RECEPTACLE", &contn) !=
2039 				    0) {
2040 					continue;
2041 				}
2042 				/* Create connector specific props. */
2043 				if (ses_set_connector_props(sdp, consnp,
2044 				    contn, connectors[i].scpd_pm) != 0) {
2045 					continue;
2046 				}
2047 			}
2048 		}
2049 		/* end indentation change */
2050 				}
2051 			}
2052 		}
2053 	}
2054 
2055 	return (0);
2056 }
2057 
2058 /*
2059  * Instantiate any protocol specific portion of a node.
2060  */
2061 /*ARGSUSED*/
2062 static int
2063 ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
2064     tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp,
2065     boolean_t dorange)
2066 {
2067 
2068 	if (type == SES_ET_ESC_ELECTRONICS) {
2069 		/* create SAS specific children(expanders and connectors. */
2070 		return (ses_create_esc_sasspecific(sdp, snp, pnode, cp,
2071 		    dorange));
2072 	}
2073 
2074 	return (0);
2075 }
2076 
2077 /*
2078  * Instantiate any children of a given type.
2079  */
2080 static int
2081 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
2082     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
2083     boolean_t dorange)
2084 {
2085 	topo_mod_t *mod = sdp->sed_mod;
2086 	boolean_t found;
2087 	uint64_t max;
2088 	ses_enum_node_t *snp;
2089 	tnode_t	*tn;
2090 
2091 	/*
2092 	 * First go through and count how many matching nodes we have.
2093 	 */
2094 	max = 0;
2095 	found = B_FALSE;
2096 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2097 	    snp = topo_list_next(snp)) {
2098 		if (snp->sen_type == type) {
2099 			found = B_TRUE;
2100 			if (snp->sen_instance > max)
2101 				max = snp->sen_instance;
2102 		}
2103 	}
2104 
2105 	/*
2106 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
2107 	 * Since we map both of these to 'disk', if an enclosure does this, we
2108 	 * just ignore the array elements.
2109 	 */
2110 	if (!found ||
2111 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
2112 		return (0);
2113 
2114 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
2115 	    cp->sec_csn, max + 1, nodename);
2116 
2117 	if (dorange && topo_node_range_create(mod, pnode,
2118 	    nodename, 0, max) != 0) {
2119 		topo_mod_dprintf(mod,
2120 		    "topo_node_create_range() failed: %s",
2121 		    topo_mod_errmsg(mod));
2122 		return (-1);
2123 	}
2124 
2125 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2126 	    snp = topo_list_next(snp)) {
2127 		if (snp->sen_type == type) {
2128 			/*
2129 			 * With flat layout of ses nodes there is no
2130 			 * way to find out the direct FRU for a node.
2131 			 * Passing NULL for fru topo node.  Note that
2132 			 * ses_create_children_from_phys_tree() provides
2133 			 * the actual direct FRU for a node.
2134 			 */
2135 			if (ses_create_generic(sdp, snp, pnode, NULL,
2136 			    nodename, defaultlabel, &tn) != 0)
2137 				return (-1);
2138 			/*
2139 			 * For some SES element there may be protocol specific
2140 			 * information to process.   Here we are processing
2141 			 * the association between enclosure controller and
2142 			 * SAS expanders.
2143 			 */
2144 			if (type == SES_ET_ESC_ELECTRONICS) {
2145 				/* create SAS expander node */
2146 				if (ses_create_protocol_specific(sdp, snp,
2147 				    tn, type, cp, dorange) != 0) {
2148 					return (-1);
2149 				}
2150 			}
2151 
2152 		}
2153 	}
2154 
2155 	return (0);
2156 }
2157 
2158 /*
2159  * Instantiate a new subchassis instance in the topology.
2160  */
2161 static int
2162 ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
2163     ses_enum_chassis_t *scp)
2164 {
2165 	topo_mod_t *mod = sdp->sed_mod;
2166 	tnode_t *tn;
2167 	nvlist_t *props;
2168 	nvlist_t *auth = NULL, *fmri = NULL;
2169 	uint64_t instance = scp->sec_instance;
2170 	char *desc;
2171 	char label[128];
2172 	char **paths;
2173 	int i, err;
2174 	ses_enum_target_t *stp;
2175 	int ret = -1;
2176 
2177 	/*
2178 	 * Copy authority information from parent enclosure node
2179 	 */
2180 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
2181 		goto error;
2182 
2183 	/*
2184 	 * Record the subchassis serial number in the FMRI.
2185 	 * For now, we assume that logical id is the subchassis serial number.
2186 	 * If this assumption changes in future, then the following
2187 	 * piece of code will need to be updated via an RFE.
2188 	 */
2189 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
2190 	    SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL,
2191 	    NULL)) == NULL) {
2192 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2193 		    topo_mod_errmsg(mod));
2194 		goto error;
2195 	}
2196 
2197 	if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
2198 	    instance, fmri)) == NULL) {
2199 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2200 		    topo_mod_errmsg(mod));
2201 		goto error;
2202 	}
2203 
2204 	props = ses_node_props(scp->sec_enclosure);
2205 
2206 	/*
2207 	 * Look for the subchassis label in the following order:
2208 	 *	<ses-description>
2209 	 *	<ses-class-description> <instance>
2210 	 *	<default-type-label> <instance>
2211 	 *
2212 	 * For subchassis, the default label is "SUBCHASSIS"
2213 	 */
2214 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
2215 	    desc[0] == '\0') {
2216 		if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
2217 		    &desc) == 0 && desc[0] != '\0')
2218 			(void) snprintf(label, sizeof (label), "%s %llu", desc,
2219 			    instance);
2220 		else
2221 			(void) snprintf(label, sizeof (label),
2222 			    "SUBCHASSIS %llu", instance);
2223 		desc = label;
2224 	}
2225 
2226 	if (topo_node_label_set(tn, desc, &err) != 0)
2227 		goto error;
2228 
2229 	if (ses_set_standard_props(mod, NULL, tn, NULL,
2230 	    ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
2231 		goto error;
2232 
2233 	/*
2234 	 * Set the 'chassis-type' property for this subchassis.  This is either
2235 	 * 'ses-class-description' or 'subchassis'.
2236 	 */
2237 	if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0)
2238 		desc = "subchassis";
2239 
2240 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
2241 	    TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) {
2242 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2243 		    TOPO_PROP_CHASSIS_TYPE, topo_strerror(err));
2244 		goto error;
2245 	}
2246 
2247 	/*
2248 	 * For enclosures, we want to include all possible targets (for upgrade
2249 	 * purposes).
2250 	 */
2251 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2252 	    stp = topo_list_next(stp), i++)
2253 		;
2254 
2255 	verify(i != 0);
2256 	paths = alloca(i * sizeof (char *));
2257 
2258 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2259 	    stp = topo_list_next(stp), i++)
2260 		paths[i] = stp->set_devpath;
2261 
2262 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2263 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2264 	    i, &err) != 0) {
2265 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2266 		    TOPO_PROP_PATHS, topo_strerror(err));
2267 		goto error;
2268 	}
2269 
2270 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2271 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
2272 		    topo_mod_errmsg(mod));
2273 		goto error;
2274 	}
2275 
2276 	/*
2277 	 * Create the nodes for controllers and bays.
2278 	 */
2279 	if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
2280 	    CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
2281 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
2282 	    BAY, "BAY", scp, B_TRUE) != 0 ||
2283 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
2284 	    BAY, "BAY", scp, B_TRUE) != 0)
2285 		goto error;
2286 
2287 	ret = 0;
2288 
2289 error:
2290 	nvlist_free(auth);
2291 	nvlist_free(fmri);
2292 	return (ret);
2293 }
2294 
2295 /*
2296  * Function we use to insert a node.
2297  */
2298 static int
2299 ses_phys_tree_insert(topo_mod_t *mod, ses_phys_tree_t **sproot,
2300     ses_phys_tree_t *child)
2301 {
2302 	uint64_t ppindex, eindex, pindex;
2303 	ses_phys_tree_t *node_ptr;
2304 	int ret = 0;
2305 
2306 	assert(sproot != NULL);
2307 	assert(child != NULL);
2308 
2309 	if (*sproot == NULL) {
2310 		*sproot = child;
2311 		return (0);
2312 	}
2313 
2314 	pindex = child->spt_pindex;
2315 	ppindex = (*sproot)->spt_pindex;
2316 	eindex = (*sproot)->spt_eonlyindex;
2317 
2318 	/*
2319 	 * If the element only index of the root is same as the physical
2320 	 * parent index of a node to be added, add the node as a child of
2321 	 * the current root.
2322 	 */
2323 	if (eindex == pindex) {
2324 		(void) ses_phys_tree_insert(mod, &(*sproot)->spt_child, child);
2325 		child->spt_parent = *sproot;
2326 	} else if (ppindex == pindex) {
2327 		/*
2328 		 * if the physical parent of the current root and the child
2329 		 * is same, then this should be a sibling node.
2330 		 * Siblings can be different element types and arrange
2331 		 * them by group.
2332 		 */
2333 		if ((*sproot)->spt_senumnode->sen_type ==
2334 		    child->spt_senumnode->sen_type) {
2335 			child->spt_sibling = *sproot;
2336 			*sproot = child;
2337 		} else {
2338 			/* add a node in front of matching element type. */
2339 			node_ptr = *sproot;
2340 			while (node_ptr->spt_sibling != NULL) {
2341 				if (node_ptr->spt_sibling->
2342 				    spt_senumnode->sen_type ==
2343 				    child->spt_senumnode->sen_type) {
2344 					child->spt_sibling =
2345 					    node_ptr->spt_sibling;
2346 					node_ptr->spt_sibling = child;
2347 					break;
2348 				}
2349 				node_ptr = node_ptr->spt_sibling;
2350 			}
2351 			/* no matching.  Add the child at the end. */
2352 			if (node_ptr->spt_sibling == NULL) {
2353 				node_ptr->spt_sibling = child;
2354 			}
2355 		}
2356 		child->spt_parent = (*sproot)->spt_parent;
2357 	} else {
2358 		/*
2359 		 * The root and the node is not directly related.
2360 		 * Try to insert to the child sub-tree first and then try to
2361 		 * insert to the sibling sub-trees.  If fails for both
2362 		 * the caller will retry insertion later.
2363 		 */
2364 		if ((*sproot)->spt_child) {
2365 			ret = ses_phys_tree_insert(mod, &(*sproot)->spt_child,
2366 			    child);
2367 		}
2368 		if ((*sproot)->spt_child == NULL || ret != 0) {
2369 			if ((*sproot)->spt_sibling) {
2370 				ret = ses_phys_tree_insert(mod,
2371 				    &(*sproot)->spt_sibling, child);
2372 			} else {
2373 				ret = 1;
2374 			}
2375 		}
2376 		return (ret);
2377 	}
2378 	return (0);
2379 }
2380 
2381 /*
2382  * Construct tree view of ses elements through parent phyiscal element index.
2383  * The root of tree is already constructed using the enclosure element.
2384  */
2385 static int
2386 ses_construct_phys_tree(ses_enum_data_t *sdp, ses_enum_chassis_t *cp,
2387     ses_phys_tree_t *sproot)
2388 {
2389 	ses_enum_node_t *snp;
2390 	ses_phys_tree_t	*child;
2391 	ses_phys_tree_t	*u_watch = NULL;
2392 	ses_phys_tree_t	*u_head = NULL;
2393 	ses_phys_tree_t	*u_tail = NULL;
2394 	int u_inserted = 0, u_left = 0;
2395 	nvlist_t *props;
2396 	topo_mod_t *mod = sdp->sed_mod;
2397 
2398 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2399 	    snp = topo_list_next(snp)) {
2400 		if ((child = topo_mod_zalloc(mod,
2401 		    sizeof (ses_phys_tree_t))) == NULL) {
2402 			topo_mod_dprintf(mod,
2403 			    "failed to allocate root.");
2404 			return (-1);
2405 		}
2406 		child->spt_snode = snp->sen_node;
2407 		props = ses_node_props(snp->sen_node);
2408 		if (nvlist_lookup_uint64(props,
2409 		    LIBSES_PROP_PHYS_PARENT, &child->spt_pindex) != 0) {
2410 			/*
2411 			 * the prop should exist. continue to see if
2412 			 * we can build a partial tree with other elements.
2413 			 */
2414 			topo_mod_dprintf(mod,
2415 			    "ses_construct_phys_tree(): Failed to find prop %s "
2416 			    "on ses element type %d and instance %d "
2417 			    "(CSN %s).", LIBSES_PROP_PHYS_PARENT,
2418 			    snp->sen_type, snp->sen_instance, cp->sec_csn);
2419 			topo_mod_free(mod, child, sizeof (ses_phys_tree_t));
2420 			continue;
2421 		} else {
2422 			if (nvlist_lookup_boolean_value(props,
2423 			    LIBSES_PROP_FRU, &child->spt_isfru) != 0) {
2424 				topo_mod_dprintf(mod,
2425 				    "ses_construct_phys_tree(): Failed to "
2426 				    "find prop %s on ses element type %d "
2427 				    "and instance %d (CSN %s).",
2428 				    LIBSES_PROP_FRU,
2429 				    snp->sen_type, snp->sen_instance,
2430 				    cp->sec_csn);
2431 				/*
2432 				 * Ignore if the prop doesn't exist.
2433 				 * Note that the enclosure itself should be
2434 				 * a FRU so if no FRU found the enclosure FRU
2435 				 * can be a direct FRU.
2436 				 */
2437 			}
2438 			verify(nvlist_lookup_uint64(props,
2439 			    SES_PROP_ELEMENT_ONLY_INDEX,
2440 			    &child->spt_eonlyindex) == 0);
2441 			verify(nvlist_lookup_uint64(props,
2442 			    SES_PROP_ELEMENT_CLASS_INDEX,
2443 			    &child->spt_cindex) == 0);
2444 		}
2445 		child->spt_senumnode = snp;
2446 		if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2447 			/* collect unresolved element to process later. */
2448 			if (u_head == NULL) {
2449 				u_head = child;
2450 				u_tail = child;
2451 			} else {
2452 				child->spt_sibling = u_head;
2453 				u_head = child;
2454 			}
2455 		}
2456 	}
2457 
2458 	/*
2459 	 * The parent of a child node may not be inserted yet.
2460 	 * Trying to insert the child until no child is left or
2461 	 * no child is not added further.  For the latter
2462 	 * the hierarchical relationship between elements
2463 	 * should be checked through SUNW,FRUID page.
2464 	 * u_watch is a watch dog to check the prgress of unresolved
2465 	 * node.
2466 	 */
2467 	u_watch = u_tail;
2468 	while (u_head) {
2469 		child = u_head;
2470 		u_head = u_head->spt_sibling;
2471 		if (u_head == NULL)
2472 			u_tail = NULL;
2473 		child->spt_sibling = NULL;
2474 		if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2475 			u_tail->spt_sibling = child;
2476 			u_tail = child;
2477 			if (child == u_watch) {
2478 				/*
2479 				 * We just scanned one round for the
2480 				 * unresolved list. Check to see whether we
2481 				 * have nodes inserted, if none, we should
2482 				 * break in case of an indefinite loop.
2483 				 */
2484 				if (u_inserted == 0) {
2485 					/*
2486 					 * Indicate there is unhandled node.
2487 					 * Chain free the whole unsolved
2488 					 * list here.
2489 					 */
2490 					u_left++;
2491 					break;
2492 				} else {
2493 					u_inserted = 0;
2494 					u_watch = u_tail;
2495 				}
2496 			}
2497 		} else {
2498 			/*
2499 			 * We just inserted one rpnode, increment the
2500 			 * unsolved_inserted counter. We will utilize this
2501 			 * counter to detect an indefinite insertion loop.
2502 			 */
2503 			u_inserted++;
2504 			if (child == u_watch) {
2505 				/*
2506 				 * watch dog node itself is inserted.
2507 				 * Set it to the tail and refresh the watching.
2508 				 */
2509 				u_watch = u_tail;
2510 				u_inserted = 0;
2511 				u_left = 0;
2512 			}
2513 		}
2514 	}
2515 
2516 	/* check if there is left out unresolved nodes. */
2517 	if (u_left) {
2518 		topo_mod_dprintf(mod, "ses_construct_phys_tree(): "
2519 		    "Failed to construct physical view of the following "
2520 		    "ses elements of Chassis CSN %s.", cp->sec_csn);
2521 		while (u_head) {
2522 			u_tail = u_head->spt_sibling;
2523 			topo_mod_dprintf(mod,
2524 			    "\telement type (%d) and instance (%d)",
2525 			    u_head->spt_senumnode->sen_type,
2526 			    u_head->spt_senumnode->sen_instance);
2527 			topo_mod_free(mod, u_head, sizeof (ses_phys_tree_t));
2528 			u_head = u_tail;
2529 		}
2530 		return (-1);
2531 	}
2532 
2533 	return (0);
2534 }
2535 
2536 /*
2537  * Free the whole phys tree.
2538  */
2539 static void ses_phys_tree_free(topo_mod_t *mod, ses_phys_tree_t *sproot)
2540 {
2541 	if (sproot == NULL)
2542 		return;
2543 
2544 	/* Free child tree. */
2545 	if (sproot->spt_child) {
2546 		ses_phys_tree_free(mod, sproot->spt_child);
2547 	}
2548 
2549 	/* Free sibling trees. */
2550 	if (sproot->spt_sibling) {
2551 		ses_phys_tree_free(mod, sproot->spt_sibling);
2552 	}
2553 
2554 	/* Free root node itself. */
2555 	topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2556 }
2557 
2558 /*
2559  * Parses phys_enum_type table to get the index of the given type.
2560  */
2561 static boolean_t
2562 is_type_enumerated(ses_phys_tree_t *node, int *index)
2563 {
2564 	int i;
2565 
2566 	for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2567 		if (node->spt_senumnode->sen_type ==
2568 		    phys_enum_type_list[i].pet_type) {
2569 			*index = i;
2570 			return (B_TRUE);
2571 		}
2572 	}
2573 	return (B_FALSE);
2574 }
2575 
2576 /*
2577  * Recusrive routine for top-down enumeration of the tree.
2578  */
2579 static int
2580 ses_enumerate_node(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp,
2581     ses_phys_tree_t *parent, int mrange[])
2582 {
2583 	topo_mod_t *mod = sdp->sed_mod;
2584 	ses_phys_tree_t *child = NULL;
2585 	int i, ret = 0, ret_ch;
2586 	uint64_t prevtype = SES_ET_UNSPECIFIED;
2587 	ses_phys_tree_t *dirfru = NULL;
2588 	tnode_t *tn = NULL, *frutn = NULL;
2589 
2590 	if (parent == NULL) {
2591 		return (0);
2592 	}
2593 
2594 	for (child = parent->spt_child; child != NULL;
2595 	    child = child->spt_sibling) {
2596 		if (is_type_enumerated(child, &i)) {
2597 			if (prevtype != phys_enum_type_list[i].pet_type) {
2598 				/* check if range needs to be created. */
2599 				if (phys_enum_type_list[i].pet_dorange &&
2600 				    topo_node_range_create(mod, pnode,
2601 				    phys_enum_type_list[i].pet_nodename, 0,
2602 				    mrange[i]) != 0) {
2603 					topo_mod_dprintf(mod,
2604 					    "topo_node_create_range() failed: "
2605 					    "%s", topo_mod_errmsg(mod));
2606 					return (-1);
2607 				}
2608 				prevtype = phys_enum_type_list[i].pet_type;
2609 			}
2610 
2611 			if (!(child->spt_isfru)) {
2612 				for (dirfru = parent; dirfru != NULL;
2613 				    dirfru = dirfru->spt_parent) {
2614 					if (dirfru->spt_isfru) {
2615 						break;
2616 					}
2617 				}
2618 				/* found direct FRU node. */
2619 				if (dirfru) {
2620 					frutn = dirfru->spt_tnode;
2621 				} else {
2622 					frutn = NULL;
2623 				}
2624 			} else {
2625 				frutn = NULL;
2626 			}
2627 
2628 			if (ses_create_generic(sdp, child->spt_senumnode,
2629 			    pnode, frutn, phys_enum_type_list[i].pet_nodename,
2630 			    phys_enum_type_list[i].pet_defaultlabel, &tn) != 0)
2631 				return (-1);
2632 
2633 			child->spt_tnode = tn;
2634 			/*
2635 			 * For some SES element there may be protocol specific
2636 			 * information to process.   Here we are processing
2637 			 * the association between enclosure controller and
2638 			 * SAS expanders.
2639 			 */
2640 			if (phys_enum_type_list[i].pet_type ==
2641 			    SES_ET_ESC_ELECTRONICS) {
2642 				/* create SAS expander node */
2643 				if (ses_create_protocol_specific(sdp,
2644 				    child->spt_senumnode, tn,
2645 				    phys_enum_type_list[i].pet_type,
2646 				    cp, phys_enum_type_list[i].pet_dorange) !=
2647 				    0) {
2648 					return (-1);
2649 				}
2650 			}
2651 		} else {
2652 			continue;
2653 		}
2654 		ret_ch = ses_enumerate_node(sdp, tn, cp, child, mrange);
2655 		if (ret_ch)
2656 			ret = ret_ch; /* there was an error and set the ret. */
2657 	}
2658 
2659 	return (ret);
2660 }
2661 
2662 /*
2663  * Instantiate types of nodes that are specified in the hierarchy
2664  * element type list.
2665  */
2666 static int
2667 ses_create_children_from_phys_tree(ses_enum_data_t *sdp, tnode_t *pnode,
2668     ses_enum_chassis_t *cp, ses_phys_tree_t *phys_tree)
2669 {
2670 	topo_mod_t *mod = sdp->sed_mod;
2671 	int mrange[N_PHYS_ENUM_TYPES] = { 0 };
2672 	ses_enum_node_t *snp;
2673 	int i, ret;
2674 
2675 	/*
2676 	 * First get max range for each type of element to be enumerated.
2677 	 */
2678 	for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2679 		if (phys_enum_type_list[i].pet_dorange) {
2680 			for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2681 			    snp = topo_list_next(snp)) {
2682 				if (snp->sen_type ==
2683 				    phys_enum_type_list[i].pet_type) {
2684 					if (snp->sen_instance > mrange[i])
2685 						mrange[i] =
2686 						    snp->sen_instance;
2687 				}
2688 			}
2689 		}
2690 	}
2691 
2692 	topo_mod_dprintf(mod, "%s: creating nodes from FRU hierarchy tree.",
2693 	    cp->sec_csn);
2694 
2695 	if ((ret = ses_enumerate_node(sdp, pnode, cp, phys_tree, mrange)) !=
2696 	    0) {
2697 		topo_mod_dprintf(mod,
2698 		    "ses_create_children_from_phys_tree() failed: ");
2699 		return (ret);
2700 	}
2701 
2702 	return (0);
2703 }
2704 
2705 /*
2706  * Instantiate a new chassis instance in the topology.
2707  */
2708 static int
2709 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
2710 {
2711 	topo_mod_t *mod = sdp->sed_mod;
2712 	nvlist_t *props;
2713 	char *raw_manufacturer, *raw_model, *raw_revision;
2714 	char *manufacturer = NULL, *model = NULL, *product = NULL;
2715 	char *revision = NULL;
2716 	char *serial;
2717 	char **paths;
2718 	size_t prodlen;
2719 	tnode_t *tn;
2720 	nvlist_t *fmri = NULL, *auth = NULL;
2721 	int ret = -1;
2722 	ses_enum_node_t *snp;
2723 	ses_enum_target_t *stp;
2724 	ses_enum_chassis_t *scp;
2725 	int i, err;
2726 	uint64_t sc_count = 0, pindex;
2727 	ses_phys_tree_t	*sproot = NULL;
2728 	hrtime_t start;
2729 	hrtime_t end;
2730 	double duration;
2731 
2732 	/*
2733 	 * Ignore any internal enclosures.
2734 	 */
2735 	if (cp->sec_internal)
2736 		return (0);
2737 
2738 	/*
2739 	 * Check to see if there are any devices presennt in the chassis.  If
2740 	 * not, ignore the chassis alltogether.  This is most useful for
2741 	 * ignoring internal HBAs that present a SES target but don't actually
2742 	 * manage any of the devices.
2743 	 */
2744 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2745 	    snp = topo_list_next(snp)) {
2746 		if (snp->sen_type == SES_ET_DEVICE ||
2747 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
2748 			break;
2749 	}
2750 
2751 	if (snp == NULL)
2752 		return (0);
2753 
2754 	props = ses_node_props(cp->sec_enclosure);
2755 
2756 	/*
2757 	 * We use the following property mappings:
2758 	 *
2759 	 * 	manufacturer		vendor-id
2760 	 * 	model			product-id
2761 	 * 	serial-number		libses-chassis-serial
2762 	 */
2763 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
2764 	    &raw_manufacturer) == 0);
2765 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
2766 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
2767 	    &raw_revision) == 0);
2768 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
2769 
2770 	/*
2771 	 * To construct the authority information, we 'clean' each string by
2772 	 * removing any offensive characters and trimmming whitespace.  For the
2773 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
2774 	 * also take the numerical serial number and convert it to a string.
2775 	 */
2776 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
2777 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
2778 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
2779 		goto error;
2780 	}
2781 
2782 	prodlen = strlen(manufacturer) + strlen(model) + 2;
2783 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
2784 		goto error;
2785 
2786 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
2787 
2788 	/*
2789 	 * Construct the topo node and bind it to our parent.
2790 	 */
2791 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
2792 		goto error;
2793 
2794 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
2795 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
2796 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
2797 		goto error;
2798 	}
2799 
2800 	/*
2801 	 * We pass NULL for the parent FMRI because there is no resource
2802 	 * associated with it.  For the toplevel enclosure, we leave the
2803 	 * serial/part/revision portions empty, which are reserved for
2804 	 * individual components within the chassis.
2805 	 */
2806 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
2807 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
2808 	    model, revision, serial)) == NULL) {
2809 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2810 		    topo_mod_errmsg(mod));
2811 		goto error;
2812 	}
2813 
2814 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
2815 	    cp->sec_instance, fmri)) == NULL) {
2816 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2817 		    topo_mod_errmsg(mod));
2818 		goto error;
2819 	}
2820 
2821 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2822 		topo_mod_dprintf(mod,
2823 		    "topo_method_register() failed: %s",
2824 		    topo_mod_errmsg(mod));
2825 		goto error;
2826 	}
2827 
2828 	if (ses_set_standard_props(mod, NULL, tn, auth,
2829 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
2830 		goto error;
2831 
2832 	/*
2833 	 * For enclosures, we want to include all possible targets (for upgrade
2834 	 * purposes).
2835 	 */
2836 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2837 	    stp = topo_list_next(stp), i++)
2838 		;
2839 
2840 	verify(i != 0);
2841 	paths = alloca(i * sizeof (char *));
2842 
2843 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2844 	    stp = topo_list_next(stp), i++)
2845 		paths[i] = stp->set_devpath;
2846 
2847 
2848 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2849 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2850 	    i, &err) != 0) {
2851 		topo_mod_dprintf(mod,
2852 		    "failed to create property %s: %s\n",
2853 		    TOPO_PROP_PATHS, topo_strerror(err));
2854 		goto error;
2855 	}
2856 
2857 	if (nvlist_lookup_uint64(props,
2858 	    LIBSES_PROP_PHYS_PARENT, &pindex) == 0) {
2859 		start = gethrtime(); /* to mearusre performance */
2860 		/*
2861 		 * The enclosure is supported through SUNW,FRUID.
2862 		 * Need to enumerate the nodes through hierarchical order.
2863 		 */
2864 		if ((sproot = topo_mod_zalloc(mod,
2865 		    sizeof (ses_phys_tree_t))) == NULL) {
2866 			topo_mod_dprintf(mod,
2867 			    "failed to allocate root: %s\n",
2868 			    topo_strerror(err));
2869 			goto error;
2870 		}
2871 		sproot->spt_pindex = pindex;
2872 		if (nvlist_lookup_boolean_value(props,
2873 		    LIBSES_PROP_FRU, &sproot->spt_isfru) != 0) {
2874 			topo_mod_dprintf(mod,
2875 			    "ses_create_chassis(): Failed to find prop %s "
2876 			    "on enclosure element (CSN %s).",
2877 			    LIBSES_PROP_FRU, cp->sec_csn);
2878 			/* an enclosure should be a FRU. continue to process. */
2879 			sproot->spt_isfru = B_TRUE;
2880 		}
2881 		if (nvlist_lookup_uint64(props,
2882 		    SES_PROP_ELEMENT_ONLY_INDEX,
2883 		    &sproot->spt_eonlyindex) != 0) {
2884 			topo_mod_dprintf(mod,
2885 			    "ses_create_chassis(): Failed to find prop %s "
2886 			    "on enclosure element (CSN %s).",
2887 			    LIBSES_PROP_PHYS_PARENT, cp->sec_csn);
2888 			topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2889 			goto error;
2890 		}
2891 		if (sproot->spt_pindex != sproot->spt_eonlyindex) {
2892 			topo_mod_dprintf(mod, "ses_create_chassis(): "
2893 			    "Enclosure element(CSN %s) should have "
2894 			    "itself as the parent to be the root node "
2895 			    "of FRU hierarchical tree.)", cp->sec_csn);
2896 			topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2897 			goto error;
2898 		} else {
2899 			sproot->spt_snode = cp->sec_enclosure;
2900 			sproot->spt_tnode = tn;
2901 			/* construct a tree. */
2902 			if (ses_construct_phys_tree(sdp, cp, sproot) != 0) {
2903 				topo_mod_dprintf(mod, "ses_create_chassis(): "
2904 				    "Failed to construct FRU hierarchical "
2905 				    "tree on enclosure (CSN %s.)",
2906 				    cp->sec_csn);
2907 			}
2908 
2909 			/* enumerate elements from the tree. */
2910 			if (ses_create_children_from_phys_tree(sdp, tn, cp,
2911 			    sproot) != 0) {
2912 				topo_mod_dprintf(mod, "ses_create_chassis(): "
2913 				    "Failed to create children topo nodes out "
2914 				    "of FRU hierarchical tree on enclosure "
2915 				    "(CSN %s).", cp->sec_csn);
2916 			}
2917 			/* destroy the phys tree. */
2918 			ses_phys_tree_free(mod, sproot);
2919 		}
2920 
2921 		end = gethrtime();
2922 		duration = end - start;
2923 		duration /= HR_SECOND;
2924 		topo_mod_dprintf(mod,
2925 		    "FRU boundary tree based enumeration: %.6f seconds",
2926 		    duration);
2927 	} else {
2928 		/*
2929 		 * Create the nodes for power supplies, fans, controllers and
2930 		 * devices.  Note that SAS exopander nodes and connector nodes
2931 		 * are handled through protocol specific processing of
2932 		 * controllers.
2933 		 */
2934 		if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
2935 		    PSU, "PSU", cp, B_TRUE) != 0 ||
2936 		    ses_create_children(sdp, tn, SES_ET_COOLING,
2937 		    FAN, "FAN", cp, B_TRUE) != 0 ||
2938 		    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
2939 		    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
2940 		    ses_create_children(sdp, tn, SES_ET_DEVICE,
2941 		    BAY, "BAY", cp, B_TRUE) != 0 ||
2942 		    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
2943 		    BAY, "BAY", cp, B_TRUE) != 0)
2944 			goto error;
2945 	}
2946 
2947 	if (cp->sec_maxinstance >= 0 &&
2948 	    (topo_node_range_create(mod, tn, SUBCHASSIS, 0,
2949 	    cp->sec_maxinstance) != 0)) {
2950 		topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
2951 		    topo_mod_errmsg(mod));
2952 		goto error;
2953 	}
2954 
2955 	for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
2956 	    scp = topo_list_next(scp)) {
2957 
2958 		if (ses_create_subchassis(sdp, tn, scp) != 0)
2959 			goto error;
2960 
2961 		topo_mod_dprintf(mod, "created Subchassis node with "
2962 		    "instance %u\nand target (%s) under Chassis with CSN %s",
2963 		    scp->sec_instance, scp->sec_target->set_devpath,
2964 		    cp->sec_csn);
2965 
2966 		sc_count++;
2967 	}
2968 
2969 	topo_mod_dprintf(mod, "%s: created %llu %s nodes",
2970 	    cp->sec_csn, sc_count, SUBCHASSIS);
2971 
2972 	cp->sec_target->set_refcount++;
2973 	topo_node_setspecific(tn, cp->sec_target);
2974 
2975 	ret = 0;
2976 error:
2977 	topo_mod_strfree(mod, manufacturer);
2978 	topo_mod_strfree(mod, model);
2979 	topo_mod_strfree(mod, revision);
2980 	topo_mod_strfree(mod, product);
2981 
2982 	nvlist_free(fmri);
2983 	nvlist_free(auth);
2984 	return (ret);
2985 }
2986 
2987 /*
2988  * Create a bay node explicitly enumerated via XML.
2989  */
2990 static int
2991 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
2992 {
2993 	topo_mod_t *mod = sdp->sed_mod;
2994 	ses_enum_chassis_t *cp;
2995 
2996 	/*
2997 	 * Iterate over chassis looking for an internal enclosure.  This
2998 	 * property is set via a vendor-specific plugin, and there should only
2999 	 * ever be a single internal chassis in a system.
3000 	 */
3001 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3002 	    cp = topo_list_next(cp)) {
3003 		if (cp->sec_internal)
3004 			break;
3005 	}
3006 
3007 	if (cp == NULL) {
3008 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
3009 		return (-1);
3010 	}
3011 
3012 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
3013 	    BAY, "BAY", cp, B_FALSE) != 0 ||
3014 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
3015 	    BAY, "BAY", cp, B_FALSE) != 0)
3016 		return (-1);
3017 
3018 	return (0);
3019 }
3020 
3021 /*
3022  * Initialize chassis or subchassis.
3023  */
3024 static int
3025 ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
3026     ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
3027     uint64_t subchassis, ses_chassis_type_e flags)
3028 {
3029 	boolean_t internal, ident;
3030 
3031 	assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
3032 	    SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
3033 
3034 	assert(cp != NULL);
3035 	assert(np != NULL);
3036 	assert(props != NULL);
3037 
3038 	if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
3039 		assert(pcp != NULL);
3040 
3041 	topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)",
3042 	    sdp->sed_name, subchassis, flags);
3043 
3044 	if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
3045 
3046 		topo_mod_dprintf(mod, "new chassis/subchassis");
3047 		if (nvlist_lookup_boolean_value(props,
3048 		    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
3049 			cp->sec_internal = internal;
3050 
3051 		cp->sec_enclosure = np;
3052 		cp->sec_target = sdp->sed_target;
3053 
3054 		if (flags & SES_NEW_CHASSIS) {
3055 			if (!cp->sec_internal)
3056 				cp->sec_instance = sdp->sed_instance++;
3057 			topo_list_append(&sdp->sed_chassis, cp);
3058 		} else {
3059 			if (subchassis != NO_SUBCHASSIS)
3060 				cp->sec_instance = subchassis;
3061 			else
3062 				cp->sec_instance = pcp->sec_scinstance++;
3063 
3064 			if (cp->sec_instance > pcp->sec_maxinstance)
3065 				pcp->sec_maxinstance = cp->sec_instance;
3066 
3067 			topo_list_append(&pcp->sec_subchassis, cp);
3068 		}
3069 
3070 	} else {
3071 		topo_mod_dprintf(mod, "dup chassis/subchassis");
3072 		if (nvlist_lookup_boolean_value(props,
3073 		    SES_PROP_IDENT, &ident) == 0) {
3074 			topo_mod_dprintf(mod,  "overriding enclosure node");
3075 
3076 			cp->sec_enclosure = np;
3077 			cp->sec_target = sdp->sed_target;
3078 		}
3079 	}
3080 
3081 	topo_list_append(&cp->sec_targets, sdp->sed_target);
3082 	sdp->sed_current = cp;
3083 
3084 	return (0);
3085 }
3086 
3087 /*
3088  * Gather nodes from the current SES target into our chassis list, merging the
3089  * results if necessary.
3090  */
3091 static ses_walk_action_t
3092 ses_enum_gather(ses_node_t *np, void *data)
3093 {
3094 	nvlist_t *props = ses_node_props(np);
3095 	ses_enum_data_t *sdp = data;
3096 	topo_mod_t *mod = sdp->sed_mod;
3097 	ses_enum_chassis_t *cp, *scp;
3098 	ses_enum_node_t *snp;
3099 	ses_alt_node_t *sap;
3100 	char *csn;
3101 	uint64_t instance, type;
3102 	uint64_t prevstatus, status;
3103 	boolean_t report;
3104 	uint64_t subchassis = NO_SUBCHASSIS;
3105 
3106 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
3107 		/*
3108 		 * If we have already identified the chassis for this target,
3109 		 * then this is a secondary enclosure and we should ignore it,
3110 		 * along with the rest of the tree (since this is depth-first).
3111 		 */
3112 		if (sdp->sed_current != NULL)
3113 			return (SES_WALK_ACTION_TERMINATE);
3114 
3115 		/*
3116 		 * Go through the list of chassis we have seen so far and see
3117 		 * if this serial number matches one of the known values.
3118 		 * If so, check whether this enclosure is a subchassis.
3119 		 */
3120 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
3121 		    &csn) != 0)
3122 			return (SES_WALK_ACTION_TERMINATE);
3123 
3124 		(void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
3125 		    &subchassis);
3126 
3127 		topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
3128 		    "CSN (%s), subchassis (%llu)", sdp->sed_name, csn,
3129 		    subchassis);
3130 
3131 		/*
3132 		 * We need to determine whether this enclosure node
3133 		 * represents a chassis or a subchassis. Since we may
3134 		 * receive the enclosure nodes in a non-deterministic
3135 		 * manner, we need to account for all possible combinations:
3136 		 *	1. Chassis for the current CSN has not yet been
3137 		 *	   allocated
3138 		 *		1.1 This is a new chassis:
3139 		 *			allocate and instantiate the chassis
3140 		 *		1.2 This is a new subchassis:
3141 		 *			allocate a placeholder chassis
3142 		 *			allocate and instantiate the subchassis
3143 		 *			link the subchassis to the chassis
3144 		 *	2. Chassis for the current CSN has been allocated
3145 		 *		2.1 This is a duplicate chassis enclosure
3146 		 *			check whether to override old chassis
3147 		 *			append to chassis' target list
3148 		 *		2.2 Only placeholder chassis exists
3149 		 *			fill in the chassis fields
3150 		 *		2.3 This is a new subchassis
3151 		 *			allocate and instantiate the subchassis
3152 		 *			link the subchassis to the chassis
3153 		 *		2.4 This is a duplicate subchassis enclosure
3154 		 *			 check whether to override old chassis
3155 		 *			 append to chassis' target list
3156 		 */
3157 
3158 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3159 		    cp = topo_list_next(cp))
3160 			if (strcmp(cp->sec_csn, csn) == 0)
3161 				break;
3162 
3163 		if (cp == NULL) {
3164 			/* 1. Haven't seen a chassis with this CSN before */
3165 
3166 			if ((cp = topo_mod_zalloc(mod,
3167 			    sizeof (ses_enum_chassis_t))) == NULL)
3168 				goto error;
3169 
3170 			cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
3171 			cp->sec_maxinstance = -1;
3172 			cp->sec_csn = csn;
3173 
3174 			if (subchassis == NO_SUBCHASSIS) {
3175 				/* 1.1 This is a new chassis */
3176 
3177 				topo_mod_dprintf(mod, "%s: Initialize new "
3178 				    "chassis with CSN %s", sdp->sed_name, csn);
3179 
3180 				if (ses_init_chassis(mod, sdp, NULL, cp,
3181 				    np, props, NO_SUBCHASSIS,
3182 				    SES_NEW_CHASSIS) < 0)
3183 					goto error;
3184 			} else {
3185 				/* 1.2 This is a new subchassis */
3186 
3187 				topo_mod_dprintf(mod, "%s: Initialize new "
3188 				    "subchassis with CSN %s and index %llu",
3189 				    sdp->sed_name, csn, subchassis);
3190 
3191 				if ((scp = topo_mod_zalloc(mod,
3192 				    sizeof (ses_enum_chassis_t))) == NULL)
3193 					goto error;
3194 
3195 				scp->sec_csn = csn;
3196 
3197 				if (ses_init_chassis(mod, sdp, cp, scp, np,
3198 				    props, subchassis, SES_NEW_SUBCHASSIS) < 0)
3199 					goto error;
3200 			}
3201 		} else {
3202 			/*
3203 			 * We have a chassis or subchassis with this CSN.  If
3204 			 * it's a chassis, we must check to see whether it is
3205 			 * a placeholder previously created because we found a
3206 			 * subchassis with this CSN.  We will know that because
3207 			 * the sec_target value will not be set; it is set only
3208 			 * in ses_init_chassis().  In that case, initialise it
3209 			 * as a new chassis; otherwise, it's a duplicate and we
3210 			 * need to append only.
3211 			 */
3212 			if (subchassis == NO_SUBCHASSIS) {
3213 				if (cp->sec_target != NULL) {
3214 					/* 2.1 This is a duplicate chassis */
3215 
3216 					topo_mod_dprintf(mod, "%s: Append "
3217 					    "duplicate chassis with CSN (%s)",
3218 					    sdp->sed_name, csn);
3219 
3220 					if (ses_init_chassis(mod, sdp, NULL, cp,
3221 					    np, props, NO_SUBCHASSIS,
3222 					    SES_DUP_CHASSIS) < 0)
3223 						goto error;
3224 				} else {
3225 					/* Placeholder chassis - init it up */
3226 					topo_mod_dprintf(mod, "%s: Initialize"
3227 					    "placeholder chassis with CSN %s",
3228 					    sdp->sed_name, csn);
3229 
3230 					if (ses_init_chassis(mod, sdp, NULL,
3231 					    cp, np, props, NO_SUBCHASSIS,
3232 					    SES_NEW_CHASSIS) < 0)
3233 						goto error;
3234 
3235 				}
3236 			} else {
3237 				/* This is a subchassis */
3238 
3239 				for (scp = topo_list_next(&cp->sec_subchassis);
3240 				    scp != NULL; scp = topo_list_next(scp))
3241 					if (scp->sec_instance == subchassis)
3242 						break;
3243 
3244 				if (scp == NULL) {
3245 					/* 2.3 This is a new subchassis */
3246 
3247 					topo_mod_dprintf(mod, "%s: Initialize "
3248 					    "new subchassis with CSN (%s) "
3249 					    "and LID (%s)",
3250 					    sdp->sed_name, csn);
3251 
3252 					if ((scp = topo_mod_zalloc(mod,
3253 					    sizeof (ses_enum_chassis_t)))
3254 					    == NULL)
3255 						goto error;
3256 
3257 					scp->sec_csn = csn;
3258 
3259 					if (ses_init_chassis(mod, sdp, cp, scp,
3260 					    np, props, subchassis,
3261 					    SES_NEW_SUBCHASSIS) < 0)
3262 						goto error;
3263 				} else {
3264 					/* 2.4 This is a duplicate subchassis */
3265 
3266 					topo_mod_dprintf(mod, "%s: Append "
3267 					    "duplicate subchassis with "
3268 					    "CSN (%s)", sdp->sed_name, csn);
3269 
3270 					if (ses_init_chassis(mod, sdp, cp, scp,
3271 					    np, props, subchassis,
3272 					    SES_DUP_SUBCHASSIS) < 0)
3273 						goto error;
3274 				}
3275 			}
3276 		}
3277 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
3278 		/*
3279 		 * If we haven't yet seen an enclosure node and identified the
3280 		 * current chassis, something is very wrong; bail out.
3281 		 */
3282 		if (sdp->sed_current == NULL)
3283 			return (SES_WALK_ACTION_TERMINATE);
3284 
3285 		/*
3286 		 * If this isn't one of the element types we care about, then
3287 		 * ignore it.
3288 		 */
3289 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
3290 		    &type) == 0);
3291 		if (type != SES_ET_DEVICE &&
3292 		    type != SES_ET_ARRAY_DEVICE &&
3293 		    type != SES_ET_SUNW_FANBOARD &&
3294 		    type != SES_ET_SUNW_FANMODULE &&
3295 		    type != SES_ET_COOLING &&
3296 		    type != SES_ET_SUNW_POWERBOARD &&
3297 		    type != SES_ET_SUNW_POWERMODULE &&
3298 		    type != SES_ET_POWER_SUPPLY &&
3299 		    type != SES_ET_ESC_ELECTRONICS &&
3300 		    type != SES_ET_SAS_EXPANDER &&
3301 		    type != SES_ET_SAS_CONNECTOR)
3302 			return (SES_WALK_ACTION_CONTINUE);
3303 
3304 		/*
3305 		 * Get the current instance number and see if we already know
3306 		 * about this element.  If so, it means we have multiple paths
3307 		 * to the same elements, and we should ignore the current path.
3308 		 */
3309 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
3310 		    &instance) == 0);
3311 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
3312 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
3313 			    &instance);
3314 
3315 		cp = sdp->sed_current;
3316 
3317 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
3318 		    snp = topo_list_next(snp)) {
3319 			if (snp->sen_type == type &&
3320 			    snp->sen_instance == instance)
3321 				break;
3322 		}
3323 
3324 		/*
3325 		 * We prefer the new element under the following circumstances:
3326 		 *
3327 		 * - The currently known element's status is unknown or not
3328 		 *   available, but the new element has a known status.  This
3329 		 *   occurs if a given element is only available through a
3330 		 *   particular target.
3331 		 *
3332 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
3333 		 *   property is set.  This allows us to get reliable firmware
3334 		 *   revision information from the enclosure node.
3335 		 */
3336 		if (snp != NULL) {
3337 			if (nvlist_lookup_uint64(
3338 			    ses_node_props(snp->sen_node),
3339 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
3340 				prevstatus = SES_ESC_UNSUPPORTED;
3341 			if (nvlist_lookup_uint64(
3342 			    props, SES_PROP_STATUS_CODE, &status) != 0)
3343 				status = SES_ESC_UNSUPPORTED;
3344 			if (nvlist_lookup_boolean_value(
3345 			    props, SES_PROP_REPORT, &report) != 0)
3346 				report = B_FALSE;
3347 
3348 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
3349 			    !SES_STATUS_UNAVAIL(status)) ||
3350 			    (type == SES_ET_ESC_ELECTRONICS &&
3351 			    report)) {
3352 				snp->sen_node = np;
3353 				snp->sen_target = sdp->sed_target;
3354 			}
3355 
3356 			if ((sap = topo_mod_zalloc(mod,
3357 			    sizeof (ses_alt_node_t))) == NULL)
3358 				goto error;
3359 
3360 			sap->san_node = np;
3361 			topo_list_append(&snp->sen_alt_nodes, sap);
3362 
3363 			return (SES_WALK_ACTION_CONTINUE);
3364 		}
3365 
3366 		if ((snp = topo_mod_zalloc(mod,
3367 		    sizeof (ses_enum_node_t))) == NULL)
3368 			goto error;
3369 
3370 		if ((sap = topo_mod_zalloc(mod,
3371 		    sizeof (ses_alt_node_t))) == NULL) {
3372 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
3373 			goto error;
3374 		}
3375 
3376 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
3377 		    sdp->sed_name, type, instance);
3378 		snp->sen_node = np;
3379 		snp->sen_type = type;
3380 		snp->sen_instance = instance;
3381 		snp->sen_target = sdp->sed_target;
3382 		sap->san_node = np;
3383 		topo_list_append(&snp->sen_alt_nodes, sap);
3384 		topo_list_append(&cp->sec_nodes, snp);
3385 
3386 		if (type == SES_ET_DEVICE)
3387 			cp->sec_hasdev = B_TRUE;
3388 	}
3389 
3390 	return (SES_WALK_ACTION_CONTINUE);
3391 
3392 error:
3393 	sdp->sed_errno = -1;
3394 	return (SES_WALK_ACTION_TERMINATE);
3395 }
3396 
3397 static int
3398 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
3399 {
3400 	topo_mod_t *mod = sdp->sed_mod;
3401 	DIR *dir;
3402 	struct dirent *dp;
3403 	char path[PATH_MAX];
3404 	ses_enum_target_t *stp;
3405 	int err = -1;
3406 
3407 	/*
3408 	 * Open the SES target directory and iterate over any available
3409 	 * targets.
3410 	 */
3411 	if ((dir = opendir(dirpath)) == NULL) {
3412 		/*
3413 		 * If the SES target directory does not exist, then return as if
3414 		 * there are no active targets.
3415 		 */
3416 		topo_mod_dprintf(mod, "failed to open ses "
3417 		    "directory '%s'", dirpath);
3418 		return (0);
3419 	}
3420 
3421 	while ((dp = readdir(dir)) != NULL) {
3422 		if (strcmp(dp->d_name, ".") == 0 ||
3423 		    strcmp(dp->d_name, "..") == 0)
3424 			continue;
3425 
3426 		/*
3427 		 * Create a new target instance and take a snapshot.
3428 		 */
3429 		if ((stp = topo_mod_zalloc(mod,
3430 		    sizeof (ses_enum_target_t))) == NULL)
3431 			goto error;
3432 
3433 		(void) pthread_mutex_init(&stp->set_lock, NULL);
3434 
3435 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
3436 		    dp->d_name);
3437 
3438 		/*
3439 		 * We keep track of the SES device path and export it on a
3440 		 * per-node basis to allow higher level software to get to the
3441 		 * corresponding SES state.
3442 		 */
3443 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
3444 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3445 			goto error;
3446 		}
3447 
3448 		if ((stp->set_target =
3449 		    ses_open(LIBSES_VERSION, path)) == NULL) {
3450 			topo_mod_dprintf(mod, "failed to open ses target "
3451 			    "'%s': %s", dp->d_name, ses_errmsg());
3452 			ses_sof_alloc(mod, stp->set_devpath);
3453 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3454 			continue;
3455 		}
3456 		topo_mod_dprintf(mod, "open contract");
3457 		ses_ssl_alloc(mod, stp);
3458 		ses_create_contract(mod, stp);
3459 
3460 		stp->set_refcount = 1;
3461 		sdp->sed_target = stp;
3462 		stp->set_snap = ses_snap_hold(stp->set_target);
3463 		stp->set_snaptime = gethrtime();
3464 
3465 		/*
3466 		 * Enumerate over all SES elements and merge them into the
3467 		 * correct ses_enum_chassis_t.
3468 		 */
3469 		sdp->sed_current = NULL;
3470 		sdp->sed_errno = 0;
3471 		sdp->sed_name = dp->d_name;
3472 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
3473 
3474 		if (sdp->sed_errno != 0)
3475 			goto error;
3476 	}
3477 
3478 	err = 0;
3479 error:
3480 	(void) closedir(dir);
3481 	return (err);
3482 }
3483 
3484 static void
3485 ses_release(topo_mod_t *mod, tnode_t *tn)
3486 {
3487 	ses_enum_target_t *stp;
3488 
3489 	if ((stp = topo_node_getspecific(tn)) != NULL) {
3490 		topo_node_setspecific(tn, NULL);
3491 		ses_target_free(mod, stp);
3492 	}
3493 }
3494 
3495 /*ARGSUSED*/
3496 static int
3497 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
3498     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
3499 {
3500 	ses_enum_chassis_t *cp;
3501 	ses_enum_data_t *data;
3502 
3503 	/*
3504 	 * Check to make sure we're being invoked sensibly, and that we're not
3505 	 * being invoked as part of a post-processing step.
3506 	 */
3507 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
3508 		return (0);
3509 
3510 	/*
3511 	 * If this is the first time we've called our enumeration method, then
3512 	 * gather information about any available enclosures.
3513 	 */
3514 	if ((data = topo_mod_getspecific(mod)) == NULL) {
3515 		ses_sof_freeall(mod);
3516 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
3517 		    NULL)
3518 			return (-1);
3519 
3520 		data->sed_mod = mod;
3521 		topo_mod_setspecific(mod, data);
3522 
3523 		if (dev_list_gather(mod, &data->sed_devs) != 0)
3524 			goto error;
3525 
3526 		/*
3527 		 * We search both the ses(7D) and sgen(7D) locations, so we are
3528 		 * independent of any particular driver class bindings.
3529 		 */
3530 		if (ses_process_dir("/dev/es", data) != 0 ||
3531 		    ses_process_dir("/dev/scsi/ses", data) != 0)
3532 			goto error;
3533 	}
3534 
3535 	if (strcmp(name, SES_ENCLOSURE) == 0) {
3536 		/*
3537 		 * This is a request to enumerate external enclosures.  Go
3538 		 * through all the targets and create chassis nodes where
3539 		 * necessary.
3540 		 */
3541 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3542 		    cp = topo_list_next(cp)) {
3543 			if (ses_create_chassis(data, rnode, cp) != 0)
3544 				goto error;
3545 		}
3546 	} else {
3547 		/*
3548 		 * This is a request to enumerate a specific bay underneath the
3549 		 * root chassis (for internal disks).
3550 		 */
3551 		if (ses_create_bays(data, rnode) != 0)
3552 			goto error;
3553 	}
3554 
3555 	/*
3556 	 * This is a bit of a kludge.  In order to allow internal disks to be
3557 	 * enumerated and share snapshot-specific information with the external
3558 	 * enclosure enumeration, we rely on the fact that we will be invoked
3559 	 * for the 'ses-enclosure' node last.
3560 	 */
3561 	if (strcmp(name, SES_ENCLOSURE) == 0) {
3562 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3563 		    cp = topo_list_next(cp))
3564 			ses_data_free(data, cp);
3565 		ses_data_free(data, NULL);
3566 		topo_mod_setspecific(mod, NULL);
3567 	}
3568 	return (0);
3569 
3570 error:
3571 	for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3572 	    cp = topo_list_next(cp))
3573 		ses_data_free(data, cp);
3574 	ses_data_free(data, NULL);
3575 	topo_mod_setspecific(mod, NULL);
3576 	return (-1);
3577 }
3578 
3579 static const topo_modops_t ses_ops =
3580 	{ ses_enum, ses_release };
3581 
3582 static topo_modinfo_t ses_info =
3583 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
3584 
3585 /*ARGSUSED*/
3586 int
3587 _topo_init(topo_mod_t *mod, topo_version_t version)
3588 {
3589 	int rval;
3590 
3591 	if (getenv("TOPOSESDEBUG") != NULL)
3592 		topo_mod_setdebug(mod);
3593 
3594 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
3595 	    SES_ENCLOSURE);
3596 
3597 	if ((rval = topo_mod_register(mod, &ses_info, TOPO_VERSION)) == 0)
3598 		ses_thread_init(mod);
3599 
3600 	return (rval);
3601 }
3602 
3603 void
3604 _topo_fini(topo_mod_t *mod)
3605 {
3606 	ses_thread_fini(mod);
3607 	ses_sof_freeall(mod);
3608 	topo_mod_unregister(mod);
3609 }
3610