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