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
_topo_init(topo_mod_t * mod,topo_version_t version)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
_topo_fini(topo_mod_t * mod)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
pi_enum(topo_mod_t * mod,tnode_t * t_parent,const char * name,topo_instance_t min,topo_instance_t max,void * pi_private,void * data)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
pi_enum_components(pi_enum_t * pip,tnode_t * t_parent,const char * hc_name,mde_cookie_t mde_node,mde_str_cookie_t component_cookie,mde_str_cookie_t arc_cookie)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