1940d71d2Seschrock /*
2940d71d2Seschrock  * CDDL HEADER START
3940d71d2Seschrock  *
4940d71d2Seschrock  * The contents of this file are subject to the terms of the
5940d71d2Seschrock  * Common Development and Distribution License (the "License").
6940d71d2Seschrock  * You may not use this file except in compliance with the License.
7940d71d2Seschrock  *
8940d71d2Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9940d71d2Seschrock  * or http://www.opensolaris.org/os/licensing.
10940d71d2Seschrock  * See the License for the specific language governing permissions
11940d71d2Seschrock  * and limitations under the License.
12940d71d2Seschrock  *
13940d71d2Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14940d71d2Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15940d71d2Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16940d71d2Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17940d71d2Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18940d71d2Seschrock  *
19940d71d2Seschrock  * CDDL HEADER END
20940d71d2Seschrock  */
21940d71d2Seschrock 
22940d71d2Seschrock /*
236efb64caSEric Schrock  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24940d71d2Seschrock  * Use is subject to license terms.
25940d71d2Seschrock  */
26940d71d2Seschrock 
276efb64caSEric Schrock #include <alloca.h>
28940d71d2Seschrock #include <dirent.h>
29940d71d2Seschrock #include <devid.h>
30940d71d2Seschrock #include <fm/libdiskstatus.h>
31940d71d2Seschrock #include <inttypes.h>
32940d71d2Seschrock #include <pthread.h>
33940d71d2Seschrock #include <strings.h>
34940d71d2Seschrock #include <unistd.h>
35940d71d2Seschrock #include <sys/dkio.h>
36940d71d2Seschrock #include <sys/fm/protocol.h>
37940d71d2Seschrock #include <sys/scsi/scsi_types.h>
38d91236feSeschrock 
39940d71d2Seschrock #include "disk.h"
40d91236feSeschrock #include "ses.h"
41940d71d2Seschrock 
42940d71d2Seschrock #define	SES_VERSION	1
43940d71d2Seschrock 
44940d71d2Seschrock #define	SES_SNAP_FREQ		1000	/* in milliseconds */
45940d71d2Seschrock 
46d91236feSeschrock #define	SES_STATUS_UNAVAIL(s)	\
470b32bb8bSEric Schrock 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
48940d71d2Seschrock 
49940d71d2Seschrock /*
50940d71d2Seschrock  * Because multiple SES targets can be part of a single chassis, we construct
51940d71d2Seschrock  * our own hierarchy that takes this into account.  These SES targets may refer
52940d71d2Seschrock  * to the same devices (multiple paths) or to different devices (managing
53940d71d2Seschrock  * different portions of the space).  We arrange things into a
54940d71d2Seschrock  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
55940d71d2Seschrock  * nodes found so far.
56940d71d2Seschrock  */
570b32bb8bSEric Schrock typedef struct ses_alt_node {
580b32bb8bSEric Schrock 	topo_list_t		san_link;
590b32bb8bSEric Schrock 	ses_node_t		*san_node;
600b32bb8bSEric Schrock } ses_alt_node_t;
61940d71d2Seschrock 
62940d71d2Seschrock typedef struct ses_enum_node {
63940d71d2Seschrock 	topo_list_t		sen_link;
64940d71d2Seschrock 	ses_node_t		*sen_node;
650b32bb8bSEric Schrock 	topo_list_t		sen_alt_nodes;
66940d71d2Seschrock 	uint64_t		sen_type;
67940d71d2Seschrock 	uint64_t		sen_instance;
68940d71d2Seschrock 	ses_enum_target_t	*sen_target;
69940d71d2Seschrock } ses_enum_node_t;
70940d71d2Seschrock 
71940d71d2Seschrock typedef struct ses_enum_chassis {
72940d71d2Seschrock 	topo_list_t		sec_link;
73*53dbcc59SSundeep Panicker 	topo_list_t		sec_subchassis;
74940d71d2Seschrock 	topo_list_t		sec_nodes;
75940d71d2Seschrock 	topo_list_t		sec_targets;
76940d71d2Seschrock 	const char		*sec_csn;
77*53dbcc59SSundeep Panicker 	const char		*sec_lid;
78940d71d2Seschrock 	ses_node_t		*sec_enclosure;
79940d71d2Seschrock 	ses_enum_target_t	*sec_target;
80940d71d2Seschrock 	topo_instance_t		sec_instance;
81*53dbcc59SSundeep Panicker 	topo_instance_t		sec_scinstance;
82940d71d2Seschrock 	boolean_t		sec_hasdev;
83d91236feSeschrock 	boolean_t		sec_internal;
84940d71d2Seschrock } ses_enum_chassis_t;
85940d71d2Seschrock 
86940d71d2Seschrock typedef struct ses_enum_data {
87940d71d2Seschrock 	topo_list_t		sed_disks;
88940d71d2Seschrock 	topo_list_t		sed_chassis;
89940d71d2Seschrock 	ses_enum_chassis_t	*sed_current;
90940d71d2Seschrock 	ses_enum_target_t	*sed_target;
91940d71d2Seschrock 	int			sed_errno;
92940d71d2Seschrock 	char			*sed_name;
93940d71d2Seschrock 	topo_mod_t		*sed_mod;
94940d71d2Seschrock 	topo_instance_t		sed_instance;
95940d71d2Seschrock } ses_enum_data_t;
96940d71d2Seschrock 
97*53dbcc59SSundeep Panicker typedef enum {
98*53dbcc59SSundeep Panicker 	SES_NEW_CHASSIS		= 0x1,
99*53dbcc59SSundeep Panicker 	SES_NEW_SUBCHASSIS	= 0x2,
100*53dbcc59SSundeep Panicker 	SES_DUP_CHASSIS		= 0x4,
101*53dbcc59SSundeep Panicker 	SES_DUP_SUBCHASSIS	= 0x8
102*53dbcc59SSundeep Panicker } ses_chassis_type_e;
103*53dbcc59SSundeep Panicker 
104940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
105940d71d2Seschrock     nvlist_t **);
106940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
107940d71d2Seschrock     nvlist_t **);
108940d71d2Seschrock 
109940d71d2Seschrock static const topo_method_t ses_component_methods[] = {
110940d71d2Seschrock 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
111940d71d2Seschrock 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
112d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
113d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
11488045cffSRobert Johnston 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
11588045cffSRobert Johnston 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
11688045cffSRobert Johnston 	    topo_method_sensor_failure },
117d91236feSeschrock 	{ NULL }
118d91236feSeschrock };
119d91236feSeschrock 
120d91236feSeschrock static const topo_method_t ses_bay_methods[] = {
121d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
122d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
123940d71d2Seschrock 	{ NULL }
124940d71d2Seschrock };
125940d71d2Seschrock 
126940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = {
127940d71d2Seschrock 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
128940d71d2Seschrock 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
129d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
130d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
131940d71d2Seschrock 	{ NULL }
132940d71d2Seschrock };
133940d71d2Seschrock 
134940d71d2Seschrock static void
135940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
136940d71d2Seschrock {
137940d71d2Seschrock 	if (--stp->set_refcount == 0) {
138940d71d2Seschrock 		ses_snap_rele(stp->set_snap);
139940d71d2Seschrock 		ses_close(stp->set_target);
140940d71d2Seschrock 		topo_mod_strfree(mod, stp->set_devpath);
141940d71d2Seschrock 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
142940d71d2Seschrock 	}
143940d71d2Seschrock }
144940d71d2Seschrock 
145940d71d2Seschrock static void
146*53dbcc59SSundeep Panicker ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
147940d71d2Seschrock {
148940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
149940d71d2Seschrock 	ses_enum_chassis_t *cp;
150940d71d2Seschrock 	ses_enum_node_t *np;
151940d71d2Seschrock 	ses_enum_target_t *tp;
1520b32bb8bSEric Schrock 	ses_alt_node_t *ap;
153*53dbcc59SSundeep Panicker 	topo_list_t *cpl;
154*53dbcc59SSundeep Panicker 
155*53dbcc59SSundeep Panicker 
156*53dbcc59SSundeep Panicker 	if (pcp != NULL)
157*53dbcc59SSundeep Panicker 		cpl = &pcp->sec_subchassis;
158*53dbcc59SSundeep Panicker 	else
159*53dbcc59SSundeep Panicker 		cpl = &sdp->sed_chassis;
160940d71d2Seschrock 
161*53dbcc59SSundeep Panicker 	while ((cp = topo_list_next(cpl)) != NULL) {
162*53dbcc59SSundeep Panicker 		topo_list_delete(cpl, cp);
163940d71d2Seschrock 
164940d71d2Seschrock 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
1650b32bb8bSEric Schrock 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
1660b32bb8bSEric Schrock 			    NULL) {
1670b32bb8bSEric Schrock 				topo_list_delete(&np->sen_alt_nodes, ap);
1680b32bb8bSEric Schrock 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
1690b32bb8bSEric Schrock 			}
170940d71d2Seschrock 			topo_list_delete(&cp->sec_nodes, np);
171940d71d2Seschrock 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
172940d71d2Seschrock 		}
173940d71d2Seschrock 
174940d71d2Seschrock 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
175940d71d2Seschrock 			topo_list_delete(&cp->sec_targets, tp);
176940d71d2Seschrock 			ses_target_free(mod, tp);
177940d71d2Seschrock 		}
178940d71d2Seschrock 
179940d71d2Seschrock 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
180940d71d2Seschrock 	}
181940d71d2Seschrock 
182*53dbcc59SSundeep Panicker 	if (pcp == NULL) {
183*53dbcc59SSundeep Panicker 		disk_list_free(mod, &sdp->sed_disks);
184*53dbcc59SSundeep Panicker 		topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
185*53dbcc59SSundeep Panicker 	}
186940d71d2Seschrock }
187940d71d2Seschrock 
188940d71d2Seschrock /*
189940d71d2Seschrock  * For enclosure nodes, we have a special contains method.  By default, the hc
190940d71d2Seschrock  * walker will compare the node name and instance number to determine if an
191940d71d2Seschrock  * FMRI matches.  For enclosures where the enumeration order is impossible to
192940d71d2Seschrock  * predict, we instead use the chassis-id as a unique identifier, and ignore
193940d71d2Seschrock  * the instance number.
194940d71d2Seschrock  */
195940d71d2Seschrock static int
196940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
197940d71d2Seschrock {
198940d71d2Seschrock 	uint8_t v1, v2;
199940d71d2Seschrock 	nvlist_t **hcp1, **hcp2;
200940d71d2Seschrock 	int err, i;
201940d71d2Seschrock 	uint_t nhcp1, nhcp2;
202940d71d2Seschrock 	nvlist_t *a1, *a2;
203940d71d2Seschrock 	char *c1, *c2;
204940d71d2Seschrock 	int mindepth;
205940d71d2Seschrock 
206940d71d2Seschrock 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
207940d71d2Seschrock 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
208940d71d2Seschrock 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
209940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
210940d71d2Seschrock 
211940d71d2Seschrock 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
212940d71d2Seschrock 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
213940d71d2Seschrock 	if (err != 0)
214940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
215940d71d2Seschrock 
216940d71d2Seschrock 	/*
217940d71d2Seschrock 	 * If the chassis-id doesn't match, then these FMRIs are not
218940d71d2Seschrock 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
219940d71d2Seschrock 	 * have no choice but to fall back to the instance ID.
220940d71d2Seschrock 	 */
221940d71d2Seschrock 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
222940d71d2Seschrock 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
223940d71d2Seschrock 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
224940d71d2Seschrock 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
225940d71d2Seschrock 		if (strcmp(c1, c2) != 0)
226940d71d2Seschrock 			return (0);
227940d71d2Seschrock 
228940d71d2Seschrock 		mindepth = 1;
229940d71d2Seschrock 	} else {
230940d71d2Seschrock 		mindepth = 0;
231940d71d2Seschrock 	}
232940d71d2Seschrock 
233940d71d2Seschrock 	if (nhcp2 < nhcp1)
234940d71d2Seschrock 		return (0);
235940d71d2Seschrock 
236940d71d2Seschrock 	for (i = 0; i < nhcp1; i++) {
237940d71d2Seschrock 		char *nm1 = NULL;
238940d71d2Seschrock 		char *nm2 = NULL;
239940d71d2Seschrock 		char *id1 = NULL;
240940d71d2Seschrock 		char *id2 = NULL;
241940d71d2Seschrock 
242940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
243940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
244940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
245940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
246940d71d2Seschrock 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
247940d71d2Seschrock 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
248940d71d2Seschrock 
249940d71d2Seschrock 		if (strcmp(nm1, nm2) == 0 &&
250940d71d2Seschrock 		    (i < mindepth || strcmp(id1, id2) == 0))
251940d71d2Seschrock 			continue;
252940d71d2Seschrock 
253940d71d2Seschrock 		return (0);
254940d71d2Seschrock 	}
255940d71d2Seschrock 
256940d71d2Seschrock 	return (1);
257940d71d2Seschrock }
258940d71d2Seschrock 
259940d71d2Seschrock /*ARGSUSED*/
260940d71d2Seschrock static int
261940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
262940d71d2Seschrock     nvlist_t *in, nvlist_t **out)
263940d71d2Seschrock {
264940d71d2Seschrock 	int ret;
265940d71d2Seschrock 	nvlist_t *nv1, *nv2;
266940d71d2Seschrock 
267940d71d2Seschrock 	if (version > TOPO_METH_CONTAINS_VERSION)
268940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
269940d71d2Seschrock 
270940d71d2Seschrock 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
271940d71d2Seschrock 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
272940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
273940d71d2Seschrock 
274940d71d2Seschrock 	ret = fmri_contains(mod, nv1, nv2);
275940d71d2Seschrock 	if (ret < 0)
276940d71d2Seschrock 		return (-1);
277940d71d2Seschrock 
278940d71d2Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
27973e32a37SRobert Johnston 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
280940d71d2Seschrock 		    ret) == 0)
281940d71d2Seschrock 			return (0);
282940d71d2Seschrock 		else
283940d71d2Seschrock 			nvlist_free(*out);
284940d71d2Seschrock 	}
285940d71d2Seschrock 
286940d71d2Seschrock 	return (-1);
287940d71d2Seschrock 
288940d71d2Seschrock }
289940d71d2Seschrock 
290940d71d2Seschrock /*
291d91236feSeschrock  * Return a current instance of the node.  This is somewhat complicated because
292d91236feSeschrock  * we need to take a new snapshot in order to get the new data, but we don't
293940d71d2Seschrock  * want to be constantly taking SES snapshots if the consumer is going to do a
294940d71d2Seschrock  * series of queries.  So we adopt the strategy of assuming that the SES state
295d91236feSeschrock  * is not going to be rapidly changing, and limit our snapshot frequency to
296d91236feSeschrock  * some defined bounds.
297940d71d2Seschrock  */
298d91236feSeschrock ses_node_t *
2990b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn)
300940d71d2Seschrock {
301940d71d2Seschrock 	struct timeval tv;
302940d71d2Seschrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
303940d71d2Seschrock 	uint64_t prev, now;
304940d71d2Seschrock 	ses_snap_t *snap;
305940d71d2Seschrock 	int err;
306d91236feSeschrock 	uint64_t nodeid;
307940d71d2Seschrock 	ses_node_t *np;
308940d71d2Seschrock 
309d91236feSeschrock 	if (tp == NULL) {
310d91236feSeschrock 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
311d91236feSeschrock 		return (NULL);
312d91236feSeschrock 	}
313940d71d2Seschrock 
3140b32bb8bSEric Schrock 	(void) pthread_mutex_lock(&tp->set_lock);
3150b32bb8bSEric Schrock 
316940d71d2Seschrock 	/*
317940d71d2Seschrock 	 * Determine if we need to take a new snapshot.
318940d71d2Seschrock 	 */
319940d71d2Seschrock 	if (gettimeofday(&tv, NULL) != 0) {
320940d71d2Seschrock 		tv.tv_sec = time(NULL);
321940d71d2Seschrock 		tv.tv_usec = 0;
322940d71d2Seschrock 	}
323940d71d2Seschrock 
324940d71d2Seschrock 	now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
325940d71d2Seschrock 	prev = tp->set_snaptime.tv_sec * 1000 +
326940d71d2Seschrock 	    tp->set_snaptime.tv_usec / 1000;
327940d71d2Seschrock 
328940d71d2Seschrock 	if (now - prev > SES_SNAP_FREQ &&
329940d71d2Seschrock 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
330940d71d2Seschrock 		if (ses_snap_generation(snap) !=
331940d71d2Seschrock 		    ses_snap_generation(tp->set_snap)) {
332940d71d2Seschrock 			/*
333940d71d2Seschrock 			 * If we find ourselves in this situation, we're in
334940d71d2Seschrock 			 * trouble.  The generation count has changed, which
335940d71d2Seschrock 			 * indicates that our current topology is out of date.
336940d71d2Seschrock 			 * But we need to consult the new topology in order to
337940d71d2Seschrock 			 * determine presence at this moment in time.  We can't
338940d71d2Seschrock 			 * go back and change the topo snapshot in situ, so
339d91236feSeschrock 			 * we'll just have to fail the call in this unlikely
340d91236feSeschrock 			 * scenario.
341940d71d2Seschrock 			 */
342940d71d2Seschrock 			ses_snap_rele(snap);
343d91236feSeschrock 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
3440b32bb8bSEric Schrock 			(void) pthread_mutex_unlock(&tp->set_lock);
345d91236feSeschrock 			return (NULL);
346940d71d2Seschrock 		} else {
347940d71d2Seschrock 			ses_snap_rele(tp->set_snap);
348940d71d2Seschrock 			tp->set_snap = snap;
349940d71d2Seschrock 		}
350940d71d2Seschrock 		tp->set_snaptime = tv;
351940d71d2Seschrock 	}
352940d71d2Seschrock 
353940d71d2Seschrock 	snap = tp->set_snap;
354940d71d2Seschrock 
355940d71d2Seschrock 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
356940d71d2Seschrock 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
357940d71d2Seschrock 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
358d91236feSeschrock 
359d91236feSeschrock 	return (np);
360d91236feSeschrock }
361d91236feSeschrock 
3620b32bb8bSEric Schrock /*ARGSUSED*/
3630b32bb8bSEric Schrock void
3640b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
3650b32bb8bSEric Schrock {
3660b32bb8bSEric Schrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
3670b32bb8bSEric Schrock 
3680b32bb8bSEric Schrock 	verify(tp != NULL);
3690b32bb8bSEric Schrock 
3700b32bb8bSEric Schrock 	(void) pthread_mutex_unlock(&tp->set_lock);
3710b32bb8bSEric Schrock }
3720b32bb8bSEric Schrock 
373d91236feSeschrock /*
374d91236feSeschrock  * Determine if the element is present.
375d91236feSeschrock  */
376d91236feSeschrock /*ARGSUSED*/
377d91236feSeschrock static int
378d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
379d91236feSeschrock     nvlist_t *in, nvlist_t **out)
380d91236feSeschrock {
381d91236feSeschrock 	boolean_t present;
382d91236feSeschrock 	ses_node_t *np;
383d91236feSeschrock 	nvlist_t *props, *nvl;
384d91236feSeschrock 	uint64_t status;
385d91236feSeschrock 
3860b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL)
387d91236feSeschrock 		return (-1);
388d91236feSeschrock 
389940d71d2Seschrock 	verify((props = ses_node_props(np)) != NULL);
390940d71d2Seschrock 	verify(nvlist_lookup_uint64(props,
391940d71d2Seschrock 	    SES_PROP_STATUS_CODE, &status) == 0);
392940d71d2Seschrock 
3930b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
3940b32bb8bSEric Schrock 
395940d71d2Seschrock 	present = (status != SES_ESC_NOT_INSTALLED);
396940d71d2Seschrock 
397940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
398940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
399940d71d2Seschrock 
400940d71d2Seschrock 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
401940d71d2Seschrock 	    present) != 0) {
402940d71d2Seschrock 		nvlist_free(nvl);
403940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
404940d71d2Seschrock 	}
405940d71d2Seschrock 
406940d71d2Seschrock 	*out = nvl;
407940d71d2Seschrock 
408940d71d2Seschrock 	return (0);
409940d71d2Seschrock }
410940d71d2Seschrock 
411940d71d2Seschrock /*
412940d71d2Seschrock  * Sets standard properties for a ses node (enclosure or bay).  This includes
413940d71d2Seschrock  * setting the FRU to be the same as the resource, as well as setting the
414940d71d2Seschrock  * authority information.
415940d71d2Seschrock  */
416940d71d2Seschrock static int
417940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth,
418940d71d2Seschrock     uint64_t nodeid, const char *path)
419940d71d2Seschrock {
420940d71d2Seschrock 	int err;
421940d71d2Seschrock 	char *product, *chassis;
422940d71d2Seschrock 	nvlist_t *fmri;
423940d71d2Seschrock 	topo_pgroup_info_t pgi;
424940d71d2Seschrock 
425940d71d2Seschrock 	/*
426940d71d2Seschrock 	 * Set the authority explicitly if specified.
427940d71d2Seschrock 	 */
428940d71d2Seschrock 	if (auth) {
429940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
430940d71d2Seschrock 		    &product) == 0);
431940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
432940d71d2Seschrock 		    &chassis) == 0);
433940d71d2Seschrock 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
434940d71d2Seschrock 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
435940d71d2Seschrock 		    &err) != 0 ||
436940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
437940d71d2Seschrock 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
438940d71d2Seschrock 		    &err) != 0 ||
439940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
440940d71d2Seschrock 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
441940d71d2Seschrock 		    &err) != 0) {
442940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to add authority "
443d91236feSeschrock 			    "properties: %s\n", topo_strerror(err));
444940d71d2Seschrock 			return (topo_mod_seterrno(mod, err));
445940d71d2Seschrock 		}
446940d71d2Seschrock 	}
447940d71d2Seschrock 
448940d71d2Seschrock 	/*
449940d71d2Seschrock 	 * Copy the resource and set that as the FRU.
450940d71d2Seschrock 	 */
451940d71d2Seschrock 	if (topo_node_resource(tn, &fmri, &err) != 0) {
452940d71d2Seschrock 		topo_mod_dprintf(mod,
453940d71d2Seschrock 		    "topo_node_resource() failed : %s\n",
454940d71d2Seschrock 		    topo_strerror(err));
455940d71d2Seschrock 		return (topo_mod_seterrno(mod, err));
456940d71d2Seschrock 	}
457940d71d2Seschrock 
458940d71d2Seschrock 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
459940d71d2Seschrock 		topo_mod_dprintf(mod,
460940d71d2Seschrock 		    "topo_node_fru_set() failed : %s\n",
461940d71d2Seschrock 		    topo_strerror(err));
462940d71d2Seschrock 		nvlist_free(fmri);
463940d71d2Seschrock 		return (topo_mod_seterrno(mod, err));
464940d71d2Seschrock 	}
465940d71d2Seschrock 
466940d71d2Seschrock 	nvlist_free(fmri);
467940d71d2Seschrock 
468940d71d2Seschrock 	/*
469940d71d2Seschrock 	 * Set the SES-specific properties so that consumers can query
470940d71d2Seschrock 	 * additional information about the particular SES element.
471940d71d2Seschrock 	 */
472940d71d2Seschrock 	pgi.tpi_name = TOPO_PGROUP_SES;
473940d71d2Seschrock 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
474940d71d2Seschrock 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
475940d71d2Seschrock 	pgi.tpi_version = TOPO_VERSION;
476940d71d2Seschrock 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
477940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to create propgroup "
478940d71d2Seschrock 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
479940d71d2Seschrock 		return (-1);
480940d71d2Seschrock 	}
481940d71d2Seschrock 
482940d71d2Seschrock 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
483940d71d2Seschrock 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
484940d71d2Seschrock 	    nodeid, &err) != 0) {
485940d71d2Seschrock 		topo_mod_dprintf(mod,
486940d71d2Seschrock 		    "failed to create property %s: %s\n",
487940d71d2Seschrock 		    TOPO_PROP_NODE_ID, topo_strerror(err));
488940d71d2Seschrock 		return (-1);
489940d71d2Seschrock 	}
490940d71d2Seschrock 
491940d71d2Seschrock 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
492940d71d2Seschrock 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
493940d71d2Seschrock 	    path, &err) != 0) {
494940d71d2Seschrock 		topo_mod_dprintf(mod,
495940d71d2Seschrock 		    "failed to create property %s: %s\n",
496940d71d2Seschrock 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
497940d71d2Seschrock 		return (-1);
498940d71d2Seschrock 	}
499940d71d2Seschrock 
500940d71d2Seschrock 	return (0);
501940d71d2Seschrock }
502940d71d2Seschrock 
503940d71d2Seschrock /*
504940d71d2Seschrock  * Callback to add a disk to a given bay.  We first check the status-code to
505940d71d2Seschrock  * determine if a disk is present, ignoring those that aren't in an appropriate
5060b32bb8bSEric Schrock  * state.  We then scan the parent bay node's SAS address array to determine
5070b32bb8bSEric Schrock  * possible attached SAS addresses.  We create a disk node if the disk is not
5080b32bb8bSEric Schrock  * SAS or the SES target does not support the necessary pages for this; if we
5090b32bb8bSEric Schrock  * find the SAS address, we create a disk node and also correlate it with
5100b32bb8bSEric Schrock  * the corresponding Solaris device node to fill in the rest of the data.
511940d71d2Seschrock  */
512940d71d2Seschrock static int
513940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
514940d71d2Seschrock {
515940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
516940d71d2Seschrock 	uint64_t status;
517940d71d2Seschrock 	nvlist_t **sas;
518940d71d2Seschrock 	uint_t s, nsas;
5190b32bb8bSEric Schrock 	char **paths;
5200b32bb8bSEric Schrock 	int err;
521940d71d2Seschrock 
522940d71d2Seschrock 	/*
523940d71d2Seschrock 	 * Skip devices that are not in a present (and possibly damaged) state.
524940d71d2Seschrock 	 */
525940d71d2Seschrock 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
526940d71d2Seschrock 		return (0);
527940d71d2Seschrock 
528940d71d2Seschrock 	if (status != SES_ESC_OK &&
529940d71d2Seschrock 	    status != SES_ESC_CRITICAL &&
530940d71d2Seschrock 	    status != SES_ESC_NONCRITICAL &&
531940d71d2Seschrock 	    status != SES_ESC_UNRECOVERABLE &&
532940d71d2Seschrock 	    status != SES_ESC_NO_ACCESS)
533940d71d2Seschrock 		return (0);
534940d71d2Seschrock 
535940d71d2Seschrock 	topo_mod_dprintf(mod, "found attached disk");
536940d71d2Seschrock 
537940d71d2Seschrock 	/*
538940d71d2Seschrock 	 * Create the disk range.
539940d71d2Seschrock 	 */
5400b32bb8bSEric Schrock 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
541940d71d2Seschrock 		topo_mod_dprintf(mod,
542940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
543940d71d2Seschrock 		    topo_mod_errmsg(mod));
544940d71d2Seschrock 		return (-1);
545940d71d2Seschrock 	}
546940d71d2Seschrock 
547940d71d2Seschrock 	/*
548940d71d2Seschrock 	 * Look through all SAS addresses and attempt to correlate them to a
549940d71d2Seschrock 	 * known Solaris device.  If we don't find a matching node, then we
550940d71d2Seschrock 	 * don't enumerate the disk node.
551940d71d2Seschrock 	 */
552940d71d2Seschrock 	if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
553940d71d2Seschrock 	    &sas, &nsas) != 0)
554940d71d2Seschrock 		return (0);
555940d71d2Seschrock 
5560b32bb8bSEric Schrock 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
5570b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
5580b32bb8bSEric Schrock 		return (0);
5590b32bb8bSEric Schrock 
5600b32bb8bSEric Schrock 	err = 0;
5610b32bb8bSEric Schrock 
562940d71d2Seschrock 	for (s = 0; s < nsas; s++) {
5630b32bb8bSEric Schrock 		if (disk_declare_addr(mod, pnode,
5640b32bb8bSEric Schrock 		    &sdp->sed_disks, paths[s]) != 0 &&
5650b32bb8bSEric Schrock 		    topo_mod_errno(mod) != EMOD_NODE_BOUND) {
5660b32bb8bSEric Schrock 			err = -1;
5670b32bb8bSEric Schrock 			break;
5680b32bb8bSEric Schrock 		}
5690b32bb8bSEric Schrock 	}
5700b32bb8bSEric Schrock 
5710b32bb8bSEric Schrock 	for (s = 0; s < nsas; s++)
5720b32bb8bSEric Schrock 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
5730b32bb8bSEric Schrock 	topo_mod_free(mod, paths, nsas * sizeof (char *));
5740b32bb8bSEric Schrock 
5750b32bb8bSEric Schrock 	return (err);
5760b32bb8bSEric Schrock }
5770b32bb8bSEric Schrock 
5780b32bb8bSEric Schrock static int
5790b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
5800b32bb8bSEric Schrock {
5810b32bb8bSEric Schrock 	ses_alt_node_t *ap;
5820b32bb8bSEric Schrock 	ses_node_t *np;
5830b32bb8bSEric Schrock 	nvlist_t *props;
5840b32bb8bSEric Schrock 
5850b32bb8bSEric Schrock 	nvlist_t **phys;
5860b32bb8bSEric Schrock 	uint_t i, j, n_phys, all_phys = 0;
5870b32bb8bSEric Schrock 	char **paths;
5880b32bb8bSEric Schrock 	uint64_t addr;
5890b32bb8bSEric Schrock 	size_t len;
5900b32bb8bSEric Schrock 	int terr, err = -1;
5910b32bb8bSEric Schrock 
5920b32bb8bSEric Schrock 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
5930b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
5940b32bb8bSEric Schrock 		np = ap->san_node;
5950b32bb8bSEric Schrock 		props = ses_node_props(np);
5960b32bb8bSEric Schrock 
5970b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
5980b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
5990b32bb8bSEric Schrock 			continue;
6000b32bb8bSEric Schrock 
6010b32bb8bSEric Schrock 		all_phys += n_phys;
6020b32bb8bSEric Schrock 	}
6030b32bb8bSEric Schrock 
6040b32bb8bSEric Schrock 	if (all_phys == 0)
6050b32bb8bSEric Schrock 		return (0);
6060b32bb8bSEric Schrock 
6070b32bb8bSEric Schrock 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
6080b32bb8bSEric Schrock 		return (-1);
6090b32bb8bSEric Schrock 
6100b32bb8bSEric Schrock 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
6110b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
6120b32bb8bSEric Schrock 		np = ap->san_node;
6130b32bb8bSEric Schrock 		props = ses_node_props(np);
6140b32bb8bSEric Schrock 
6150b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
6160b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
617940d71d2Seschrock 			continue;
618940d71d2Seschrock 
6190b32bb8bSEric Schrock 		for (j = 0; j < n_phys; j++) {
6200b32bb8bSEric Schrock 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
6210b32bb8bSEric Schrock 			    &addr) != 0)
6220b32bb8bSEric Schrock 				continue;
623940d71d2Seschrock 
6240b32bb8bSEric Schrock 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
6250b32bb8bSEric Schrock 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
6260b32bb8bSEric Schrock 				goto error;
6270b32bb8bSEric Schrock 
6280b32bb8bSEric Schrock 			(void) snprintf(paths[i], len, "%016llx", addr);
6290b32bb8bSEric Schrock 
6300b32bb8bSEric Schrock 			++i;
6310b32bb8bSEric Schrock 		}
632940d71d2Seschrock 	}
633940d71d2Seschrock 
6340b32bb8bSEric Schrock 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
6350b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
6360b32bb8bSEric Schrock 	    (const char **)paths, i, &terr);
6370b32bb8bSEric Schrock 	if (err != 0)
6380b32bb8bSEric Schrock 		err = topo_mod_seterrno(mod, terr);
6390b32bb8bSEric Schrock 
6400b32bb8bSEric Schrock error:
6410b32bb8bSEric Schrock 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
6420b32bb8bSEric Schrock 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
6430b32bb8bSEric Schrock 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
6440b32bb8bSEric Schrock 
6450b32bb8bSEric Schrock 	return (err);
646940d71d2Seschrock }
647940d71d2Seschrock 
648940d71d2Seschrock /*
649940d71d2Seschrock  * Callback to create a basic node (bay, psu, fan, or controller).
650940d71d2Seschrock  */
651940d71d2Seschrock static int
652940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp,
653940d71d2Seschrock     tnode_t *pnode, const char *nodename, const char *labelname)
654940d71d2Seschrock {
655940d71d2Seschrock 	ses_node_t *np = snp->sen_node;
656d91236feSeschrock 	ses_node_t *parent;
657940d71d2Seschrock 	uint64_t instance = snp->sen_instance;
658940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
659940d71d2Seschrock 	nvlist_t *props, *aprops;
660940d71d2Seschrock 	nvlist_t *auth = NULL, *fmri = NULL;
661940d71d2Seschrock 	tnode_t *tn;
662940d71d2Seschrock 	char label[128];
663940d71d2Seschrock 	int err;
664d91236feSeschrock 	char *part = NULL, *serial = NULL, *revision = NULL;
665d91236feSeschrock 	char *desc;
666d91236feSeschrock 	boolean_t report;
667940d71d2Seschrock 
668940d71d2Seschrock 	props = ses_node_props(np);
669940d71d2Seschrock 
670940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
671940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
672940d71d2Seschrock 
673940d71d2Seschrock 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
674940d71d2Seschrock 
675940d71d2Seschrock 	/*
676940d71d2Seschrock 	 * Create the node.  The interesting information is all copied from the
677940d71d2Seschrock 	 * parent enclosure node, so there is not much to do.
678940d71d2Seschrock 	 */
679940d71d2Seschrock 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
680940d71d2Seschrock 		goto error;
681940d71d2Seschrock 
682d91236feSeschrock 	/*
683d91236feSeschrock 	 * We want to report revision information for the controller nodes, but
684d91236feSeschrock 	 * we do not get per-element revision information.  However, we do have
685d91236feSeschrock 	 * revision information for the entire enclosure, and we can use the
686d91236feSeschrock 	 * 'reported-via' property to know that this controller corresponds to
687d91236feSeschrock 	 * the given revision information.  This means we cannot get revision
688d91236feSeschrock 	 * information for targets we are not explicitly connected to, but
689d91236feSeschrock 	 * there is little we can do about the situation.
690d91236feSeschrock 	 */
691d91236feSeschrock 	if (strcmp(nodename, CONTROLLER) == 0 &&
692d91236feSeschrock 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
693d91236feSeschrock 	    report) {
694d91236feSeschrock 		for (parent = ses_node_parent(np); parent != NULL;
695d91236feSeschrock 		    parent = ses_node_parent(parent)) {
696d91236feSeschrock 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
697d91236feSeschrock 				(void) nvlist_lookup_string(
698d91236feSeschrock 				    ses_node_props(parent),
699d91236feSeschrock 				    SES_EN_PROP_REV, &revision);
700d91236feSeschrock 				break;
701d91236feSeschrock 			}
702d91236feSeschrock 		}
703d91236feSeschrock 	}
704d91236feSeschrock 
705940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
706d91236feSeschrock 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
707940d71d2Seschrock 	    serial)) == NULL) {
708940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
709940d71d2Seschrock 		    topo_mod_errmsg(mod));
710940d71d2Seschrock 		goto error;
711940d71d2Seschrock 	}
712940d71d2Seschrock 
713940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, nodename,
714940d71d2Seschrock 	    instance, fmri)) == NULL) {
715940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
716940d71d2Seschrock 		    topo_mod_errmsg(mod));
717940d71d2Seschrock 		goto error;
718940d71d2Seschrock 	}
719940d71d2Seschrock 
720940d71d2Seschrock 	/*
721d91236feSeschrock 	 * For the node label, we look for the following in order:
722d91236feSeschrock 	 *
723d91236feSeschrock 	 * 	<ses-description>
724d91236feSeschrock 	 * 	<ses-class-description> <instance>
725d91236feSeschrock 	 * 	<default-type-label> <instance>
726940d71d2Seschrock 	 */
727d91236feSeschrock 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
728d91236feSeschrock 	    desc[0] == '\0') {
729d91236feSeschrock 		parent = ses_node_parent(np);
730d91236feSeschrock 		aprops = ses_node_props(parent);
731d91236feSeschrock 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
732d91236feSeschrock 		    &desc) == 0 && desc[0] != '\0')
733d91236feSeschrock 			labelname = desc;
734d91236feSeschrock 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
735d91236feSeschrock 		    instance);
736d91236feSeschrock 		desc = label;
737d91236feSeschrock 	}
738d91236feSeschrock 
739d91236feSeschrock 	if (topo_node_label_set(tn, desc, &err) != 0)
740940d71d2Seschrock 		goto error;
741940d71d2Seschrock 
742940d71d2Seschrock 	if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np),
743940d71d2Seschrock 	    snp->sen_target->set_devpath) != 0)
744940d71d2Seschrock 		goto error;
745940d71d2Seschrock 
746940d71d2Seschrock 	if (strcmp(nodename, "bay") == 0) {
7470b32bb8bSEric Schrock 		if (ses_add_bay_props(mod, tn, snp) != 0)
7480b32bb8bSEric Schrock 			goto error;
7490b32bb8bSEric Schrock 
750940d71d2Seschrock 		if (ses_create_disk(sdp, tn, props) != 0)
751940d71d2Seschrock 			goto error;
752d91236feSeschrock 
753d91236feSeschrock 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
754d91236feSeschrock 			topo_mod_dprintf(mod,
755d91236feSeschrock 			    "topo_method_register() failed: %s",
756d91236feSeschrock 			    topo_mod_errmsg(mod));
757d91236feSeschrock 			goto error;
758d91236feSeschrock 		}
759940d71d2Seschrock 	} else {
760940d71d2Seschrock 		/*
761d91236feSeschrock 		 * Only fan, psu, and controller nodes have a 'present' method.
762d91236feSeschrock 		 * Bay nodes are always present, and disk nodes are present by
763d91236feSeschrock 		 * virtue of being enumerated.
764940d71d2Seschrock 		 */
765940d71d2Seschrock 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
766940d71d2Seschrock 			topo_mod_dprintf(mod,
767940d71d2Seschrock 			    "topo_method_register() failed: %s",
768940d71d2Seschrock 			    topo_mod_errmsg(mod));
769940d71d2Seschrock 			goto error;
770940d71d2Seschrock 		}
771940d71d2Seschrock 
772940d71d2Seschrock 	}
773940d71d2Seschrock 
774d91236feSeschrock 	snp->sen_target->set_refcount++;
775d91236feSeschrock 	topo_node_setspecific(tn, snp->sen_target);
776d91236feSeschrock 
777940d71d2Seschrock 	nvlist_free(auth);
778940d71d2Seschrock 	nvlist_free(fmri);
779940d71d2Seschrock 	return (0);
780940d71d2Seschrock 
781940d71d2Seschrock error:
782940d71d2Seschrock 	nvlist_free(auth);
783940d71d2Seschrock 	nvlist_free(fmri);
784940d71d2Seschrock 	return (-1);
785940d71d2Seschrock }
786940d71d2Seschrock 
787940d71d2Seschrock /*
788940d71d2Seschrock  * Instantiate any children of a given type.
789940d71d2Seschrock  */
790940d71d2Seschrock static int
791940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
792d91236feSeschrock     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
793d91236feSeschrock     boolean_t dorange)
794940d71d2Seschrock {
795940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
796940d71d2Seschrock 	boolean_t found;
797940d71d2Seschrock 	uint64_t max;
798940d71d2Seschrock 	ses_enum_node_t *snp;
799940d71d2Seschrock 
800940d71d2Seschrock 	/*
801940d71d2Seschrock 	 * First go through and count how many matching nodes we have.
802940d71d2Seschrock 	 */
803940d71d2Seschrock 	max = 0;
804940d71d2Seschrock 	found = B_FALSE;
805940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
806940d71d2Seschrock 	    snp = topo_list_next(snp)) {
807940d71d2Seschrock 		if (snp->sen_type == type) {
808940d71d2Seschrock 			found = B_TRUE;
809940d71d2Seschrock 			if (snp->sen_instance > max)
810940d71d2Seschrock 				max = snp->sen_instance;
811940d71d2Seschrock 		}
812940d71d2Seschrock 	}
813940d71d2Seschrock 
814940d71d2Seschrock 	/*
815940d71d2Seschrock 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
816940d71d2Seschrock 	 * Since we map both of these to 'disk', if an enclosure does this, we
817940d71d2Seschrock 	 * just ignore the array elements.
818940d71d2Seschrock 	 */
819940d71d2Seschrock 	if (!found ||
820940d71d2Seschrock 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
821940d71d2Seschrock 		return (0);
822940d71d2Seschrock 
823940d71d2Seschrock 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
824*53dbcc59SSundeep Panicker 	    cp->sec_csn, max + 1, nodename);
825940d71d2Seschrock 
826d91236feSeschrock 	if (dorange && topo_node_range_create(mod, pnode,
827940d71d2Seschrock 	    nodename, 0, max) != 0) {
828940d71d2Seschrock 		topo_mod_dprintf(mod,
829940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
830940d71d2Seschrock 		    topo_mod_errmsg(mod));
831940d71d2Seschrock 		return (-1);
832940d71d2Seschrock 	}
833940d71d2Seschrock 
834940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
835940d71d2Seschrock 	    snp = topo_list_next(snp)) {
836940d71d2Seschrock 		if (snp->sen_type == type) {
837940d71d2Seschrock 			if (ses_create_generic(sdp, snp, pnode,
838940d71d2Seschrock 			    nodename, defaultlabel) != 0)
839940d71d2Seschrock 				return (-1);
840940d71d2Seschrock 		}
841940d71d2Seschrock 	}
842940d71d2Seschrock 
843940d71d2Seschrock 	return (0);
844940d71d2Seschrock }
845940d71d2Seschrock 
846*53dbcc59SSundeep Panicker /*
847*53dbcc59SSundeep Panicker  * Instantiate a new subchassis instance in the topology.
848*53dbcc59SSundeep Panicker  */
849*53dbcc59SSundeep Panicker static int
850*53dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
851*53dbcc59SSundeep Panicker     ses_enum_chassis_t *scp)
852*53dbcc59SSundeep Panicker {
853*53dbcc59SSundeep Panicker 	topo_mod_t *mod = sdp->sed_mod;
854*53dbcc59SSundeep Panicker 	tnode_t *tn;
855*53dbcc59SSundeep Panicker 	nvlist_t *props;
856*53dbcc59SSundeep Panicker 	nvlist_t *auth = NULL, *fmri = NULL;
857*53dbcc59SSundeep Panicker 	char *part = NULL, *revision = NULL;
858*53dbcc59SSundeep Panicker 	uint64_t instance = scp->sec_instance;
859*53dbcc59SSundeep Panicker 	char *desc;
860*53dbcc59SSundeep Panicker 	char label[128];
861*53dbcc59SSundeep Panicker 	char **paths;
862*53dbcc59SSundeep Panicker 	int i, err;
863*53dbcc59SSundeep Panicker 	ses_enum_target_t *stp;
864*53dbcc59SSundeep Panicker 	int ret = -1;
865*53dbcc59SSundeep Panicker 
866*53dbcc59SSundeep Panicker 	/*
867*53dbcc59SSundeep Panicker 	 * Copy authority information from parent enclosure node
868*53dbcc59SSundeep Panicker 	 */
869*53dbcc59SSundeep Panicker 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
870*53dbcc59SSundeep Panicker 		goto error;
871*53dbcc59SSundeep Panicker 
872*53dbcc59SSundeep Panicker 	/*
873*53dbcc59SSundeep Panicker 	 * Record the subchassis serial number in the FMRI.
874*53dbcc59SSundeep Panicker 	 * For now, we assume that logical id is the subchassis serial number.
875*53dbcc59SSundeep Panicker 	 * If this assumption changes in future, then the following
876*53dbcc59SSundeep Panicker 	 * piece of code will need to be updated via an RFE.
877*53dbcc59SSundeep Panicker 	 */
878*53dbcc59SSundeep Panicker 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
879*53dbcc59SSundeep Panicker 	    SUBCHASSIS, (topo_instance_t)instance, NULL, auth, part, revision,
880*53dbcc59SSundeep Panicker 	    scp->sec_lid)) == NULL) {
881*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
882*53dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
883*53dbcc59SSundeep Panicker 		goto error;
884*53dbcc59SSundeep Panicker 	}
885*53dbcc59SSundeep Panicker 
886*53dbcc59SSundeep Panicker 	if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
887*53dbcc59SSundeep Panicker 	    instance, fmri)) == NULL) {
888*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
889*53dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
890*53dbcc59SSundeep Panicker 		goto error;
891*53dbcc59SSundeep Panicker 	}
892*53dbcc59SSundeep Panicker 
893*53dbcc59SSundeep Panicker 	props = ses_node_props(scp->sec_enclosure);
894*53dbcc59SSundeep Panicker 
895*53dbcc59SSundeep Panicker 	/*
896*53dbcc59SSundeep Panicker 	 * Look for the subchassis label in the following order:
897*53dbcc59SSundeep Panicker 	 *	<ses-description>
898*53dbcc59SSundeep Panicker 	 *	<ses-class-description> <instance>
899*53dbcc59SSundeep Panicker 	 *	<default-type-label> <instance>
900*53dbcc59SSundeep Panicker 	 *
901*53dbcc59SSundeep Panicker 	 * For subchassis, the default label is "SUBCHASSIS"
902*53dbcc59SSundeep Panicker 	 */
903*53dbcc59SSundeep Panicker 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
904*53dbcc59SSundeep Panicker 	    desc[0] == '\0') {
905*53dbcc59SSundeep Panicker 		if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
906*53dbcc59SSundeep Panicker 		    &desc) == 0 && desc[0] != '\0')
907*53dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label), "%s %llu", desc,
908*53dbcc59SSundeep Panicker 			    instance);
909*53dbcc59SSundeep Panicker 		else
910*53dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label),
911*53dbcc59SSundeep Panicker 			    "SUBCHASSIS %llu", instance);
912*53dbcc59SSundeep Panicker 		desc = label;
913*53dbcc59SSundeep Panicker 	}
914*53dbcc59SSundeep Panicker 
915*53dbcc59SSundeep Panicker 	if (topo_node_label_set(tn, desc, &err) != 0)
916*53dbcc59SSundeep Panicker 		goto error;
917*53dbcc59SSundeep Panicker 
918*53dbcc59SSundeep Panicker 	if (ses_set_standard_props(mod, tn, NULL,
919*53dbcc59SSundeep Panicker 	    ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
920*53dbcc59SSundeep Panicker 		goto error;
921*53dbcc59SSundeep Panicker 
922*53dbcc59SSundeep Panicker 	/*
923*53dbcc59SSundeep Panicker 	 * For enclosures, we want to include all possible targets (for upgrade
924*53dbcc59SSundeep Panicker 	 * purposes).
925*53dbcc59SSundeep Panicker 	 */
926*53dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
927*53dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
928*53dbcc59SSundeep Panicker 		;
929*53dbcc59SSundeep Panicker 
930*53dbcc59SSundeep Panicker 	verify(i != 0);
931*53dbcc59SSundeep Panicker 	paths = alloca(i * sizeof (char *));
932*53dbcc59SSundeep Panicker 
933*53dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
934*53dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
935*53dbcc59SSundeep Panicker 		paths[i] = stp->set_devpath;
936*53dbcc59SSundeep Panicker 
937*53dbcc59SSundeep Panicker 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
938*53dbcc59SSundeep Panicker 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
939*53dbcc59SSundeep Panicker 	    i, &err) != 0) {
940*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
941*53dbcc59SSundeep Panicker 		    TOPO_PROP_PATHS, topo_strerror(err));
942*53dbcc59SSundeep Panicker 		goto error;
943*53dbcc59SSundeep Panicker 	}
944*53dbcc59SSundeep Panicker 
945*53dbcc59SSundeep Panicker 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
946*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
947*53dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
948*53dbcc59SSundeep Panicker 		goto error;
949*53dbcc59SSundeep Panicker 	}
950*53dbcc59SSundeep Panicker 
951*53dbcc59SSundeep Panicker 	/*
952*53dbcc59SSundeep Panicker 	 * Create the nodes for controllers and bays.
953*53dbcc59SSundeep Panicker 	 */
954*53dbcc59SSundeep Panicker 	if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
955*53dbcc59SSundeep Panicker 	    CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
956*53dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
957*53dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0 ||
958*53dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
959*53dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0)
960*53dbcc59SSundeep Panicker 		goto error;
961*53dbcc59SSundeep Panicker 
962*53dbcc59SSundeep Panicker 	ret = 0;
963*53dbcc59SSundeep Panicker 
964*53dbcc59SSundeep Panicker error:
965*53dbcc59SSundeep Panicker 	nvlist_free(auth);
966*53dbcc59SSundeep Panicker 	nvlist_free(fmri);
967*53dbcc59SSundeep Panicker 	return (ret);
968*53dbcc59SSundeep Panicker }
969*53dbcc59SSundeep Panicker 
970940d71d2Seschrock /*
971940d71d2Seschrock  * Instantiate a new chassis instance in the topology.
972940d71d2Seschrock  */
973940d71d2Seschrock static int
974940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
975940d71d2Seschrock {
976940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
977940d71d2Seschrock 	nvlist_t *props;
978940d71d2Seschrock 	char *raw_manufacturer, *raw_model, *raw_revision;
979940d71d2Seschrock 	char *manufacturer = NULL, *model = NULL, *product = NULL;
980940d71d2Seschrock 	char *revision = NULL;
981940d71d2Seschrock 	char *serial;
9826efb64caSEric Schrock 	char **paths;
983940d71d2Seschrock 	size_t prodlen;
984940d71d2Seschrock 	tnode_t *tn;
985940d71d2Seschrock 	nvlist_t *fmri = NULL, *auth = NULL;
986940d71d2Seschrock 	int ret = -1;
987940d71d2Seschrock 	ses_enum_node_t *snp;
9886efb64caSEric Schrock 	ses_enum_target_t *stp;
989*53dbcc59SSundeep Panicker 	ses_enum_chassis_t *scp;
9906efb64caSEric Schrock 	int i, err;
991*53dbcc59SSundeep Panicker 	uint64_t sc_count = 0;
992940d71d2Seschrock 
993d91236feSeschrock 	/*
994d91236feSeschrock 	 * Ignore any internal enclosures.
995d91236feSeschrock 	 */
996d91236feSeschrock 	if (cp->sec_internal)
997d91236feSeschrock 		return (0);
998d91236feSeschrock 
999940d71d2Seschrock 	/*
1000940d71d2Seschrock 	 * Check to see if there are any devices presennt in the chassis.  If
1001940d71d2Seschrock 	 * not, ignore the chassis alltogether.  This is most useful for
1002940d71d2Seschrock 	 * ignoring internal HBAs that present a SES target but don't actually
1003940d71d2Seschrock 	 * manage any of the devices.
1004940d71d2Seschrock 	 */
1005940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1006940d71d2Seschrock 	    snp = topo_list_next(snp)) {
1007940d71d2Seschrock 		if (snp->sen_type == SES_ET_DEVICE ||
1008940d71d2Seschrock 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
1009940d71d2Seschrock 			break;
1010940d71d2Seschrock 	}
1011940d71d2Seschrock 
1012940d71d2Seschrock 	if (snp == NULL)
1013940d71d2Seschrock 		return (0);
1014940d71d2Seschrock 
1015940d71d2Seschrock 	props = ses_node_props(cp->sec_enclosure);
1016940d71d2Seschrock 
1017940d71d2Seschrock 	/*
1018940d71d2Seschrock 	 * We use the following property mappings:
1019940d71d2Seschrock 	 *
1020940d71d2Seschrock 	 * 	manufacturer		vendor-id
1021940d71d2Seschrock 	 * 	model			product-id
1022940d71d2Seschrock 	 * 	serial-number		libses-chassis-serial
1023940d71d2Seschrock 	 */
1024940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
1025940d71d2Seschrock 	    &raw_manufacturer) == 0);
1026940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
1027940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
1028940d71d2Seschrock 	    &raw_revision) == 0);
1029940d71d2Seschrock 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
1030940d71d2Seschrock 
1031940d71d2Seschrock 	/*
1032940d71d2Seschrock 	 * To construct the authority information, we 'clean' each string by
1033940d71d2Seschrock 	 * removing any offensive characters and trimmming whitespace.  For the
1034940d71d2Seschrock 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
1035940d71d2Seschrock 	 * also take the numerical serial number and convert it to a string.
1036940d71d2Seschrock 	 */
1037940d71d2Seschrock 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
1038940d71d2Seschrock 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
1039940d71d2Seschrock 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
1040940d71d2Seschrock 		goto error;
1041940d71d2Seschrock 	}
1042940d71d2Seschrock 
1043940d71d2Seschrock 	prodlen = strlen(manufacturer) + strlen(model) + 2;
1044940d71d2Seschrock 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
1045940d71d2Seschrock 		goto error;
1046940d71d2Seschrock 
1047940d71d2Seschrock 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
1048940d71d2Seschrock 
1049940d71d2Seschrock 	/*
1050940d71d2Seschrock 	 * Construct the topo node and bind it to our parent.
1051940d71d2Seschrock 	 */
1052940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
1053940d71d2Seschrock 		goto error;
1054940d71d2Seschrock 
1055940d71d2Seschrock 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
1056940d71d2Seschrock 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
1057940d71d2Seschrock 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1058940d71d2Seschrock 		goto error;
1059940d71d2Seschrock 	}
1060940d71d2Seschrock 
1061940d71d2Seschrock 	/*
1062940d71d2Seschrock 	 * We pass NULL for the parent FMRI because there is no resource
1063940d71d2Seschrock 	 * associated with it.  For the toplevel enclosure, we leave the
1064940d71d2Seschrock 	 * serial/part/revision portions empty, which are reserved for
1065940d71d2Seschrock 	 * individual components within the chassis.
1066940d71d2Seschrock 	 */
1067940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
1068940d71d2Seschrock 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
1069940d71d2Seschrock 	    model, revision, serial)) == NULL) {
1070940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1071940d71d2Seschrock 		    topo_mod_errmsg(mod));
1072940d71d2Seschrock 		goto error;
1073940d71d2Seschrock 	}
1074940d71d2Seschrock 
1075940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
1076940d71d2Seschrock 	    cp->sec_instance, fmri)) == NULL) {
1077940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1078940d71d2Seschrock 		    topo_mod_errmsg(mod));
1079940d71d2Seschrock 		goto error;
1080940d71d2Seschrock 	}
1081940d71d2Seschrock 
1082940d71d2Seschrock 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
1083940d71d2Seschrock 		topo_mod_dprintf(mod,
1084940d71d2Seschrock 		    "topo_method_register() failed: %s",
1085940d71d2Seschrock 		    topo_mod_errmsg(mod));
1086940d71d2Seschrock 		goto error;
1087940d71d2Seschrock 	}
1088940d71d2Seschrock 
1089940d71d2Seschrock 	if (ses_set_standard_props(mod, tn, auth,
1090940d71d2Seschrock 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
1091940d71d2Seschrock 		goto error;
1092940d71d2Seschrock 
10936efb64caSEric Schrock 	/*
10946efb64caSEric Schrock 	 * For enclosures, we want to include all possible targets (for upgrade
10956efb64caSEric Schrock 	 * purposes).
10966efb64caSEric Schrock 	 */
10976efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
10986efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
10996efb64caSEric Schrock 		;
11006efb64caSEric Schrock 
11016efb64caSEric Schrock 	verify(i != 0);
11026efb64caSEric Schrock 	paths = alloca(i * sizeof (char *));
11036efb64caSEric Schrock 
11046efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
11056efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
11066efb64caSEric Schrock 		paths[i] = stp->set_devpath;
11076efb64caSEric Schrock 
11086efb64caSEric Schrock 
11096efb64caSEric Schrock 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
11106efb64caSEric Schrock 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
11116efb64caSEric Schrock 	    i, &err) != 0) {
11126efb64caSEric Schrock 		topo_mod_dprintf(mod,
11136efb64caSEric Schrock 		    "failed to create property %s: %s\n",
11146efb64caSEric Schrock 		    TOPO_PROP_PATHS, topo_strerror(err));
11156efb64caSEric Schrock 		goto error;
11166efb64caSEric Schrock 	}
11176efb64caSEric Schrock 
1118940d71d2Seschrock 	/*
1119940d71d2Seschrock 	 * Create the nodes for power supplies, fans, and devices.
1120940d71d2Seschrock 	 */
1121940d71d2Seschrock 	if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
1122d91236feSeschrock 	    PSU, "PSU", cp, B_TRUE) != 0 ||
1123940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_COOLING,
1124d91236feSeschrock 	    FAN, "FAN", cp, B_TRUE) != 0 ||
1125940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
1126d91236feSeschrock 	    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
1127940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
1128d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0 ||
1129940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
1130d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0)
1131940d71d2Seschrock 		goto error;
1132940d71d2Seschrock 
1133*53dbcc59SSundeep Panicker 	if (cp->sec_scinstance > 0 &&
1134*53dbcc59SSundeep Panicker 	    topo_node_range_create(mod, tn, SUBCHASSIS, 0,
1135*53dbcc59SSundeep Panicker 	    cp->sec_scinstance - 1) != 0) {
1136*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
1137*53dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
1138*53dbcc59SSundeep Panicker 		goto error;
1139*53dbcc59SSundeep Panicker 	}
1140*53dbcc59SSundeep Panicker 
1141*53dbcc59SSundeep Panicker 	for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
1142*53dbcc59SSundeep Panicker 	    scp = topo_list_next(scp)) {
1143*53dbcc59SSundeep Panicker 
1144*53dbcc59SSundeep Panicker 		if (ses_create_subchassis(sdp, tn, scp) != 0)
1145*53dbcc59SSundeep Panicker 			goto error;
1146*53dbcc59SSundeep Panicker 
1147*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "created Subchassis node with "
1148*53dbcc59SSundeep Panicker 		    "LID (%s)\n  and target (%s) under Chassis with CSN (%s)",
1149*53dbcc59SSundeep Panicker 		    scp->sec_lid, scp->sec_target->set_devpath, cp->sec_csn);
1150*53dbcc59SSundeep Panicker 
1151*53dbcc59SSundeep Panicker 		sc_count++;
1152*53dbcc59SSundeep Panicker 	}
1153*53dbcc59SSundeep Panicker 
1154*53dbcc59SSundeep Panicker 	topo_mod_dprintf(mod, "%s: created %llu %s nodes",
1155*53dbcc59SSundeep Panicker 	    cp->sec_csn, sc_count, SUBCHASSIS);
1156*53dbcc59SSundeep Panicker 
11576efb64caSEric Schrock 	cp->sec_target->set_refcount++;
11586efb64caSEric Schrock 	topo_node_setspecific(tn, cp->sec_target);
1159d91236feSeschrock 
1160940d71d2Seschrock 	ret = 0;
1161940d71d2Seschrock error:
1162940d71d2Seschrock 	topo_mod_strfree(mod, manufacturer);
1163940d71d2Seschrock 	topo_mod_strfree(mod, model);
1164940d71d2Seschrock 	topo_mod_strfree(mod, revision);
1165940d71d2Seschrock 	topo_mod_strfree(mod, product);
1166940d71d2Seschrock 
1167940d71d2Seschrock 	nvlist_free(fmri);
1168940d71d2Seschrock 	nvlist_free(auth);
1169940d71d2Seschrock 	return (ret);
1170940d71d2Seschrock }
1171940d71d2Seschrock 
1172d91236feSeschrock /*
1173d91236feSeschrock  * Create a bay node explicitly enumerated via XML.
1174d91236feSeschrock  */
1175d91236feSeschrock static int
1176d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
1177d91236feSeschrock {
1178d91236feSeschrock 	topo_mod_t *mod = sdp->sed_mod;
1179d91236feSeschrock 	ses_enum_chassis_t *cp;
1180d91236feSeschrock 
1181d91236feSeschrock 	/*
1182d91236feSeschrock 	 * Iterate over chassis looking for an internal enclosure.  This
1183d91236feSeschrock 	 * property is set via a vendor-specific plugin, and there should only
1184d91236feSeschrock 	 * ever be a single internal chassis in a system.
1185d91236feSeschrock 	 */
1186d91236feSeschrock 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1187d91236feSeschrock 	    cp = topo_list_next(cp)) {
1188d91236feSeschrock 		if (cp->sec_internal)
1189d91236feSeschrock 			break;
1190d91236feSeschrock 	}
1191d91236feSeschrock 
1192d91236feSeschrock 	if (cp == NULL) {
1193d91236feSeschrock 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
1194d91236feSeschrock 		return (-1);
1195d91236feSeschrock 	}
1196d91236feSeschrock 
1197d91236feSeschrock 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
1198d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0 ||
1199d91236feSeschrock 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
1200d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0)
1201d91236feSeschrock 		return (-1);
1202d91236feSeschrock 
1203d91236feSeschrock 	return (0);
1204d91236feSeschrock }
1205*53dbcc59SSundeep Panicker 
1206*53dbcc59SSundeep Panicker /*
1207*53dbcc59SSundeep Panicker  * Initialize chassis or subchassis.
1208*53dbcc59SSundeep Panicker  */
1209*53dbcc59SSundeep Panicker static int
1210*53dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
1211*53dbcc59SSundeep Panicker     ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
1212*53dbcc59SSundeep Panicker     char *lid, ses_chassis_type_e flags)
1213*53dbcc59SSundeep Panicker {
1214*53dbcc59SSundeep Panicker 	boolean_t internal, ident;
1215*53dbcc59SSundeep Panicker 
1216*53dbcc59SSundeep Panicker 	assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
1217*53dbcc59SSundeep Panicker 	    SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
1218*53dbcc59SSundeep Panicker 
1219*53dbcc59SSundeep Panicker 	assert((cp != NULL) && (np != NULL) && (props != NULL) &&
1220*53dbcc59SSundeep Panicker 	    (lid != NULL));
1221*53dbcc59SSundeep Panicker 
1222*53dbcc59SSundeep Panicker 	if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
1223*53dbcc59SSundeep Panicker 		assert(pcp != NULL);
1224*53dbcc59SSundeep Panicker 
1225*53dbcc59SSundeep Panicker 	topo_mod_dprintf(mod, "ses_init_chassis: %s: lid(%s), flags (%d)",
1226*53dbcc59SSundeep Panicker 	    sdp->sed_name, lid, flags);
1227*53dbcc59SSundeep Panicker 
1228*53dbcc59SSundeep Panicker 	if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
1229*53dbcc59SSundeep Panicker 
1230*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "new chassis/subchassis");
1231*53dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
1232*53dbcc59SSundeep Panicker 		    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
1233*53dbcc59SSundeep Panicker 			cp->sec_internal = internal;
1234*53dbcc59SSundeep Panicker 
1235*53dbcc59SSundeep Panicker 		cp->sec_lid = lid;
1236*53dbcc59SSundeep Panicker 		cp->sec_enclosure = np;
1237*53dbcc59SSundeep Panicker 		cp->sec_target = sdp->sed_target;
1238*53dbcc59SSundeep Panicker 
1239*53dbcc59SSundeep Panicker 		if (flags & SES_NEW_CHASSIS) {
1240*53dbcc59SSundeep Panicker 			cp->sec_instance = sdp->sed_instance++;
1241*53dbcc59SSundeep Panicker 			topo_list_append(&sdp->sed_chassis, cp);
1242*53dbcc59SSundeep Panicker 		} else {
1243*53dbcc59SSundeep Panicker 			cp->sec_instance = pcp->sec_scinstance++;
1244*53dbcc59SSundeep Panicker 			topo_list_append(&pcp->sec_subchassis, cp);
1245*53dbcc59SSundeep Panicker 		}
1246*53dbcc59SSundeep Panicker 
1247*53dbcc59SSundeep Panicker 	} else {
1248*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "dup chassis/subchassis");
1249*53dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
1250*53dbcc59SSundeep Panicker 		    SES_PROP_IDENT, &ident) == 0) {
1251*53dbcc59SSundeep Panicker 			topo_mod_dprintf(mod,  "overriding enclosure node");
1252*53dbcc59SSundeep Panicker 
1253*53dbcc59SSundeep Panicker 			cp->sec_enclosure = np;
1254*53dbcc59SSundeep Panicker 			cp->sec_target = sdp->sed_target;
1255*53dbcc59SSundeep Panicker 		}
1256*53dbcc59SSundeep Panicker 	}
1257*53dbcc59SSundeep Panicker 
1258*53dbcc59SSundeep Panicker 	topo_list_append(&cp->sec_targets, sdp->sed_target);
1259*53dbcc59SSundeep Panicker 	sdp->sed_current = cp;
1260*53dbcc59SSundeep Panicker 
1261*53dbcc59SSundeep Panicker 	return (0);
1262*53dbcc59SSundeep Panicker }
1263*53dbcc59SSundeep Panicker 
1264940d71d2Seschrock /*
1265940d71d2Seschrock  * Gather nodes from the current SES target into our chassis list, merging the
1266940d71d2Seschrock  * results if necessary.
1267940d71d2Seschrock  */
1268940d71d2Seschrock static ses_walk_action_t
1269940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data)
1270940d71d2Seschrock {
1271940d71d2Seschrock 	nvlist_t *props = ses_node_props(np);
1272940d71d2Seschrock 	ses_enum_data_t *sdp = data;
1273940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1274*53dbcc59SSundeep Panicker 	ses_enum_chassis_t *cp, *scp;
1275940d71d2Seschrock 	ses_enum_node_t *snp;
12760b32bb8bSEric Schrock 	ses_alt_node_t *sap;
1277940d71d2Seschrock 	char *csn;
1278940d71d2Seschrock 	uint64_t instance, type;
1279d91236feSeschrock 	uint64_t prevstatus, status;
1280*53dbcc59SSundeep Panicker 	boolean_t report;
1281*53dbcc59SSundeep Panicker 	boolean_t have_subchassis = B_TRUE;
1282*53dbcc59SSundeep Panicker 	char *lid;
1283940d71d2Seschrock 
1284940d71d2Seschrock 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
1285940d71d2Seschrock 		/*
1286940d71d2Seschrock 		 * If we have already identified the chassis for this target,
1287940d71d2Seschrock 		 * then this is a secondary enclosure and we should ignore it,
1288940d71d2Seschrock 		 * along with the rest of the tree (since this is depth-first).
1289940d71d2Seschrock 		 */
1290940d71d2Seschrock 		if (sdp->sed_current != NULL)
1291940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1292940d71d2Seschrock 
1293940d71d2Seschrock 		/*
1294940d71d2Seschrock 		 * Go through the list of chassis we have seen so far and see
1295940d71d2Seschrock 		 * if this serial number matches one of the known values.
1296*53dbcc59SSundeep Panicker 		 * If so, check whether this enclosure is a subchassis.
1297940d71d2Seschrock 		 */
1298940d71d2Seschrock 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
1299940d71d2Seschrock 		    &csn) != 0)
1300940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1301940d71d2Seschrock 
1302*53dbcc59SSundeep Panicker 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
1303*53dbcc59SSundeep Panicker 		    &lid) != 0) {
1304*53dbcc59SSundeep Panicker 			have_subchassis = B_FALSE;
1305*53dbcc59SSundeep Panicker 			if ((lid = topo_mod_zalloc(mod, sizeof (char))) == NULL)
1306*53dbcc59SSundeep Panicker 				goto error;
1307*53dbcc59SSundeep Panicker 		}
1308*53dbcc59SSundeep Panicker 
1309*53dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
1310*53dbcc59SSundeep Panicker 		    "CSN (%s), LID (%s)", sdp->sed_name, csn, lid);
1311*53dbcc59SSundeep Panicker 
1312*53dbcc59SSundeep Panicker 		/*
1313*53dbcc59SSundeep Panicker 		 * We need to determine whether this enclosure node
1314*53dbcc59SSundeep Panicker 		 * represents a chassis or a subchassis. Since we may
1315*53dbcc59SSundeep Panicker 		 * receive the enclosure nodes in a non-deterministic
1316*53dbcc59SSundeep Panicker 		 * manner, we need to account for all possible combinations:
1317*53dbcc59SSundeep Panicker 		 *	1. Chassis for the current CSN has not yet been
1318*53dbcc59SSundeep Panicker 		 *	   allocated
1319*53dbcc59SSundeep Panicker 		 *		1.1 This is a new chassis:
1320*53dbcc59SSundeep Panicker 		 *			allocate and instantiate the chassis
1321*53dbcc59SSundeep Panicker 		 *		1.2 This is a new subchassis:
1322*53dbcc59SSundeep Panicker 		 *			allocate a placeholder chassis
1323*53dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
1324*53dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
1325*53dbcc59SSundeep Panicker 		 *	2. Chassis for the current CSN has been allocated
1326*53dbcc59SSundeep Panicker 		 *		2.1 This is a duplicate chassis enclosure
1327*53dbcc59SSundeep Panicker 		 *			check whether to override old chassis
1328*53dbcc59SSundeep Panicker 		 *			append to chassis' target list
1329*53dbcc59SSundeep Panicker 		 *		2.2 Only placeholder chassis exists
1330*53dbcc59SSundeep Panicker 		 *			fill in the chassis fields
1331*53dbcc59SSundeep Panicker 		 *		2.3 This is a new subchassis
1332*53dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
1333*53dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
1334*53dbcc59SSundeep Panicker 		 *		2.4 This is a duplicate subchassis enclosure
1335*53dbcc59SSundeep Panicker 		 *			 check whether to override old chassis
1336*53dbcc59SSundeep Panicker 		 *			 append to chassis' target list
1337*53dbcc59SSundeep Panicker 		 */
1338*53dbcc59SSundeep Panicker 
1339940d71d2Seschrock 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1340*53dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
1341*53dbcc59SSundeep Panicker 			if (strcmp(cp->sec_csn, csn) == 0)
1342940d71d2Seschrock 				break;
1343940d71d2Seschrock 
1344940d71d2Seschrock 		if (cp == NULL) {
1345*53dbcc59SSundeep Panicker 			/* 1. Haven't seen a chassis with this CSN before */
1346940d71d2Seschrock 
1347940d71d2Seschrock 			if ((cp = topo_mod_zalloc(mod,
1348940d71d2Seschrock 			    sizeof (ses_enum_chassis_t))) == NULL)
1349940d71d2Seschrock 				goto error;
1350940d71d2Seschrock 
1351940d71d2Seschrock 			cp->sec_csn = csn;
1352*53dbcc59SSundeep Panicker 
1353*53dbcc59SSundeep Panicker 			if (!have_subchassis || strcmp(csn, lid) == 0) {
1354*53dbcc59SSundeep Panicker 				/* 1.1 This is a new chassis */
1355*53dbcc59SSundeep Panicker 
1356*53dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
1357*53dbcc59SSundeep Panicker 				    "chassis with CSN (%s) and LID (%s)",
1358*53dbcc59SSundeep Panicker 				    sdp->sed_name, csn, lid);
1359*53dbcc59SSundeep Panicker 
1360*53dbcc59SSundeep Panicker 				if (ses_init_chassis(mod, sdp, NULL, cp,
1361*53dbcc59SSundeep Panicker 				    np, props, lid, SES_NEW_CHASSIS) < 0)
1362*53dbcc59SSundeep Panicker 					goto error;
1363*53dbcc59SSundeep Panicker 			} else {
1364*53dbcc59SSundeep Panicker 				/* 1.2 This is a new subchassis */
1365*53dbcc59SSundeep Panicker 
1366*53dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
1367*53dbcc59SSundeep Panicker 				    "subchassis with CSN (%s) and LID (%s)",
1368*53dbcc59SSundeep Panicker 				    sdp->sed_name, csn, lid);
1369*53dbcc59SSundeep Panicker 
1370*53dbcc59SSundeep Panicker 				if ((scp = topo_mod_zalloc(mod,
1371*53dbcc59SSundeep Panicker 				    sizeof (ses_enum_chassis_t))) == NULL)
1372*53dbcc59SSundeep Panicker 					goto error;
1373*53dbcc59SSundeep Panicker 
1374*53dbcc59SSundeep Panicker 				scp->sec_csn = csn;
1375*53dbcc59SSundeep Panicker 
1376*53dbcc59SSundeep Panicker 				if (ses_init_chassis(mod, sdp, cp, scp,
1377*53dbcc59SSundeep Panicker 				    np, props, lid, SES_NEW_SUBCHASSIS) < 0)
1378*53dbcc59SSundeep Panicker 					goto error;
1379*53dbcc59SSundeep Panicker 			}
13806efb64caSEric Schrock 		} else {
1381*53dbcc59SSundeep Panicker 			/* 2. We have a chassis with this CSN */
1382*53dbcc59SSundeep Panicker 
1383*53dbcc59SSundeep Panicker 			if (!have_subchassis || strcmp(csn, lid) == 0) {
1384*53dbcc59SSundeep Panicker 				/* This is a chassis */
1385*53dbcc59SSundeep Panicker 
1386*53dbcc59SSundeep Panicker 				if (!have_subchassis ||
1387*53dbcc59SSundeep Panicker 				    strlen(cp->sec_lid) > 0) {
1388*53dbcc59SSundeep Panicker 					/* 2.1 This is a duplicate chassis */
1389*53dbcc59SSundeep Panicker 
1390*53dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
1391*53dbcc59SSundeep Panicker 					    "duplicate chassis with CSN (%s) "
1392*53dbcc59SSundeep Panicker 					    "and LID (%s)",
1393*53dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
1394*53dbcc59SSundeep Panicker 
1395*53dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, NULL, cp,
1396*53dbcc59SSundeep Panicker 					    np, props, lid,
1397*53dbcc59SSundeep Panicker 					    SES_DUP_CHASSIS) < 0)
1398*53dbcc59SSundeep Panicker 						goto error;
1399*53dbcc59SSundeep Panicker 				} else {
1400*53dbcc59SSundeep Panicker 					/* 2.2 Init the placeholder chassis */
1401*53dbcc59SSundeep Panicker 
1402*53dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize"
1403*53dbcc59SSundeep Panicker 					    "placeholder chassis with CSN (%s) "
1404*53dbcc59SSundeep Panicker 					    "and LID (%s)",
1405*53dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
1406*53dbcc59SSundeep Panicker 
1407*53dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, NULL, cp,
1408*53dbcc59SSundeep Panicker 					    np, props, lid,
1409*53dbcc59SSundeep Panicker 					    SES_NEW_CHASSIS) < 0)
1410*53dbcc59SSundeep Panicker 						goto error;
1411*53dbcc59SSundeep Panicker 
1412*53dbcc59SSundeep Panicker 				}
1413*53dbcc59SSundeep Panicker 			} else {
1414*53dbcc59SSundeep Panicker 				/* This is a subchassis */
1415*53dbcc59SSundeep Panicker 
1416*53dbcc59SSundeep Panicker 				for (scp = topo_list_next(&cp->sec_subchassis);
1417*53dbcc59SSundeep Panicker 				    scp != NULL; scp = topo_list_next(scp))
1418*53dbcc59SSundeep Panicker 					if (strcmp(scp->sec_lid, lid) == 0)
1419*53dbcc59SSundeep Panicker 						break;
1420*53dbcc59SSundeep Panicker 
1421*53dbcc59SSundeep Panicker 				if (scp == NULL) {
1422*53dbcc59SSundeep Panicker 					/* 2.3 This is a new subchassis */
1423*53dbcc59SSundeep Panicker 
1424*53dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize "
1425*53dbcc59SSundeep Panicker 					    "new subchassis with CSN (%s) "
1426*53dbcc59SSundeep Panicker 					    "and LID (%s)",
1427*53dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
1428*53dbcc59SSundeep Panicker 
1429*53dbcc59SSundeep Panicker 					if ((scp = topo_mod_zalloc(mod,
1430*53dbcc59SSundeep Panicker 					    sizeof (ses_enum_chassis_t)))
1431*53dbcc59SSundeep Panicker 					    == NULL)
1432*53dbcc59SSundeep Panicker 						goto error;
1433*53dbcc59SSundeep Panicker 
1434*53dbcc59SSundeep Panicker 					scp->sec_csn = csn;
1435*53dbcc59SSundeep Panicker 
1436*53dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
1437*53dbcc59SSundeep Panicker 					    np, props, lid,
1438*53dbcc59SSundeep Panicker 					    SES_NEW_SUBCHASSIS) < 0)
1439*53dbcc59SSundeep Panicker 						goto error;
1440*53dbcc59SSundeep Panicker 				} else {
1441*53dbcc59SSundeep Panicker 					/* 2.4 This is a duplicate subchassis */
1442*53dbcc59SSundeep Panicker 
1443*53dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
1444*53dbcc59SSundeep Panicker 					    "duplicate subchassis with "
1445*53dbcc59SSundeep Panicker 					    "CSN (%s) and LID (%s)",
1446*53dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
1447*53dbcc59SSundeep Panicker 
1448*53dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
1449*53dbcc59SSundeep Panicker 					    np, props, lid,
1450*53dbcc59SSundeep Panicker 					    SES_DUP_SUBCHASSIS) < 0)
1451*53dbcc59SSundeep Panicker 						goto error;
1452*53dbcc59SSundeep Panicker 				}
14536efb64caSEric Schrock 			}
1454940d71d2Seschrock 		}
1455940d71d2Seschrock 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
1456940d71d2Seschrock 		/*
1457940d71d2Seschrock 		 * If we haven't yet seen an enclosure node and identified the
1458940d71d2Seschrock 		 * current chassis, something is very wrong; bail out.
1459940d71d2Seschrock 		 */
1460940d71d2Seschrock 		if (sdp->sed_current == NULL)
1461940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1462940d71d2Seschrock 
1463940d71d2Seschrock 		/*
1464940d71d2Seschrock 		 * If this isn't one of the element types we care about, then
1465940d71d2Seschrock 		 * ignore it.
1466940d71d2Seschrock 		 */
1467940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
1468940d71d2Seschrock 		    &type) == 0);
1469940d71d2Seschrock 		if (type != SES_ET_DEVICE &&
1470940d71d2Seschrock 		    type != SES_ET_ARRAY_DEVICE &&
1471940d71d2Seschrock 		    type != SES_ET_COOLING &&
1472940d71d2Seschrock 		    type != SES_ET_POWER_SUPPLY &&
1473940d71d2Seschrock 		    type != SES_ET_ESC_ELECTRONICS)
1474940d71d2Seschrock 			return (SES_WALK_ACTION_CONTINUE);
1475940d71d2Seschrock 
1476940d71d2Seschrock 		/*
1477940d71d2Seschrock 		 * Get the current instance number and see if we already know
1478940d71d2Seschrock 		 * about this element.  If so, it means we have multiple paths
1479940d71d2Seschrock 		 * to the same elements, and we should ignore the current path.
1480940d71d2Seschrock 		 */
1481940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
1482940d71d2Seschrock 		    &instance) == 0);
1483940d71d2Seschrock 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
1484940d71d2Seschrock 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
1485940d71d2Seschrock 			    &instance);
1486940d71d2Seschrock 
1487940d71d2Seschrock 		cp = sdp->sed_current;
1488940d71d2Seschrock 
1489940d71d2Seschrock 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1490940d71d2Seschrock 		    snp = topo_list_next(snp)) {
1491940d71d2Seschrock 			if (snp->sen_type == type &&
1492940d71d2Seschrock 			    snp->sen_instance == instance)
1493d91236feSeschrock 				break;
1494d91236feSeschrock 		}
1495d91236feSeschrock 
1496d91236feSeschrock 		/*
1497d91236feSeschrock 		 * We prefer the new element under the following circumstances:
1498d91236feSeschrock 		 *
1499d91236feSeschrock 		 * - The currently known element's status is unknown or not
1500d91236feSeschrock 		 *   available, but the new element has a known status.  This
1501d91236feSeschrock 		 *   occurs if a given element is only available through a
1502d91236feSeschrock 		 *   particular target.
1503d91236feSeschrock 		 *
1504d91236feSeschrock 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
1505d91236feSeschrock 		 *   property is set.  This allows us to get reliable firmware
1506d91236feSeschrock 		 *   revision information from the enclosure node.
1507d91236feSeschrock 		 */
1508d91236feSeschrock 		if (snp != NULL) {
1509d91236feSeschrock 			if (nvlist_lookup_uint64(
1510d91236feSeschrock 			    ses_node_props(snp->sen_node),
1511d91236feSeschrock 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
1512d91236feSeschrock 				prevstatus = SES_ESC_UNSUPPORTED;
1513d91236feSeschrock 			if (nvlist_lookup_uint64(
1514d91236feSeschrock 			    props, SES_PROP_STATUS_CODE, &status) != 0)
1515d91236feSeschrock 				status = SES_ESC_UNSUPPORTED;
1516d91236feSeschrock 			if (nvlist_lookup_boolean_value(
1517d91236feSeschrock 			    props, SES_PROP_REPORT, &report) != 0)
1518d91236feSeschrock 				report = B_FALSE;
1519d91236feSeschrock 
1520d91236feSeschrock 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
1521d91236feSeschrock 			    !SES_STATUS_UNAVAIL(status)) ||
1522d91236feSeschrock 			    (type == SES_ET_ESC_ELECTRONICS &&
1523d91236feSeschrock 			    report)) {
1524d91236feSeschrock 				snp->sen_node = np;
1525d91236feSeschrock 				snp->sen_target = sdp->sed_target;
1526d91236feSeschrock 			}
1527d91236feSeschrock 
15280b32bb8bSEric Schrock 			if ((sap = topo_mod_zalloc(mod,
15290b32bb8bSEric Schrock 			    sizeof (ses_alt_node_t))) == NULL)
15300b32bb8bSEric Schrock 				goto error;
15310b32bb8bSEric Schrock 
15320b32bb8bSEric Schrock 			sap->san_node = np;
15330b32bb8bSEric Schrock 			topo_list_append(&snp->sen_alt_nodes, sap);
15340b32bb8bSEric Schrock 
1535d91236feSeschrock 			return (SES_WALK_ACTION_CONTINUE);
1536940d71d2Seschrock 		}
1537940d71d2Seschrock 
1538940d71d2Seschrock 		if ((snp = topo_mod_zalloc(mod,
1539940d71d2Seschrock 		    sizeof (ses_enum_node_t))) == NULL)
1540940d71d2Seschrock 			goto error;
1541940d71d2Seschrock 
15420b32bb8bSEric Schrock 		if ((sap = topo_mod_zalloc(mod,
15430b32bb8bSEric Schrock 		    sizeof (ses_alt_node_t))) == NULL) {
15440b32bb8bSEric Schrock 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
15450b32bb8bSEric Schrock 			goto error;
15460b32bb8bSEric Schrock 		}
15470b32bb8bSEric Schrock 
1548940d71d2Seschrock 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
1549940d71d2Seschrock 		    sdp->sed_name, type, instance);
1550940d71d2Seschrock 		snp->sen_node = np;
1551940d71d2Seschrock 		snp->sen_type = type;
1552940d71d2Seschrock 		snp->sen_instance = instance;
1553940d71d2Seschrock 		snp->sen_target = sdp->sed_target;
15540b32bb8bSEric Schrock 		sap->san_node = np;
15550b32bb8bSEric Schrock 		topo_list_append(&snp->sen_alt_nodes, sap);
1556940d71d2Seschrock 		topo_list_append(&cp->sec_nodes, snp);
1557940d71d2Seschrock 
1558940d71d2Seschrock 		if (type == SES_ET_DEVICE)
1559940d71d2Seschrock 			cp->sec_hasdev = B_TRUE;
1560940d71d2Seschrock 	}
1561940d71d2Seschrock 
1562940d71d2Seschrock 	return (SES_WALK_ACTION_CONTINUE);
1563940d71d2Seschrock 
1564940d71d2Seschrock error:
1565940d71d2Seschrock 	sdp->sed_errno = -1;
1566940d71d2Seschrock 	return (SES_WALK_ACTION_TERMINATE);
1567940d71d2Seschrock }
1568940d71d2Seschrock 
1569940d71d2Seschrock static int
1570940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
1571940d71d2Seschrock {
1572940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1573940d71d2Seschrock 	DIR *dir;
1574940d71d2Seschrock 	struct dirent *dp;
1575940d71d2Seschrock 	char path[PATH_MAX];
1576940d71d2Seschrock 	ses_enum_target_t *stp;
1577940d71d2Seschrock 	int err = -1;
1578940d71d2Seschrock 
1579940d71d2Seschrock 	/*
1580940d71d2Seschrock 	 * Open the SES target directory and iterate over any available
1581940d71d2Seschrock 	 * targets.
1582940d71d2Seschrock 	 */
1583940d71d2Seschrock 	if ((dir = opendir(dirpath)) == NULL) {
1584940d71d2Seschrock 		/*
1585940d71d2Seschrock 		 * If the SES target directory does not exist, then return as if
1586940d71d2Seschrock 		 * there are no active targets.
1587940d71d2Seschrock 		 */
1588940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to open ses "
1589940d71d2Seschrock 		    "directory '%s'", dirpath);
1590940d71d2Seschrock 		return (0);
1591940d71d2Seschrock 	}
1592940d71d2Seschrock 
1593940d71d2Seschrock 	while ((dp = readdir(dir)) != NULL) {
1594940d71d2Seschrock 		if (strcmp(dp->d_name, ".") == 0 ||
1595940d71d2Seschrock 		    strcmp(dp->d_name, "..") == 0)
1596940d71d2Seschrock 			continue;
1597940d71d2Seschrock 
1598940d71d2Seschrock 		/*
1599940d71d2Seschrock 		 * Create a new target instance and take a snapshot.
1600940d71d2Seschrock 		 */
1601940d71d2Seschrock 		if ((stp = topo_mod_zalloc(mod,
1602940d71d2Seschrock 		    sizeof (ses_enum_target_t))) == NULL)
1603940d71d2Seschrock 			goto error;
1604940d71d2Seschrock 
16050b32bb8bSEric Schrock 		(void) pthread_mutex_init(&stp->set_lock, NULL);
16060b32bb8bSEric Schrock 
1607940d71d2Seschrock 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
1608940d71d2Seschrock 		    dp->d_name);
1609940d71d2Seschrock 
1610940d71d2Seschrock 		/*
1611940d71d2Seschrock 		 * We keep track of the SES device path and export it on a
1612940d71d2Seschrock 		 * per-node basis to allow higher level software to get to the
1613940d71d2Seschrock 		 * corresponding SES state.
1614940d71d2Seschrock 		 */
1615940d71d2Seschrock 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
1616940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1617940d71d2Seschrock 			goto error;
1618940d71d2Seschrock 		}
1619940d71d2Seschrock 
1620940d71d2Seschrock 		if ((stp->set_target =
1621940d71d2Seschrock 		    ses_open(LIBSES_VERSION, path)) == NULL) {
1622940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to open ses target "
1623940d71d2Seschrock 			    "'%s': %s", dp->d_name, ses_errmsg());
1624940d71d2Seschrock 			topo_mod_strfree(mod, stp->set_devpath);
1625940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1626940d71d2Seschrock 			continue;
1627940d71d2Seschrock 		}
1628940d71d2Seschrock 
1629940d71d2Seschrock 		stp->set_refcount = 1;
1630940d71d2Seschrock 		sdp->sed_target = stp;
1631940d71d2Seschrock 		stp->set_snap = ses_snap_hold(stp->set_target);
1632940d71d2Seschrock 		if (gettimeofday(&stp->set_snaptime, NULL) != 0)
1633940d71d2Seschrock 			stp->set_snaptime.tv_sec = time(NULL);
1634940d71d2Seschrock 
1635940d71d2Seschrock 		/*
1636940d71d2Seschrock 		 * Enumerate over all SES elements and merge them into the
1637940d71d2Seschrock 		 * correct ses_enum_chassis_t.
1638940d71d2Seschrock 		 */
1639940d71d2Seschrock 		sdp->sed_current = NULL;
1640940d71d2Seschrock 		sdp->sed_errno = 0;
1641940d71d2Seschrock 		sdp->sed_name = dp->d_name;
1642940d71d2Seschrock 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
1643940d71d2Seschrock 
1644940d71d2Seschrock 		if (sdp->sed_errno != 0)
1645940d71d2Seschrock 			goto error;
1646940d71d2Seschrock 	}
1647940d71d2Seschrock 
1648940d71d2Seschrock 	err = 0;
1649940d71d2Seschrock error:
1650940d71d2Seschrock 	closedir(dir);
1651940d71d2Seschrock 	return (err);
1652940d71d2Seschrock }
1653940d71d2Seschrock 
1654940d71d2Seschrock static void
1655940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn)
1656940d71d2Seschrock {
1657940d71d2Seschrock 	ses_enum_target_t *stp;
1658940d71d2Seschrock 
1659940d71d2Seschrock 	if ((stp = topo_node_getspecific(tn)) != NULL)
1660940d71d2Seschrock 		ses_target_free(mod, stp);
1661940d71d2Seschrock }
1662940d71d2Seschrock 
1663940d71d2Seschrock /*ARGSUSED*/
1664940d71d2Seschrock static int
1665940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
1666940d71d2Seschrock     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
1667940d71d2Seschrock {
1668940d71d2Seschrock 	ses_enum_chassis_t *cp;
1669d91236feSeschrock 	ses_enum_data_t *data;
1670940d71d2Seschrock 
1671940d71d2Seschrock 	/*
1672940d71d2Seschrock 	 * Check to make sure we're being invoked sensibly, and that we're not
1673940d71d2Seschrock 	 * being invoked as part of a post-processing step.
1674940d71d2Seschrock 	 */
1675d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
1676940d71d2Seschrock 		return (0);
1677940d71d2Seschrock 
1678940d71d2Seschrock 	/*
1679d91236feSeschrock 	 * If this is the first time we've called our enumeration method, then
1680d91236feSeschrock 	 * gather information about any available enclosures.
1681940d71d2Seschrock 	 */
1682d91236feSeschrock 	if ((data = topo_mod_getspecific(mod)) == NULL) {
1683d91236feSeschrock 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
1684d91236feSeschrock 		    NULL)
1685d91236feSeschrock 			return (-1);
1686940d71d2Seschrock 
1687d91236feSeschrock 		data->sed_mod = mod;
1688d91236feSeschrock 		topo_mod_setspecific(mod, data);
1689d91236feSeschrock 
1690d91236feSeschrock 		if (disk_list_gather(mod, &data->sed_disks) != 0)
1691d91236feSeschrock 			goto error;
1692d91236feSeschrock 
1693d91236feSeschrock 		/*
1694d91236feSeschrock 		 * We search both the ses(7D) and sgen(7D) locations, so we are
1695d91236feSeschrock 		 * independent of any particular driver class bindings.
1696d91236feSeschrock 		 */
1697d91236feSeschrock 		if (ses_process_dir("/dev/es", data) != 0 ||
1698d91236feSeschrock 		    ses_process_dir("/dev/scsi/ses", data) != 0)
1699d91236feSeschrock 			goto error;
1700d91236feSeschrock 	}
1701d91236feSeschrock 
1702d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
1703d91236feSeschrock 		/*
1704d91236feSeschrock 		 * This is a request to enumerate external enclosures.  Go
1705d91236feSeschrock 		 * through all the targets and create chassis nodes where
1706d91236feSeschrock 		 * necessary.
1707d91236feSeschrock 		 */
1708d91236feSeschrock 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
1709d91236feSeschrock 		    cp = topo_list_next(cp)) {
1710d91236feSeschrock 			if (ses_create_chassis(data, rnode, cp) != 0)
1711d91236feSeschrock 				goto error;
1712d91236feSeschrock 		}
1713d91236feSeschrock 	} else {
1714d91236feSeschrock 		/*
1715d91236feSeschrock 		 * This is a request to enumerate a specific bay underneath the
1716d91236feSeschrock 		 * root chassis (for internal disks).
1717d91236feSeschrock 		 */
1718d91236feSeschrock 		if (ses_create_bays(data, rnode) != 0)
1719940d71d2Seschrock 			goto error;
1720940d71d2Seschrock 	}
1721940d71d2Seschrock 
1722d91236feSeschrock 	/*
1723d91236feSeschrock 	 * This is a bit of a kludge.  In order to allow internal disks to be
1724d91236feSeschrock 	 * enumerated and share snapshot-specific information with the external
1725d91236feSeschrock 	 * enclosure enumeration, we rely on the fact that we will be invoked
1726d91236feSeschrock 	 * for the 'ses-enclosure' node last.
1727d91236feSeschrock 	 */
1728d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
1729*53dbcc59SSundeep Panicker 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
1730*53dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
1731*53dbcc59SSundeep Panicker 			ses_data_free(data, cp);
1732*53dbcc59SSundeep Panicker 		ses_data_free(data, NULL);
1733d91236feSeschrock 		topo_mod_setspecific(mod, NULL);
1734d91236feSeschrock 	}
1735940d71d2Seschrock 	return (0);
1736940d71d2Seschrock 
1737940d71d2Seschrock error:
1738*53dbcc59SSundeep Panicker 	for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
1739*53dbcc59SSundeep Panicker 	    cp = topo_list_next(cp))
1740*53dbcc59SSundeep Panicker 		ses_data_free(data, cp);
1741*53dbcc59SSundeep Panicker 	ses_data_free(data, NULL);
1742d91236feSeschrock 	topo_mod_setspecific(mod, NULL);
1743940d71d2Seschrock 	return (-1);
1744940d71d2Seschrock }
1745940d71d2Seschrock 
1746940d71d2Seschrock static const topo_modops_t ses_ops =
1747940d71d2Seschrock 	{ ses_enum, ses_release };
1748940d71d2Seschrock 
1749940d71d2Seschrock static topo_modinfo_t ses_info =
1750940d71d2Seschrock 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
1751940d71d2Seschrock 
1752940d71d2Seschrock /*ARGSUSED*/
1753940d71d2Seschrock int
1754940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version)
1755940d71d2Seschrock {
1756940d71d2Seschrock 	if (getenv("TOPOSESDEBUG") != NULL)
1757940d71d2Seschrock 		topo_mod_setdebug(mod);
1758940d71d2Seschrock 
1759940d71d2Seschrock 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
1760940d71d2Seschrock 	    SES_ENCLOSURE);
1761940d71d2Seschrock 
1762940d71d2Seschrock 	return (topo_mod_register(mod, &ses_info, TOPO_VERSION));
1763940d71d2Seschrock }
1764940d71d2Seschrock 
1765940d71d2Seschrock void
1766940d71d2Seschrock _topo_fini(topo_mod_t *mod)
1767940d71d2Seschrock {
1768940d71d2Seschrock 	topo_mod_unregister(mod);
1769940d71d2Seschrock }
1770