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