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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Topology Nodes 31 * 32 * Topology nodes, tnode_t, are data structures containing per-FMRI 33 * information and are linked together to form the topology tree. 34 * Nodes are created during the enumeration process of topo_snap_hold() 35 * and destroyed during topo_snap_rele(). For the most part, tnode_t data 36 * is read-only and no lock protection is required. Nodes are 37 * held in place during tree walk functions. Tree walk functions 38 * may access node data safely without locks. The exception to this rule 39 * is data associated with node properties (topo_prop.c). Properties 40 * may change at anytime and are protected by a per-property locking 41 * strategy. 42 * 43 * Enumerator plugin modules may also safely access node data. Enumeration 44 * occurs only during topo_snap_hold() where a per-topo_hdl_t lock prevents 45 * multi-threaded access to the topology trees. 46 * 47 * Like tree walking functions, method plugin modules have access to read-only 48 * node data but may make changes to property information. 49 * 50 * Node Interfaces 51 * 52 * Nodes are created when an enumerator calls topo_node_bind(). Prior to the 53 * call to topo_node_bind(), the caller should have reserved a range of 54 * node instances with topo_node_range_create(). topo_node_range_create() 55 * does not allocate any node resources but creates the infrastruture 56 * required for a fully populated topology level. This allows enumerators 57 * reading from a <scheme>-topology.xml file to parse the file for a range 58 * of resources before confirming the existence of a resource via a helper 59 * plugin. Only when the resource has been confirmed to exist should 60 * the node be bound. 61 * 62 * Node range and node linkage is only performed during enumeration when it 63 * is safe to change node hash lists and next pointers. Nodes and node ranges 64 * are deallocated when all references to the node have been released: 65 * last walk completes and topo_snap_rele() is called. 66 * 67 * Node Hash/Ranges 68 * 69 * Each parent node may have one or more ranges of child nodes. Each range 70 * serves as a hash list of like sibling nodes all with the same name but 71 * different instance numbers. A parent may have more than one node hash 72 * (child range). If that is the case, the hash lists are strung together to 73 * form sibling relationships between ranges. Hash/Ranges are sparsely 74 * populated with only nodes that have represented resources in the system. 75 */ 76 77 #include <assert.h> 78 #include <pthread.h> 79 #include <strings.h> 80 #include <topo_alloc.h> 81 #include <topo_tree.h> 82 #include <topo_subr.h> 83 #include <topo_error.h> 84 85 static void 86 topo_node_destroy(tnode_t *node) 87 { 88 int i; 89 tnode_t *pnode = node->tn_parent; 90 topo_nodehash_t *nhp; 91 topo_mod_t *hmod, *mod = node->tn_enum; 92 93 if (node == NULL) 94 return; 95 96 assert(node->tn_refs == 0); 97 98 topo_dprintf(TOPO_DBG_TREE, "destroying node %s=%d\n", node->tn_name, 99 node->tn_instance); 100 /* 101 * If not a root node, remove this node from the parent's node hash 102 */ 103 104 if (!(node->tn_state & TOPO_NODE_ROOT)) { 105 topo_node_lock(pnode); 106 107 nhp = node->tn_phash; 108 for (i = 0; i < nhp->th_arrlen; i++) { 109 if (node == nhp->th_nodearr[i]) { 110 nhp->th_nodearr[i] = NULL; 111 112 /* 113 * Release hold on parent 114 */ 115 --pnode->tn_refs; 116 if (pnode->tn_refs == 0) 117 topo_node_destroy(pnode); 118 } 119 } 120 topo_node_unlock(pnode); 121 } 122 123 topo_node_unlock(node); 124 125 /* 126 * Allow enumerator to clean-up private data and then release 127 * ref count 128 */ 129 if (mod->tm_info->tmi_release != NULL) 130 mod->tm_info->tmi_release(mod, node); 131 132 topo_method_unregister_all(mod, node); 133 134 /* 135 * Destroy all node hash lists 136 */ 137 while ((nhp = topo_list_next(&node->tn_children)) != NULL) { 138 for (i = 0; i < nhp->th_arrlen; i++) { 139 assert(nhp->th_nodearr[i] == NULL); 140 } 141 hmod = nhp->th_enum; 142 topo_mod_strfree(hmod, nhp->th_name); 143 topo_mod_free(hmod, nhp->th_nodearr, 144 nhp->th_arrlen * sizeof (tnode_t *)); 145 topo_list_delete(&node->tn_children, nhp); 146 topo_mod_free(hmod, nhp, sizeof (topo_nodehash_t)); 147 topo_mod_rele(hmod); 148 } 149 150 /* 151 * Destroy all property data structures, free the node and release 152 * the module that created it 153 */ 154 topo_pgroup_destroy_all(node); 155 topo_mod_free(mod, node, sizeof (tnode_t)); 156 topo_mod_rele(mod); 157 } 158 159 void 160 topo_node_lock(tnode_t *node) 161 { 162 (void) pthread_mutex_lock(&node->tn_lock); 163 } 164 165 void 166 topo_node_unlock(tnode_t *node) 167 { 168 (void) pthread_mutex_unlock(&node->tn_lock); 169 } 170 171 void 172 topo_node_hold(tnode_t *node) 173 { 174 topo_node_lock(node); 175 ++node->tn_refs; 176 topo_node_unlock(node); 177 } 178 179 void 180 topo_node_rele(tnode_t *node) 181 { 182 topo_node_lock(node); 183 --node->tn_refs; 184 185 /* 186 * Ok to remove this node from the topo tree and destroy it 187 */ 188 if (node->tn_refs == 0) 189 topo_node_destroy(node); 190 else 191 topo_node_unlock(node); 192 } 193 194 char * 195 topo_node_name(tnode_t *node) 196 { 197 return (node->tn_name); 198 } 199 200 topo_instance_t 201 topo_node_instance(tnode_t *node) 202 { 203 return (node->tn_instance); 204 } 205 206 void * 207 topo_node_private(tnode_t *node) 208 { 209 return (node->tn_priv); 210 } 211 212 static int 213 node_create_seterror(topo_mod_t *mod, tnode_t *pnode, topo_nodehash_t *nhp, 214 int err) 215 { 216 topo_node_unlock(pnode); 217 218 topo_dprintf(TOPO_DBG_ERR, "unable to insert child:" 219 "%s\n", topo_strerror(err)); 220 221 if (nhp != NULL) { 222 if (nhp->th_name != NULL) 223 topo_mod_strfree(mod, nhp->th_name); 224 if (nhp->th_nodearr != NULL) { 225 topo_mod_free(mod, nhp->th_nodearr, 226 nhp->th_arrlen * sizeof (tnode_t *)); 227 } 228 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t)); 229 } 230 231 return (topo_mod_seterrno(mod, err)); 232 } 233 234 int 235 topo_node_range_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 236 topo_instance_t min, topo_instance_t max) 237 { 238 topo_nodehash_t *nhp; 239 240 topo_node_lock(pnode); 241 242 assert((pnode->tn_state & TOPO_NODE_BOUND) || 243 (pnode->tn_state & TOPO_NODE_ROOT)); 244 245 for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL; 246 nhp = topo_list_next(nhp)) { 247 if (strcmp(nhp->th_name, name) == 0) 248 return (node_create_seterror(mod, pnode, NULL, 249 ETOPO_NODE_DUP)); 250 } 251 252 if (min < 0 || max < min) 253 return (node_create_seterror(mod, pnode, NULL, 254 ETOPO_NODE_INVAL)); 255 256 if ((nhp = topo_mod_zalloc(mod, sizeof (topo_nodehash_t))) == NULL) 257 return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM)); 258 259 if ((nhp->th_name = topo_mod_strdup(mod, name)) == NULL) 260 return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM)); 261 262 nhp->th_arrlen = max - min + 1; 263 264 if ((nhp->th_nodearr = topo_mod_zalloc(mod, 265 nhp->th_arrlen * sizeof (tnode_t *))) == NULL) 266 return (node_create_seterror(mod, pnode, nhp, ETOPO_NOMEM)); 267 268 nhp->th_range.tr_min = min; 269 nhp->th_range.tr_max = max; 270 nhp->th_enum = mod; 271 topo_mod_hold(mod); 272 273 /* 274 * Add these nodes to parent child list 275 */ 276 topo_list_append(&pnode->tn_children, nhp); 277 topo_node_unlock(pnode); 278 279 topo_dprintf(TOPO_DBG_MOD, "created node range %s[%d-%d]\n", name, 280 min, max); 281 282 return (0); 283 } 284 285 void 286 topo_node_range_destroy(tnode_t *pnode, const char *name) 287 { 288 int i; 289 topo_nodehash_t *nhp; 290 topo_mod_t *mod; 291 292 topo_node_lock(pnode); 293 for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL; 294 nhp = topo_list_next(nhp)) { 295 if (strcmp(nhp->th_name, name) == 0) { 296 break; 297 } 298 } 299 300 if (nhp == NULL) { 301 topo_node_unlock(pnode); 302 return; 303 } 304 305 topo_list_delete(&pnode->tn_children, nhp); 306 topo_node_unlock(pnode); 307 308 /* 309 * Should be an empty node range 310 */ 311 for (i = 0; i < nhp->th_arrlen; i++) { 312 topo_node_unbind(nhp->th_nodearr[i]); 313 } 314 315 mod = nhp->th_enum; 316 if (nhp->th_name != NULL) 317 topo_mod_strfree(mod, nhp->th_name); 318 if (nhp->th_nodearr != NULL) { 319 topo_mod_free(mod, nhp->th_nodearr, 320 nhp->th_arrlen * sizeof (tnode_t *)); 321 } 322 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t)); 323 topo_mod_rele(mod); 324 325 } 326 327 tnode_t * 328 topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst) 329 { 330 int h; 331 tnode_t *node; 332 topo_nodehash_t *nhp; 333 334 topo_node_lock(pnode); 335 for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL; 336 nhp = topo_list_next(nhp)) { 337 if (strcmp(nhp->th_name, name) == 0) { 338 339 if (inst > nhp->th_range.tr_max || 340 inst < nhp->th_range.tr_min) { 341 topo_node_unlock(pnode); 342 return (NULL); 343 } 344 345 h = topo_node_hash(nhp, inst); 346 node = nhp->th_nodearr[h]; 347 topo_node_unlock(pnode); 348 return (node); 349 } 350 } 351 topo_node_unlock(pnode); 352 353 return (NULL); 354 } 355 356 int 357 topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst) 358 { 359 return (nhp->th_range.tr_max == 0 ? 360 nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1)); 361 } 362 363 static tnode_t * 364 node_bind_seterror(topo_mod_t *mod, tnode_t *pnode, tnode_t *node, int err) 365 { 366 topo_node_unlock(pnode); 367 368 (void) topo_mod_seterrno(mod, err); 369 370 if (node == NULL) 371 return (NULL); 372 373 topo_dprintf(TOPO_DBG_ERR, "unable to bind %s=%d: " 374 "%s\n", (node->tn_name != NULL ? node->tn_name : "unknown"), 375 node->tn_instance, topo_strerror(err)); 376 377 topo_node_lock(node); /* expected to be locked */ 378 topo_node_destroy(node); 379 380 return (NULL); 381 } 382 383 tnode_t * 384 topo_node_bind(topo_mod_t *mod, tnode_t *pnode, const char *name, 385 topo_instance_t inst, nvlist_t *fmri, void *priv) 386 { 387 int h, err; 388 tnode_t *node; 389 topo_nodehash_t *nhp; 390 391 topo_node_lock(pnode); 392 for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL; 393 nhp = topo_list_next(nhp)) { 394 if (strcmp(nhp->th_name, name) == 0) { 395 396 if (inst > nhp->th_range.tr_max || 397 inst < nhp->th_range.tr_min) 398 return (node_bind_seterror(mod, pnode, NULL, 399 ETOPO_NODE_INVAL)); 400 401 h = topo_node_hash(nhp, inst); 402 if (nhp->th_nodearr[h] != NULL) 403 return (node_bind_seterror(mod, pnode, NULL, 404 ETOPO_NODE_BOUND)); 405 else 406 break; 407 408 } 409 } 410 411 if (nhp == NULL) 412 return (node_bind_seterror(mod, pnode, NULL, ETOPO_NODE_NOENT)); 413 414 if ((node = topo_mod_zalloc(mod, sizeof (tnode_t))) == NULL) 415 return (node_bind_seterror(mod, pnode, NULL, ETOPO_NOMEM)); 416 417 (void) pthread_mutex_init(&node->tn_lock, NULL); 418 419 node->tn_enum = mod; 420 node->tn_hdl = mod->tm_hdl; 421 node->tn_parent = pnode; 422 node->tn_name = nhp->th_name; 423 node->tn_instance = inst; 424 node->tn_phash = nhp; 425 node->tn_refs = 0; 426 427 /* Ref count module that bound this node */ 428 topo_mod_hold(mod); 429 430 if (fmri == NULL) 431 return (node_bind_seterror(mod, pnode, node, ETOPO_NODE_INVAL)); 432 433 if (topo_pgroup_create(node, TOPO_PGROUP_PROTOCOL, 434 TOPO_STABILITY_PRIVATE, &err) < 0) 435 return (node_bind_seterror(mod, pnode, node, err)); 436 437 if (topo_prop_set_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE, 438 TOPO_PROP_SET_ONCE, fmri, &err) < 0) 439 return (node_bind_seterror(mod, pnode, node, err)); 440 441 topo_dprintf(TOPO_DBG_MOD, "node bound %s=%d\n", node->tn_name, 442 node->tn_instance); 443 444 node->tn_state |= TOPO_NODE_BOUND; 445 node->tn_priv = priv; 446 447 topo_node_hold(node); 448 nhp->th_nodearr[h] = node; 449 ++pnode->tn_refs; 450 topo_node_unlock(pnode); 451 452 if (topo_pgroup_create(node, TOPO_PGROUP_SYSTEM, 453 TOPO_STABILITY_PRIVATE, &err) == 0) { 454 (void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM, 455 TOPO_PROP_PLATFORM, &err); 456 (void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM, 457 TOPO_PROP_ISA, &err); 458 (void) topo_prop_inherit(node, TOPO_PGROUP_SYSTEM, 459 TOPO_PROP_MACHINE, &err); 460 } 461 462 return (node); 463 } 464 465 void 466 topo_node_unbind(tnode_t *node) 467 { 468 if (node == NULL) 469 return; 470 471 topo_node_lock(node); 472 if (!(node->tn_state & TOPO_NODE_BOUND)) { 473 topo_node_unlock(node); 474 return; 475 } 476 477 node->tn_state &= ~TOPO_NODE_BOUND; 478 topo_node_unlock(node); 479 480 topo_node_rele(node); 481 } 482 483 /*ARGSUSED*/ 484 int 485 topo_node_present(tnode_t *node) 486 { 487 return (0); 488 } 489 490 /*ARGSUSED*/ 491 int 492 topo_node_contains(tnode_t *er, tnode_t *ee) 493 { 494 return (0); 495 } 496 497 /*ARGSUSED*/ 498 int 499 topo_node_unusable(tnode_t *node) 500 { 501 return (0); 502 } 503