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