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