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