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 * Walk the LDOM PRI component nodes and create appropriate topology nodes 29 */ 30 31 #include <sys/types.h> 32 #include <sys/time.h> 33 #include <stddef.h> 34 #include <inttypes.h> 35 #include <strings.h> 36 #include <string.h> 37 #include <libuutil.h> 38 #include <libnvpair.h> 39 #include <sys/mdesc.h> 40 #include <fm/topo_mod.h> 41 #include <fm/topo_hc.h> 42 #include "pi_impl.h" 43 44 #define PI_STR_MIN "instance_min" 45 #define PI_STR_MAX "instance_max" 46 47 /* 48 * Allow for custom topo node creation routines based on topo-hc-name. 49 */ 50 struct pi_enum_functions_s { 51 pi_enum_fn_t *func; 52 char *hc_name; /* topo-hc-name */ 53 }; 54 typedef struct pi_enum_functions_s pi_enum_functions_t; 55 56 struct pi_methods_s { 57 topo_method_t *meths; 58 char *hc_name; 59 }; 60 typedef struct pi_methods_s pi_methods_t; 61 62 extern topo_method_t pi_cpu_methods[], pi_mem_methods[]; 63 64 /* 65 * List of custom enumerators for PRI nodes that require them. The most 66 * common nodes are listed first. 67 */ 68 static pi_enum_functions_t pi_enum_fns_builtin[] = { 69 {pi_enum_cpu, STRAND}, 70 {pi_enum_cpu, CPU}, 71 {pi_enum_mem, DIMM}, 72 {pi_enum_cpu, CORE}, 73 {pi_enum_cpu, CHIP}, 74 {pi_enum_hostbridge, HOSTBRIDGE}, 75 {pi_enum_pciexrc, PCIEX_ROOT}, 76 {pi_enum_niu, NIU}, 77 {NULL, NULL} 78 }; 79 static nvlist_t *pi_enum_fns; 80 81 /* List of methods that will be registered in the nodes. */ 82 static pi_methods_t pi_meths_builtin[] = { 83 {pi_cpu_methods, CHIP}, 84 {pi_cpu_methods, CORE}, 85 {pi_cpu_methods, STRAND}, 86 {pi_cpu_methods, CPU}, 87 {pi_mem_methods, DIMM}, 88 {NULL, NULL} 89 }; 90 nvlist_t *pi_meths; 91 92 /* 93 * In order to create a topology node from a PRI MDE node we need to know the 94 * topology parent node that should be used. So, after creating a topology 95 * node from an MDE node, we associate children of the MDE node with the new 96 * topology node. Thus, when the children are visited we can know the 97 * appropriate parent topology node to use. 98 * 99 * We take advantage of the libtopo threading model here, which guarantees a 100 * single thread and a single invocation at a time for an enumerator. This 101 * makes using a file-global safe. 102 */ 103 static uu_list_pool_t *walker_pool = NULL; 104 static uu_list_t *walker_list = NULL; 105 106 struct pi_walkernode_s { 107 uu_list_node_t walker_node; 108 tnode_t *t_parent; /* Parent topology node */ 109 mde_cookie_t mde_node; /* Child MDE node index */ 110 }; 111 typedef struct pi_walkernode_s pi_walkernode_t; 112 113 114 /* The routine called for each node in the PRI while walking the graph */ 115 static int pi_walker_node(md_t *, mde_cookie_t, mde_cookie_t, void *); 116 117 /* 118 * Create a sub-range for a given PRI node and associate the given topology 119 * node with the children. 120 */ 121 static int pi_walker_node_range(topo_mod_t *, md_t *, tnode_t *, mde_cookie_t); 122 static int pi_walker_node_create(topo_mod_t *, md_t *, mde_cookie_t, tnode_t *, 123 topo_instance_t, tnode_t **); 124 125 /* Routines to handle the list of topology parents and mde_nodes */ 126 static int pi_walkerlist_compare(const void *, const void *, void *); 127 static int pi_walkerlist_create(topo_mod_t *); 128 static void pi_walkerlist_destroy(topo_mod_t *); 129 static int pi_walkerlist_add(topo_mod_t *, tnode_t *, mde_cookie_t); 130 static int pi_walkerlist_addtype(topo_mod_t *, nvlist_t *, char *, uint32_t, 131 uint32_t); 132 static int pi_walkerlist_find(topo_mod_t *, mde_cookie_t, tnode_t **); 133 134 135 int 136 pi_walker_init(topo_mod_t *mod) 137 { 138 int result; 139 pi_enum_functions_t *fp; 140 pi_methods_t *mp; 141 142 result = topo_mod_nvalloc(mod, &pi_enum_fns, NV_UNIQUE_NAME); 143 result |= topo_mod_nvalloc(mod, &pi_meths, NV_UNIQUE_NAME); 144 if (result != 0) { 145 topo_mod_dprintf(mod, "pi_walker_init failed\n"); 146 nvlist_free(pi_enum_fns); 147 nvlist_free(pi_meths); 148 return (-1); 149 } 150 151 /* Add the builtin functions to the list */ 152 fp = pi_enum_fns_builtin; 153 while (fp != NULL && fp->hc_name != NULL) { 154 uint64_t faddr; 155 156 faddr = (uint64_t)(uintptr_t)*(fp->func); 157 result |= nvlist_add_uint64(pi_enum_fns, fp->hc_name, faddr); 158 fp++; 159 } 160 161 /* Add the builtin methods to the list */ 162 mp = pi_meths_builtin; 163 while (mp != NULL && mp->hc_name != NULL) { 164 uint64_t maddr; 165 166 maddr = (uint64_t)(uintptr_t)mp->meths; 167 result |= nvlist_add_uint64(pi_meths, mp->hc_name, maddr); 168 mp++; 169 } 170 171 if (result != 0) { 172 topo_mod_dprintf(mod, "pi_walker_init failed\n"); 173 nvlist_free(pi_enum_fns); 174 nvlist_free(pi_meths); 175 return (-1); 176 } 177 178 return (0); 179 } 180 181 182 void 183 pi_walker_fini(topo_mod_t *mod) 184 { 185 topo_mod_dprintf(mod, "pi_walker_fini: enter\n"); 186 nvlist_free(pi_enum_fns); 187 nvlist_free(pi_meths); 188 } 189 190 191 /* 192 * Begin to walk the machine description array starting at the given PRI node. 193 */ 194 int 195 pi_walker(pi_enum_t *pip, tnode_t *t_parent, const char *hc_name, 196 mde_cookie_t mde_node, mde_str_cookie_t component_cookie, 197 mde_str_cookie_t arc_cookie) 198 { 199 int result; 200 hrtime_t starttime; 201 hrtime_t endtime; 202 topo_mod_t *mod; 203 204 if (pip == NULL) { 205 return (-1); 206 } 207 mod = pip->mod; 208 209 starttime = gethrtime(); 210 topo_mod_dprintf(mod, "walker starting at node_0x%llx\n", 211 mde_node); 212 213 /* 214 * Create a list to store topology nodes and their associated machine 215 * description index. This allows the code to know the parent of a 216 * node when creating topology entries. 217 */ 218 result = pi_walkerlist_create(mod); 219 if (result != 0) { 220 topo_mod_dprintf(mod, "walker could not create list\n"); 221 return (result); 222 } 223 224 /* Create a walker node for the parent of the start node */ 225 result = pi_walkerlist_add(mod, t_parent, mde_node); 226 if (result != 0) { 227 pi_walkerlist_destroy(mod); 228 topo_mod_dprintf(mod, "walker could not add to list\n"); 229 (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); 230 return (result); 231 } 232 233 /* 234 * This is a top-level node. Make sure we call the top level 235 * enumerator if there is not already a custom enumerator registered. 236 */ 237 if (! nvlist_exists(pi_enum_fns, hc_name)) { 238 uint64_t faddr; 239 240 /* 241 * There is no enumerator function registered for this 242 * hc name. Automatically register the top level node 243 * enumerator function. 244 */ 245 faddr = (uint64_t)(uintptr_t)pi_enum_top; 246 result = nvlist_add_uint64(pi_enum_fns, hc_name, faddr); 247 if (result != 0) { 248 pi_walkerlist_destroy(mod); 249 topo_mod_dprintf(mod, 250 "walker could not register enumerator for type " 251 "%s\n", hc_name); 252 (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); 253 return (-1); 254 } 255 topo_mod_dprintf(mod, 256 "walker registered pi_enum_top enumerator for type %s\n", 257 hc_name); 258 } 259 260 /* Walk the machine description list starting at the given node */ 261 result = md_walk_dag(pip->mdp, mde_node, component_cookie, arc_cookie, 262 pi_walker_node, (void *)pip); 263 switch (result) { 264 case 0: 265 /* Successful completion */ 266 /* DO NOTHING */ 267 break; 268 269 case MDE_WALK_ERROR: 270 /* 271 * Store that we have a partial enumeration and return 272 * that we have encountered an error. 273 */ 274 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 275 result = -1; 276 break; 277 278 default: 279 /* 280 * This should not happen. We want to always produce 281 * as complete a topology as possible, even in the face 282 * of errors, however, so set an error and continue. 283 */ 284 topo_mod_dprintf(mod, 285 "walker encountered invalid result: %d. " 286 "Continuing\n", result); 287 (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM); 288 result = 0; 289 break; 290 } 291 292 /* Destroy the walker list, which is no longer necessary */ 293 pi_walkerlist_destroy(mod); 294 295 topo_mod_dprintf(mod, "walker done with node_0x%llx\n", mde_node); 296 297 endtime = gethrtime(); 298 topo_mod_dprintf(mod, "walker scan time %lld ms\n", 299 (endtime-starttime)/MICROSEC); 300 301 return (result); 302 } 303 304 305 /* 306 * Visited once for each node in the machine description. Creates a topo 307 * node for the machine description node and associates it with it's parent, 308 * by calling an appropriate creation routine for the node type. 309 * 310 * Output: 311 * This routine returns MDE_WALK_NEXT, MDE_WALK_DONE or MDE_WALK_ERROR 312 * only. 313 */ 314 static int 315 pi_walker_node(md_t *mdp, mde_cookie_t parent_mde_node, mde_cookie_t mde_node, 316 void *private) 317 { 318 int result; 319 pi_enum_t *pip = (pi_enum_t *)private; 320 uint64_t skip; /* flag in md to skip this node */ 321 tnode_t *t_parent; /* topo parent to this md node */ 322 tnode_t *t_node; /* topo parent to this md node */ 323 topo_instance_t inst; 324 325 topo_mod_t *mod; 326 327 /* Make sure we have our private data */ 328 if (pip == NULL) { 329 return (MDE_WALK_ERROR); 330 } 331 mod = pip->mod; 332 333 topo_mod_dprintf(pip->mod, 334 "walker processing node_0x%llx parent node 0x%llx\n", 335 (uint64_t)mde_node, (uint64_t)parent_mde_node); 336 337 /* Should we skip this node ? */ 338 skip = pi_skip_node(mod, pip->mdp, mde_node); 339 if (skip) { 340 /* Skip this node and continue to the next node */ 341 topo_mod_dprintf(mod, "walker skipping node_0x%llx\n", 342 (uint64_t)mde_node); 343 return (MDE_WALK_NEXT); 344 } 345 346 result = pi_get_instance(mod, mdp, mde_node, &inst); 347 if (result != 0) { 348 /* 349 * No ID available to place this mde node in the topology so 350 * we cannot create a topology node. 351 */ 352 topo_mod_dprintf(mod, "walker skipping node_0x%llx: " 353 "no instance\n", (uint64_t)mde_node); 354 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 355 return (MDE_WALK_NEXT); 356 } 357 358 /* 359 * Find the parent topo node for this machine description node. 360 * 361 * If found, the element will also be removed from the list and the 362 * memory used to keep track of it released. We will only visit an 363 * MDE node once and so the memory is no longer needed. 364 */ 365 t_parent = NULL; 366 result = pi_walkerlist_find(mod, mde_node, &t_parent); 367 if (result != 0 || t_parent == NULL) { 368 /* 369 * No parent was found or a NULL parent encountered. We 370 * cannot create a new topology node without a parent ( 371 * even for top level nodes). We associate children of 372 * this MDE node with a NULL parent to silently skip the 373 * remainder of this MDE branch. 374 */ 375 topo_mod_dprintf(mod, "no topo parent found for node_0x%llx\n", 376 mde_node); 377 result = pi_walker_node_range(mod, mdp, NULL, mde_node); 378 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 379 380 return (result); 381 } 382 383 /* 384 * We have the mde node instance and parent information. 385 * Attempt to create a topology node for this mde node. 386 */ 387 t_node = NULL; 388 result = pi_walker_node_create(mod, mdp, mde_node, t_parent, inst, 389 &t_node); 390 if (result != MDE_WALK_NEXT || t_node == NULL) { 391 /* 392 * We have failed to create a new topology node based on 393 * the current MDE node. We set partial enumeration and 394 * return without associating the children of this MDE 395 * node with a topology parent. This will propgate the 396 * creation error down this MDE branch. 397 */ 398 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 399 return (result); 400 } 401 402 /* 403 * Associate the new topology node with any children of this mde node. 404 */ 405 result = pi_walker_node_range(mod, mdp, t_node, mde_node); 406 407 topo_mod_dprintf(mod, "walker completed node_0x%llx result = %d\n", 408 (uint64_t)mde_node, result); 409 410 return (result); 411 } 412 413 414 static int 415 pi_walker_node_create(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 416 tnode_t *t_parent, topo_instance_t inst, tnode_t **t_node) 417 { 418 int result; 419 char *hc_name; 420 uint64_t faddr; 421 pi_enum_fn_t *func; 422 423 if (t_parent == NULL) { 424 /* 425 * A parent topology node is required even for top-level 426 * nodes. 427 */ 428 return (MDE_WALK_NEXT); 429 } 430 431 /* 432 * Find the topo-hc-name for this node which is used to find 433 * the specific creation function 434 */ 435 hc_name = pi_get_topo_hc_name(mod, mdp, mde_node); 436 if (hc_name == NULL) { 437 /* Cannot get the hc-name */ 438 topo_mod_dprintf(mod, 439 "failed to find hc-name for node_0x%llx\n", mde_node); 440 return (MDE_WALK_NEXT); 441 } 442 443 /* Determine the topology node creation routine to use */ 444 func = pi_enum_generic; 445 faddr = 0; 446 result = nvlist_lookup_uint64(pi_enum_fns, hc_name, &faddr); 447 if (result == 0) { 448 /* 449 * A function is registered for this node. Convert the 450 * address to a pointer to function 451 */ 452 func = (pi_enum_fn_t *)(uintptr_t)faddr; 453 } 454 455 /* 456 * Create a topology node for this mde node by calling the identified 457 * enumeration function 458 */ 459 *t_node = NULL; 460 result = (func)(mod, mdp, mde_node, inst, t_parent, hc_name, t_node); 461 if (result != 0) { 462 topo_mod_dprintf(mod, 463 "failed to create topo entry for node_0x%llx type %s\n", 464 (uint64_t)mde_node, hc_name); 465 } 466 467 topo_mod_strfree(mod, hc_name); 468 469 return (MDE_WALK_NEXT); 470 } 471 472 473 /* 474 * Scan the children of a given MDE node and find all the sets of topo-hc-name 475 * types and their instance ranges. From this information we create topology 476 * node ranges on the given parent so that when the children are visited and a 477 * topology node is created, the range exists and the creation will succeed. 478 */ 479 static int 480 pi_walker_node_range(topo_mod_t *mod, md_t *mdp, tnode_t *t_parent, 481 mde_cookie_t mde_node) 482 { 483 int result; 484 int rc; 485 int num_arcs; 486 nvlist_t *typelist; 487 nvpair_t *nvp; 488 mde_cookie_t *arcp; 489 size_t arcsize; 490 int arcidx; 491 char *hc_name; 492 nvlist_t *hc_range; 493 topo_instance_t inst; 494 uint32_t min; 495 uint32_t max; 496 497 if (t_parent == NULL) { 498 topo_mod_dprintf(mod, 499 "walker failed to create node range with a NULL parent\n"); 500 return (MDE_WALK_NEXT); 501 } 502 503 /* Determine how many children the given node has */ 504 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, NULL, 0); 505 if (num_arcs == 0) { 506 /* This node has no children */ 507 return (MDE_WALK_NEXT); 508 } 509 topo_mod_dprintf(mod, "node_0x%llx has %d children\n", 510 (uint64_t)mde_node, num_arcs); 511 512 /* Get the indexes for all the child nodes and put them in an array */ 513 arcsize = sizeof (mde_cookie_t) * num_arcs; 514 arcp = topo_mod_zalloc(mod, arcsize); 515 if (arcp == NULL) { 516 topo_mod_dprintf(mod, "out of memory\n"); 517 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 518 return (MDE_WALK_ERROR); 519 } 520 num_arcs = md_get_prop_arcs(mdp, mde_node, MD_STR_FWD, arcp, arcsize); 521 522 /* 523 * The children of the given node may have multiple types. 524 * Potentially, each child may have a different type and we need to 525 * create a topo node range for each one. 526 * 527 * We loop through the children and collect the type information for 528 * each one and associate the child with the given parent topo node. 529 */ 530 result = topo_mod_nvalloc(mod, &typelist, NV_UNIQUE_NAME); 531 if (result != 0) { 532 topo_mod_free(mod, arcp, arcsize); 533 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 534 return (MDE_WALK_ERROR); 535 } 536 537 arcidx = 0; 538 for (arcidx = 0; arcidx < num_arcs; arcidx++) { 539 /* Should this node be skipped? */ 540 if (pi_skip_node(mod, mdp, arcp[arcidx])) { 541 /* Skip this node */ 542 topo_mod_dprintf(mod, "skipping node_0x%llx\n", 543 (uint64_t)arcp[arcidx]); 544 continue; 545 } 546 547 /* Get the type of this node */ 548 hc_name = pi_get_topo_hc_name(mod, mdp, arcp[arcidx]); 549 rc = pi_get_instance(mod, mdp, arcp[arcidx], &inst); 550 if (rc == 0 && hc_name != NULL) { 551 /* Increment the count of nodes with this type */ 552 hc_range = NULL; 553 rc = nvlist_lookup_nvlist(typelist, hc_name, &hc_range); 554 if (rc != 0) { 555 /* 556 * We have not visited this type yet. Create 557 * a new range based on this nodes instance 558 * information. 559 */ 560 result = pi_walkerlist_addtype(mod, typelist, 561 hc_name, (uint32_t)inst, (uint32_t)inst); 562 if (result != 0) { 563 /* 564 * This error can only if there was a 565 * memory failure of some kind. Stop 566 * the walk or it will just get worse. 567 */ 568 nvlist_free(typelist); 569 topo_mod_strfree(mod, hc_name); 570 topo_mod_free(mod, arcp, arcsize); 571 (void) topo_mod_seterrno(mod, 572 EMOD_PARTIAL_ENUM); 573 return (MDE_WALK_ERROR); 574 } 575 576 /* 577 * We know the list exists now or the above 578 * would have failed. Just look it up. 579 */ 580 (void) nvlist_lookup_nvlist(typelist, hc_name, 581 &hc_range); 582 } 583 584 /* Re-calculate the range minimums and maximums */ 585 (void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min); 586 (void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max); 587 min = MIN(min, (uint32_t)inst); 588 max = MAX(max, (uint32_t)inst); 589 (void) nvlist_add_uint32(hc_range, PI_STR_MIN, min); 590 (void) nvlist_add_uint32(hc_range, PI_STR_MAX, max); 591 592 } else { 593 topo_mod_dprintf(mod, "node_0x%llx type %s has no id. " 594 "Excluding from range", (uint64_t)arcp[arcidx], 595 hc_name); 596 } 597 topo_mod_strfree(mod, hc_name); 598 599 /* 600 * Associate this node with the given topo parent even if it 601 * has no instance. We do this so that later an error with 602 * the PRI node will be reported instead of an internal 603 * error about not being able to find the parent of a node 604 */ 605 rc = pi_walkerlist_add(mod, t_parent, arcp[arcidx]); 606 if (rc != 0) { 607 topo_mod_dprintf(mod, 608 "could not add node_0x%llx to walker list\n", 609 (uint64_t)arcp[arcidx]); 610 } 611 } 612 613 /* 614 * We have associated all the child nodes with the given topo parent 615 * in the walker list. Now we need to create topo ranges for each 616 * set of child types under the parent. 617 */ 618 nvp = nvlist_next_nvpair(typelist, NULL); 619 while (nvp != NULL) { 620 /* Get the type name and count from the list element */ 621 hc_name = nvpair_name(nvp); 622 (void) nvpair_value_nvlist(nvp, &hc_range); 623 (void) nvlist_lookup_uint32(hc_range, PI_STR_MIN, &min); 624 (void) nvlist_lookup_uint32(hc_range, PI_STR_MAX, &max); 625 626 /* 627 * We have the number of children with this type. 628 * Create an appropriate range. 629 */ 630 topo_mod_dprintf(mod, 631 "creating instance range %d to %d of type %s\n", 632 min, max, hc_name); 633 rc = topo_node_range_create(mod, t_parent, hc_name, 634 (topo_instance_t)min, (topo_instance_t)max); 635 if (rc != 0) { 636 topo_mod_dprintf(mod, 637 "failed to created node range %d to %d for " 638 "nodes of type %s\n", min, max, hc_name); 639 } 640 641 /* Check the next node */ 642 nvp = nvlist_next_nvpair(typelist, nvp); 643 } 644 topo_mod_free(mod, arcp, arcsize); 645 nvlist_free(typelist); 646 647 return (MDE_WALK_NEXT); 648 } 649 650 651 static int 652 pi_walkerlist_addtype(topo_mod_t *mod, nvlist_t *typelist, char *hc_name, 653 uint32_t min, uint32_t max) 654 { 655 int result; 656 nvlist_t *nvl; 657 658 result = topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME); 659 if (result != 0) { 660 return (result); 661 } 662 663 /* Create min and max elements in this list */ 664 if (nvlist_add_uint32(nvl, PI_STR_MIN, min) != 0 || 665 nvlist_add_uint32(nvl, PI_STR_MAX, max) != 0 || 666 nvlist_add_nvlist(typelist, hc_name, nvl) != 0) { 667 nvlist_free(nvl); 668 return (-1); 669 } 670 nvlist_free(nvl); 671 672 return (0); 673 } 674 675 676 /* ARGSUSED */ 677 static int 678 pi_walkerlist_compare(const void *left, const void *right, void *private) 679 { 680 pi_walkernode_t *lp = (pi_walkernode_t *)left; 681 pi_walkernode_t *rp = (pi_walkernode_t *)right; 682 683 if (lp->mde_node > rp->mde_node) { 684 return (1); 685 } 686 if (lp->mde_node < rp->mde_node) { 687 return (-1); 688 } 689 return (0); 690 } 691 692 693 static int 694 pi_walkerlist_create(topo_mod_t *mod) 695 { 696 /* Initialize the uutil list structure */ 697 walker_pool = uu_list_pool_create("pi_walker_pool", 698 sizeof (pi_walkernode_t), offsetof(pi_walkernode_t, walker_node), 699 pi_walkerlist_compare, NULL); 700 if (walker_pool == NULL) { 701 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 702 return (-1); 703 } 704 walker_list = uu_list_create(walker_pool, NULL, 0); 705 if (walker_list == NULL) { 706 uu_list_pool_destroy(walker_pool); 707 walker_pool = NULL; 708 return (-1); 709 } 710 711 return (0); 712 } 713 714 715 static void 716 pi_walkerlist_destroy(topo_mod_t *mod) 717 { 718 void *wvp; 719 pi_walkernode_t *wp; 720 721 /* Destroy our list of items */ 722 while ((wvp = uu_list_first(walker_list)) != NULL) { 723 /* 724 * First, we empty the list of elements and free each one. 725 * We do not free the data elements as they are libtopo nodes 726 * and will be freed by libtopo 727 */ 728 wp = (pi_walkernode_t *)wvp; 729 uu_list_remove(walker_list, wvp); 730 uu_list_node_fini(wp, &(wp->walker_node), walker_pool); 731 732 topo_mod_free(mod, wvp, sizeof (pi_walkernode_t)); 733 } 734 uu_list_destroy(walker_list); 735 uu_list_pool_destroy(walker_pool); 736 walker_list = NULL; 737 walker_pool = NULL; 738 } 739 740 741 static int 742 pi_walkerlist_add(topo_mod_t *mod, tnode_t *t_parent, mde_cookie_t mde_node) 743 { 744 uu_list_index_t idx; 745 pi_walkernode_t *wnp; 746 747 wnp = topo_mod_zalloc(mod, sizeof (pi_walkernode_t)); 748 if (wnp == NULL) { 749 topo_mod_dprintf(mod, "failed to add node_0x%llx parent %p\n", 750 (uint64_t)mde_node, t_parent); 751 return (-1); 752 } 753 uu_list_node_init(wnp, &(wnp->walker_node), walker_pool); 754 755 wnp->t_parent = t_parent; 756 wnp->mde_node = mde_node; 757 758 (void) uu_list_find(walker_list, wnp, NULL, &idx); 759 uu_list_insert(walker_list, wnp, idx); 760 761 return (0); 762 } 763 764 765 /* 766 * Find the parent topo node for this machine description node. 767 * 768 * Nodes are removed from the list as they are found. They are only 769 * visited once and this eliminates the need for a separate routine 770 * that walks the list to free elements later. 771 */ 772 static int 773 pi_walkerlist_find(topo_mod_t *mod, mde_cookie_t mde_node, tnode_t **tpp) 774 { 775 pi_walkernode_t *result; 776 777 uu_list_index_t idx; 778 pi_walkernode_t search_criteria; 779 780 search_criteria.mde_node = mde_node; 781 search_criteria.t_parent = NULL; 782 783 *tpp = NULL; 784 result = uu_list_find(walker_list, &search_criteria, NULL, &idx); 785 if (result == NULL) { 786 return (-1); 787 } 788 *tpp = result->t_parent; 789 790 /* Remove this element from the list */ 791 uu_list_remove(walker_list, result); 792 uu_list_node_fini(result, &(result->walker_node), walker_pool); 793 topo_mod_free(mod, result, sizeof (pi_walkernode_t)); 794 795 return (0); 796 } 797