xref: /titanic_52/usr/src/lib/fm/topo/modules/sun4v/sun4vpi/sun4vpi.c (revision 186d582bd9dbcd38e0aeea49036d47d3426a3536)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Main entry points for SUN4V Platform Independent topology enumerator
29  */
30 #include <sys/types.h>
31 #include <strings.h>
32 #include <fm/topo_mod.h>
33 #include <fm/topo_hc.h>
34 #include <sys/systeminfo.h>
35 #include <pi_impl.h>
36 
37 /*
38  * Entry point called by libtopo when enumeration is required
39  */
40 static topo_enum_f	pi_enum;	/* libtopo enumeration entry point */
41 
42 
43 /*
44  * Declare the operations vector and information structure used during
45  * module registration
46  */
47 static topo_modops_t	pi_ops	= {pi_enum, NULL};
48 static topo_modinfo_t	pi_modinfo	= {
49     SUN4VPI_DESC, SUN4VPI_SCHEME, SUN4VPI_VERSION, &pi_ops
50 };
51 
52 static int pi_enum_components(pi_enum_t *, tnode_t *, const char *,
53     mde_cookie_t, mde_str_cookie_t, mde_str_cookie_t);
54 
55 
56 /*
57  * Called by libtopo when the topo module is loaded.
58  */
59 void
60 _topo_init(topo_mod_t *mod, topo_version_t version)
61 {
62 	int		result;
63 	char		isa[MAXNAMELEN];
64 
65 	if (getenv("TOPOSUN4VPIDBG") != NULL) {
66 		/* Debugging is requested for this module */
67 		topo_mod_setdebug(mod);
68 	}
69 	topo_mod_dprintf(mod, "sun4vpi module initializing.\n");
70 
71 	if (version != TOPO_VERSION) {
72 		(void) topo_mod_seterrno(mod, EMOD_VER_NEW);
73 		topo_mod_dprintf(mod, "incompatible topo version %d\n",
74 		    version);
75 		return;
76 	}
77 
78 	/* Verify that this is a SUN4V architecture machine */
79 	(void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
80 	if (strncmp(isa, "sun4v", MAXNAMELEN) != 0) {
81 		topo_mod_dprintf(mod, "not sun4v architecture: %s\n", isa);
82 		return;
83 	}
84 
85 	result = topo_mod_register(mod, &pi_modinfo, TOPO_VERSION);
86 	if (result < 0) {
87 		topo_mod_dprintf(mod, "registration failed: %s\n",
88 		    topo_mod_errmsg(mod));
89 
90 		/* module errno already set */
91 		return;
92 	}
93 	topo_mod_dprintf(mod, "module ready.\n");
94 }
95 
96 
97 /*
98  * Clean up any data used by the module before it is unloaded.
99  */
100 void
101 _topo_fini(topo_mod_t *mod)
102 {
103 	topo_mod_dprintf(mod, "module finishing.\n");
104 
105 	/* Unregister from libtopo */
106 	topo_mod_unregister(mod);
107 }
108 
109 
110 /*
111  * Enumeration entry point for the SUN4V topology enumerator
112  */
113 /* ARGSUSED */
114 static int
115 pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name,
116     topo_instance_t min, topo_instance_t max, void *pi_private, void *data)
117 {
118 	int		result;
119 	int		idx;
120 	int		num_components;
121 	size_t		csize;
122 	hrtime_t	starttime;
123 
124 	pi_enum_t	pi;
125 
126 	mde_cookie_t	*components;
127 	mde_str_cookie_t arc_cookie;
128 	mde_str_cookie_t component_cookie;
129 
130 	/* Begin enumeration */
131 	starttime = gethrtime();
132 	topo_mod_dprintf(mod, "enumeration starting.\n");
133 
134 	/* Initialize the walker */
135 	result = pi_walker_init(mod);
136 	if (result != 0) {
137 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
138 		return (-1);
139 	}
140 
141 	/* Open a connection to the LDOM PRI */
142 	bzero(&pi, sizeof (pi_enum_t));
143 	result = pi_ldompri_open(mod, &pi);
144 	if (result != 0) {
145 		pi_walker_fini(mod);
146 		topo_mod_dprintf(mod, "could not open LDOM PRI\n");
147 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
148 		return (-1);
149 	}
150 	pi.mod = mod;
151 
152 	/*
153 	 * Find the top of the components graph in the PRI using the machine
154 	 * description library.
155 	 */
156 	num_components = pi_find_mdenodes(mod, pi.mdp, MDE_INVAL_ELEM_COOKIE,
157 	    MD_STR_COMPONENTS, MD_STR_FWD, &components, &csize);
158 	if (num_components < 0 || components == NULL) {
159 		/* No nodes were found */
160 		pi_walker_fini(mod);
161 		topo_mod_dprintf(mod, "could not find components in PRI\n");
162 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
163 		return (-1);
164 	}
165 
166 	/*
167 	 * There should be a single components node.  But scan all of the
168 	 * results just in case a future machine has multiple hierarchies
169 	 * for some unknown reason.
170 	 *
171 	 * We continue to walk components nodes until they are all exhausted
172 	 * and do not stop if a node cannot be enumerated.  Instead, we
173 	 * enumerate what we can and return a partial-enumeration error if
174 	 * there is a problem.
175 	 */
176 	topo_mod_dprintf(mod, "enumerating %d components hierarchies\n",
177 	    num_components);
178 
179 	component_cookie = md_find_name(pi.mdp, MD_STR_COMPONENT);
180 	arc_cookie	 = md_find_name(pi.mdp, MD_STR_FWD);
181 	result = 0;
182 	for (idx = 0; idx < num_components; idx++) {
183 		int	skip;
184 
185 		/*
186 		 * We have found a component hierarchy to process.  First,
187 		 * make sure we are not supposed to skip the graph.
188 		 */
189 		skip = pi_skip_node(mod, pi.mdp, components[idx]);
190 		if (skip == 0) {
191 			/*
192 			 * We have found a components node.  Find the top-
193 			 * level nodes and create a topology tree from them.
194 			 */
195 			result = pi_enum_components(&pi, t_parent, name,
196 			    components[idx], component_cookie, arc_cookie);
197 		}
198 	}
199 	topo_mod_free(mod, components, csize);
200 
201 	/* Close our connection to the PRI */
202 	pi_ldompri_close(mod, &pi);
203 
204 	/* Clean up after the walker */
205 	pi_walker_fini(mod);
206 
207 	/* Complete enumeration */
208 	topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n",
209 	    ((gethrtime() - starttime)/MICROSEC));
210 
211 	/* All done */
212 	return (result);
213 }
214 
215 
216 /*
217  * This routined is called once for each mde node of type 'components'.  It
218  * initiates enumeration of the graph starting with with this node.
219  */
220 static int
221 pi_enum_components(pi_enum_t *pip, tnode_t *t_parent, const char *hc_name,
222     mde_cookie_t mde_node, mde_str_cookie_t component_cookie,
223     mde_str_cookie_t arc_cookie)
224 {
225 	int		result;
226 
227 	int		num_arcs;
228 	mde_cookie_t	*arcp;
229 	size_t		arcsize;
230 	int		arcidx;
231 
232 	topo_mod_t	*mod = pip->mod;
233 	md_t		*mdp = pip->mdp;
234 
235 	if (t_parent == NULL) {
236 		topo_mod_dprintf(mod,
237 		    "walker failed to create node range with a NULL parent\n");
238 		(void) topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
239 		return (-1);
240 	}
241 
242 	/* Determine how many children the given node has */
243 	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, NULL, 0);
244 	if (num_arcs == 0) {
245 		/*
246 		 * This components node has no children and is not a topo
247 		 * node itself, so set partial enumeration and return.
248 		 */
249 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
250 		return (0);
251 	}
252 	topo_mod_dprintf(mod, "node_0x%llx has %d children\n",
253 	    (uint64_t)mde_node, num_arcs);
254 
255 	/* Get the indexes for all the child nodes and put them in an array */
256 	arcsize = sizeof (mde_cookie_t) * num_arcs;
257 	arcp = topo_mod_zalloc(mod, arcsize);
258 	if (arcp == NULL) {
259 		topo_mod_dprintf(mod, "out of memory\n");
260 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
261 		return (-1);
262 	}
263 	num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, arcp,
264 	    arcsize);
265 
266 	result = 0;
267 	for (arcidx = 0; arcidx < num_arcs; arcidx++) {
268 		/*
269 		 * Initiate walking the PRI graph starting with the current
270 		 * child of the components node.
271 		 */
272 		result = pi_walker(pip, t_parent, hc_name,
273 		    arcp[arcidx], component_cookie, arc_cookie);
274 		if (result != 0) {
275 			(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
276 		}
277 	}
278 	topo_mod_free(mod, arcp, arcsize);
279 
280 	/*
281 	 * We have now walked the entire PRI graph.  Execute any deferred
282 	 * enumeration routines that need all the nodes to be available.
283 	 */
284 	result = pi_defer_exec(mod, mdp);
285 
286 	return (result);
287 }
288