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