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 2007 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 * Snapshot Library Interfaces 30 * 31 * Consumers of topology data may use the interfaces in this file to open, 32 * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu) 33 * builtin plugins and their helper modules. A topology handle is obtained 34 * by calling topo_open(). Upon a successful return, the caller may use this 35 * handle to open a new snapshot. Each snapshot is assigned a Universally 36 * Unique Identifier that in a future enchancement to the libtopo API will be 37 * used as the file locator in /var/fm/topo to persist new snapshots or lookup 38 * a previously captured snapshot. topo_snap_hold() will capture the current 39 * system topology. All consumers of the topo_hdl_t argument will be 40 * blocked from accessing the topology trees until the snapshot completes. 41 * 42 * A snapshot may be cleared by calling topo_snap_rele(). As with 43 * topo_snap_hold(), all topology accesses are blocked until the topology 44 * trees have been released and deallocated. 45 * 46 * Walker Library Interfaces 47 * 48 * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders 49 * may initiate topology tree walks on a scheme-tree basis. topo_walk_init() 50 * will initiate the data structures required to walk any one one of the 51 * FMRI scheme trees. The walker data structure, topo_walk_t, is an opaque 52 * handle passed to topo_walk_step to begin the walk. At each node in the 53 * topology tree, a callback function is called with access to the node at 54 * which our current walk falls. The callback function is passed in during 55 * calls to topo_walk_init() and used throughout the walk_step of the 56 * scheme tree. At any time, the callback may terminate the walk by returning 57 * TOPO_WALK_TERMINATE or TOPO_WALK_ERR. TOPO_WALK_NEXT will continue the walk. 58 * 59 * The type of walk through the tree may be breadth first or depth first by 60 * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to 61 * the topo_walk_step() function. Topology nodes 62 * associated with an outstanding walk are held in place and will not be 63 * deallocated until the walk through that node completes. 64 * 65 * Once the walk has terminated, the walking process should call 66 * topo_walk_fini() to clean-up resources created in topo_walk_init() 67 * and release nodes that may be still held. 68 */ 69 70 #include <alloca.h> 71 #include <ctype.h> 72 #include <pthread.h> 73 #include <limits.h> 74 #include <assert.h> 75 #include <fcntl.h> 76 #include <smbios.h> 77 #include <sys/param.h> 78 #include <sys/types.h> 79 #include <sys/stat.h> 80 #include <sys/systeminfo.h> 81 #include <sys/utsname.h> 82 #include <uuid/uuid.h> 83 84 #include <fm/libtopo.h> 85 86 #include <topo_alloc.h> 87 #include <topo_builtin.h> 88 #include <topo_string.h> 89 #include <topo_error.h> 90 #include <topo_subr.h> 91 92 static void topo_snap_destroy(topo_hdl_t *); 93 94 static topo_hdl_t * 95 set_open_errno(topo_hdl_t *thp, int *errp, int err) 96 { 97 if (thp != NULL) { 98 topo_close(thp); 99 } 100 if (errp != NULL) 101 *errp = err; 102 return (NULL); 103 } 104 105 static char * 106 smbios_fix(topo_hdl_t *thp, char *begin) 107 { 108 char buf[MAXNAMELEN]; 109 size_t count; 110 char *str, *end, *pp; 111 112 end = begin + strlen(begin); 113 114 while (begin < end && isspace(*begin)) 115 begin++; 116 while (begin < end && isspace(*(end - 1))) 117 end--; 118 119 if (begin >= end) 120 return (NULL); 121 122 count = end - begin; 123 count += 1; 124 125 if (count > sizeof (buf)) 126 return (NULL); 127 128 (void) snprintf(buf, count, "%s", begin); 129 while ((str = strchr(buf, ' ')) != NULL) 130 *str = '-'; 131 132 pp = topo_hdl_strdup(thp, buf); 133 return (pp); 134 } 135 136 topo_hdl_t * 137 topo_open(int version, const char *rootdir, int *errp) 138 { 139 topo_hdl_t *thp = NULL; 140 topo_alloc_t *tap; 141 142 char platform[MAXNAMELEN]; 143 char isa[MAXNAMELEN]; 144 struct utsname uts; 145 struct stat st; 146 147 smbios_hdl_t *shp; 148 smbios_system_t s1; 149 smbios_info_t s2; 150 id_t id; 151 152 char *dbflags, *dbout; 153 154 if (version != TOPO_VERSION) 155 return (set_open_errno(thp, errp, ETOPO_HDL_ABIVER)); 156 157 if (rootdir != NULL && stat(rootdir, &st) < 0) 158 return (set_open_errno(thp, errp, ETOPO_HDL_INVAL)); 159 160 if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL) 161 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 162 163 (void) pthread_mutex_init(&thp->th_lock, NULL); 164 165 if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL) 166 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 167 168 /* 169 * Install default allocators 170 */ 171 tap->ta_flags = 0; 172 tap->ta_alloc = topo_alloc; 173 tap->ta_zalloc = topo_zalloc; 174 tap->ta_free = topo_free; 175 tap->ta_nvops.nv_ao_alloc = topo_nv_alloc; 176 tap->ta_nvops.nv_ao_free = topo_nv_free; 177 (void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops); 178 thp->th_alloc = tap; 179 180 if ((thp->th_modhash = topo_modhash_create(thp)) == NULL) 181 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 182 183 /* 184 * Set-up system information and search paths for modules 185 * and topology map files 186 */ 187 if (rootdir == NULL) { 188 rootdir = topo_hdl_strdup(thp, "/"); 189 thp->th_rootdir = (char *)rootdir; 190 } else { 191 int len; 192 char *rpath; 193 194 len = strlen(rootdir); 195 if (len >= PATH_MAX) 196 return (set_open_errno(thp, errp, EINVAL)); 197 198 if (rootdir[len] != '/') { 199 rpath = alloca(len + 1); 200 (void) snprintf(rpath, len + 1, "%s/", rootdir); 201 } else { 202 rpath = (char *)rootdir; 203 } 204 thp->th_rootdir = topo_hdl_strdup(thp, rpath); 205 } 206 207 platform[0] = '\0'; 208 isa[0] = '\0'; 209 (void) sysinfo(SI_PLATFORM, platform, sizeof (platform)); 210 (void) sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)); 211 (void) uname(&uts); 212 thp->th_platform = topo_hdl_strdup(thp, platform); 213 thp->th_isa = topo_hdl_strdup(thp, isa); 214 thp->th_machine = topo_hdl_strdup(thp, uts.machine); 215 if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) { 216 if ((id = smbios_info_system(shp, &s1)) != SMB_ERR && 217 smbios_info_common(shp, id, &s2) != SMB_ERR) { 218 219 if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 && 220 strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) { 221 thp->th_product = smbios_fix(thp, 222 (char *)s2.smbi_product); 223 } 224 } 225 smbios_close(shp); 226 } else { 227 thp->th_product = topo_hdl_strdup(thp, thp->th_platform); 228 } 229 230 if (thp->th_rootdir == NULL) 231 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 232 233 dbflags = getenv("TOPO_DEBUG"); 234 dbout = getenv("TOPO_DEBUG_OUT"); 235 if (dbflags != NULL) 236 topo_debug_set(thp, dbflags, dbout); 237 238 if (topo_builtin_create(thp, thp->th_rootdir) != 0) { 239 topo_dprintf(thp, TOPO_DBG_ERR, 240 "failed to load builtin modules: %s\n", 241 topo_hdl_errmsg(thp)); 242 topo_close(thp); 243 return (NULL); 244 } 245 246 return (thp); 247 } 248 249 void 250 topo_close(topo_hdl_t *thp) 251 { 252 ttree_t *tp; 253 254 topo_hdl_lock(thp); 255 if (thp->th_platform != NULL) 256 topo_hdl_strfree(thp, thp->th_platform); 257 if (thp->th_isa != NULL) 258 topo_hdl_strfree(thp, thp->th_isa); 259 if (thp->th_machine != NULL) 260 topo_hdl_strfree(thp, thp->th_machine); 261 if (thp->th_product != NULL) 262 topo_hdl_strfree(thp, thp->th_product); 263 if (thp->th_rootdir != NULL) 264 topo_hdl_strfree(thp, thp->th_rootdir); 265 266 /* 267 * Clean-up snapshot 268 */ 269 topo_snap_destroy(thp); 270 271 /* 272 * Clean-up trees 273 */ 274 while ((tp = topo_list_next(&thp->th_trees)) != NULL) { 275 topo_list_delete(&thp->th_trees, tp); 276 topo_tree_destroy(tp); 277 } 278 279 /* 280 * Unload all plugins 281 */ 282 topo_modhash_unload_all(thp); 283 284 if (thp->th_modhash != NULL) 285 topo_modhash_destroy(thp); 286 if (thp->th_alloc != NULL) 287 topo_free(thp->th_alloc, sizeof (topo_alloc_t)); 288 289 topo_hdl_unlock(thp); 290 291 topo_free(thp, sizeof (topo_hdl_t)); 292 } 293 294 static char * 295 topo_snap_create(topo_hdl_t *thp, int *errp) 296 { 297 uuid_t uuid; 298 char *ustr = NULL; 299 300 topo_hdl_lock(thp); 301 if (thp->th_uuid != NULL) { 302 *errp = ETOPO_HDL_UUID; 303 topo_hdl_unlock(thp); 304 return (NULL); 305 } 306 307 if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) { 308 *errp = ETOPO_NOMEM; 309 topo_dprintf(thp, TOPO_DBG_ERR, "unable to allocate uuid: %s\n", 310 topo_strerror(*errp)); 311 topo_hdl_unlock(thp); 312 return (NULL); 313 } 314 315 uuid_generate(uuid); 316 uuid_unparse(uuid, thp->th_uuid); 317 318 if (topo_tree_enum_all(thp) < 0) { 319 topo_dprintf(thp, TOPO_DBG_ERR, "enumeration failure: %s\n", 320 topo_hdl_errmsg(thp)); 321 if (topo_hdl_errno(thp) == ETOPO_ENUM_FATAL) { 322 *errp = thp->th_errno; 323 topo_hdl_unlock(thp); 324 return (NULL); 325 } 326 } 327 328 if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) 329 *errp = ETOPO_NOMEM; 330 331 thp->th_di = DI_NODE_NIL; 332 thp->th_pi = DI_PROM_HANDLE_NIL; 333 334 topo_hdl_unlock(thp); 335 336 return (ustr); 337 } 338 339 /*ARGSUSED*/ 340 static char * 341 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp) 342 { 343 return ((char *)uuid); 344 } 345 346 /* 347 * Return snapshot id 348 */ 349 char * 350 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp) 351 { 352 if (thp == NULL) 353 return (NULL); 354 355 if (uuid == NULL) 356 return (topo_snap_create(thp, errp)); 357 else 358 return (topo_snap_log_create(thp, uuid, errp)); 359 } 360 361 /*ARGSUSED*/ 362 static int 363 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused) 364 { 365 tnode_t *cnode; 366 367 cnode = topo_child_first(node); 368 369 if (cnode != NULL) 370 return (TOPO_WALK_NEXT); 371 372 topo_node_unbind(node); 373 374 return (TOPO_WALK_NEXT); 375 } 376 377 static void 378 topo_snap_destroy(topo_hdl_t *thp) 379 { 380 int i; 381 ttree_t *tp; 382 topo_walk_t *twp; 383 tnode_t *root; 384 topo_nodehash_t *nhp; 385 topo_mod_t *mod; 386 387 for (tp = topo_list_next(&thp->th_trees); tp != NULL; 388 tp = topo_list_next(tp)) { 389 390 root = tp->tt_root; 391 twp = tp->tt_walk; 392 /* 393 * Clean-up tree nodes from the bottom-up 394 */ 395 if ((twp->tw_node = topo_child_first(root)) != NULL) { 396 twp->tw_cb = topo_walk_destroy; 397 topo_node_hold(root); 398 topo_node_hold(twp->tw_node); /* released at walk end */ 399 (void) topo_walk_bottomup(twp, TOPO_WALK_CHILD); 400 topo_node_rele(root); 401 } 402 403 /* 404 * Tidy-up the root node 405 */ 406 while ((nhp = topo_list_next(&root->tn_children)) != NULL) { 407 for (i = 0; i < nhp->th_arrlen; i++) { 408 assert(nhp->th_nodearr[i] == NULL); 409 } 410 mod = nhp->th_enum; 411 topo_mod_strfree(mod, nhp->th_name); 412 topo_mod_free(mod, nhp->th_nodearr, 413 nhp->th_arrlen * sizeof (tnode_t *)); 414 topo_list_delete(&root->tn_children, nhp); 415 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t)); 416 topo_mod_rele(mod); 417 } 418 419 } 420 421 if (thp->th_uuid != NULL) { 422 topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE); 423 thp->th_uuid = NULL; 424 } 425 } 426 427 void 428 topo_snap_release(topo_hdl_t *thp) 429 { 430 if (thp == NULL) 431 return; 432 433 topo_hdl_lock(thp); 434 topo_snap_destroy(thp); 435 topo_hdl_unlock(thp); 436 437 } 438 439 topo_walk_t * 440 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f, 441 void *pdata, int *errp) 442 { 443 ttree_t *tp; 444 topo_walk_t *wp; 445 446 for (tp = topo_list_next(&thp->th_trees); tp != NULL; 447 tp = topo_list_next(tp)) { 448 if (strcmp(scheme, tp->tt_scheme) == 0) { 449 450 /* 451 * Hold the root node and start walk at the first 452 * child node 453 */ 454 assert(tp->tt_root != NULL); 455 456 if ((wp = topo_node_walk_init(thp, NULL, tp->tt_root, 457 cb_f, pdata, errp)) == NULL) /* errp set */ 458 return (NULL); 459 460 return (wp); 461 } 462 } 463 464 *errp = ETOPO_WALK_NOTFOUND; 465 return (NULL); 466 } 467 468 static int 469 step_child(tnode_t *cnp, topo_walk_t *wp, int bottomup) 470 { 471 int status; 472 tnode_t *nnp; 473 474 nnp = topo_child_first(cnp); 475 476 if (nnp == NULL) { 477 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 478 "step_child: TOPO_WALK_TERMINATE for %s=%d\n", 479 cnp->tn_name, cnp->tn_instance); 480 return (TOPO_WALK_TERMINATE); 481 } 482 483 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 484 "step_child: walk through node %s=%d to %s=%d\n", 485 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance); 486 487 topo_node_hold(nnp); /* released on return from walk_step */ 488 wp->tw_node = nnp; 489 if (bottomup == 1) 490 status = topo_walk_bottomup(wp, TOPO_WALK_CHILD); 491 else 492 status = topo_walk_step(wp, TOPO_WALK_CHILD); 493 494 return (status); 495 } 496 497 static int 498 step_sibling(tnode_t *cnp, topo_walk_t *wp, int bottomup) 499 { 500 int status; 501 tnode_t *nnp; 502 503 nnp = topo_child_next(cnp->tn_parent, cnp); 504 505 if (nnp == NULL) { 506 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 507 "step_sibling: TOPO_WALK_TERMINATE for %s=%d\n", 508 cnp->tn_name, cnp->tn_instance); 509 return (TOPO_WALK_TERMINATE); 510 } 511 512 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 513 "step_sibling: through sibling node %s=%d to %s=%d\n", 514 cnp->tn_name, cnp->tn_instance, nnp->tn_name, nnp->tn_instance); 515 516 topo_node_hold(nnp); /* released on return from walk_step */ 517 wp->tw_node = nnp; 518 if (bottomup == 1) 519 status = topo_walk_bottomup(wp, TOPO_WALK_CHILD); 520 else 521 status = topo_walk_step(wp, TOPO_WALK_CHILD); 522 523 return (status); 524 } 525 526 int 527 topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t inst) 528 { 529 int status; 530 tnode_t *nnp, *cnp; 531 532 cnp = wp->tw_node; 533 nnp = topo_node_lookup(cnp, name, inst); 534 if (nnp == NULL) 535 return (TOPO_WALK_TERMINATE); 536 537 topo_node_hold(nnp); 538 wp->tw_node = nnp; 539 if (wp->tw_mod != NULL) 540 status = wp->tw_cb(wp->tw_mod, nnp, wp->tw_pdata); 541 else 542 status = wp->tw_cb(wp->tw_thp, nnp, wp->tw_pdata); 543 topo_node_rele(nnp); 544 wp->tw_node = cnp; 545 546 return (status); 547 } 548 549 int 550 topo_walk_step(topo_walk_t *wp, int flag) 551 { 552 int status; 553 tnode_t *cnp = wp->tw_node; 554 555 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) { 556 topo_node_rele(cnp); 557 return (TOPO_WALK_ERR); 558 } 559 560 /* 561 * No more nodes to walk 562 */ 563 if (cnp == NULL) { 564 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 565 "walk_step terminated\n"); 566 topo_node_rele(cnp); 567 return (TOPO_WALK_TERMINATE); 568 } 569 570 571 if (wp->tw_mod != NULL) 572 status = wp->tw_cb(wp->tw_mod, cnp, wp->tw_pdata); 573 else 574 status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata); 575 576 /* 577 * Walker callback says we're done 578 */ 579 if (status != TOPO_WALK_NEXT) { 580 topo_node_rele(cnp); 581 return (status); 582 } 583 584 flag == TOPO_WALK_CHILD ? (status = step_child(cnp, wp, 0)) : 585 (status = step_sibling(cnp, wp, 0)); 586 587 /* 588 * No more nodes in this hash, skip to next node hash by stepping 589 * to next sibling (depth-first walk) or next child (breadth-first 590 * walk). 591 */ 592 if (status == TOPO_WALK_TERMINATE) { 593 594 flag == TOPO_WALK_CHILD ? (status = step_sibling(cnp, wp, 0)) : 595 (status = step_child(cnp, wp, 0)); 596 } 597 598 topo_node_rele(cnp); /* done with current node */ 599 600 return (status); 601 } 602 603 void 604 topo_walk_fini(topo_walk_t *wp) 605 { 606 if (wp == NULL) 607 return; 608 609 topo_node_rele(wp->tw_root); 610 611 topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t)); 612 } 613 614 int 615 topo_walk_bottomup(topo_walk_t *wp, int flag) 616 { 617 int status; 618 tnode_t *cnp; 619 620 if (wp == NULL) 621 return (TOPO_WALK_ERR); 622 623 cnp = wp->tw_node; 624 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) { 625 topo_node_rele(cnp); 626 return (TOPO_WALK_ERR); 627 } 628 629 /* 630 * End of the line 631 */ 632 if (cnp == NULL) { 633 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 634 "walk_bottomup terminated\n"); 635 topo_node_rele(cnp); 636 return (TOPO_WALK_TERMINATE); 637 } 638 639 topo_dprintf(wp->tw_thp, TOPO_DBG_WALK, 640 "%s walk_bottomup through node %s=%d\n", 641 (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"), 642 cnp->tn_name, cnp->tn_instance); 643 644 if (flag == TOPO_WALK_CHILD) 645 status = step_child(cnp, wp, 1); 646 else 647 status = step_sibling(cnp, wp, 1); 648 649 /* 650 * At a leaf, run the callback 651 */ 652 if (status == TOPO_WALK_TERMINATE) { 653 if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata)) 654 != TOPO_WALK_NEXT) { 655 topo_node_rele(cnp); 656 return (status); 657 } 658 } 659 660 /* 661 * Try next child or sibling 662 */ 663 if (status == TOPO_WALK_NEXT) { 664 if (flag == TOPO_WALK_CHILD) 665 status = step_sibling(cnp, wp, 1); 666 else 667 status = step_child(cnp, wp, 1); 668 } 669 670 topo_node_rele(cnp); /* done with current node */ 671 672 return (status); 673 } 674