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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Snapshot Library Interfaces 31 * 32 * Consumers of topology data may use the interfaces in this file to open, 33 * snapshot and close a topology exported by FMRI scheme (hc, mem and cpu) 34 * builtin plugins and their helper modules. A topology handle is obtained 35 * by calling topo_open(). Upon a successful return, the caller may use this 36 * handle to open a new snapshot. Each snapshot is assigned a Universally 37 * Unique Identifier that in a future enchancement to the libtopo API will be 38 * used as the file locator in /var/fm/topo to persist new snapshots or lookup 39 * a previously captured snapshot. topo_snap_hold() will capture the current 40 * system topology. All consumers of the topo_hdl_t argument will be 41 * blocked from accessing the topology trees until the snapshot completes. 42 * 43 * A snapshot may be cleared by calling topo_snap_rele(). As with 44 * topo_snap_hold(), all topology accesses are blocked until the topology 45 * trees have been released and deallocated. 46 * 47 * Walker Library Interfaces 48 * 49 * Once a snapshot has been taken with topo_snap_hold(), topo_hdl_t holders 50 * may initiate topology tree walks on a scheme-tree basis. topo_walk_init() 51 * will initiate the data structures required to walk any one one of the 52 * FMRI scheme trees. The walker data structure, topo_walk_t, is an opaque 53 * handle passed to topo_walk_step to begin the walk. At each node in the 54 * topology tree, a callback function is called with access to the node at 55 * which our current walk falls. The callback function is passed in during 56 * calls to topo_walk_init() and used throughout the walk_step of the 57 * scheme tree. At any time, the callback may terminate the walk by returning 58 * TOPO_WALK_TERMINATE or TOPO_WALK_ERR. TOPO_WALK_NEXT will continue the 59 * walk. 60 * 61 * Walks through the tree may be breadth first or depth first by 62 * respectively passing in TOPO_WALK_SIBLING or TOPO_WALK_CHILD to 63 * the topo_walk_step() function. Topology nodes associated with an 64 * outstanding walk are held in place and will not be deallocated until 65 * the walk through that node completes. 66 * 67 * Once the walk has terminated, the walking process should call 68 * topo_walk_fini() to clean-up resources created in topo_walk_init() 69 * and release nodes that may be still held. 70 */ 71 72 #include <pthread.h> 73 #include <limits.h> 74 #include <assert.h> 75 #include <fcntl.h> 76 #include <sys/types.h> 77 #include <sys/stat.h> 78 #include <uuid/uuid.h> 79 80 #include <fm/libtopo.h> 81 82 #include <topo_alloc.h> 83 #include <topo_builtin.h> 84 #include <topo_string.h> 85 #include <topo_error.h> 86 #include <topo_subr.h> 87 88 static void topo_snap_destroy(topo_hdl_t *); 89 90 static topo_hdl_t * 91 set_open_errno(topo_hdl_t *thp, int *errp, int err) 92 { 93 if (thp != NULL) { 94 topo_close(thp); 95 } 96 if (errp != NULL) 97 *errp = err; 98 return (NULL); 99 } 100 101 topo_hdl_t * 102 topo_open(int version, const char *rootdir, int *errp) 103 { 104 topo_hdl_t *thp = NULL; 105 topo_alloc_t *tap; 106 struct stat st; 107 108 if (version < TOPO_VERSION) 109 return (set_open_errno(thp, errp, ETOPO_HDL_VER)); 110 111 if (version > TOPO_VERSION) 112 return (set_open_errno(thp, errp, ETOPO_HDL_VER)); 113 114 if (rootdir != NULL && stat(rootdir, &st) < 0) 115 return (set_open_errno(thp, errp, ETOPO_HDL_INVAL)); 116 117 if ((thp = topo_zalloc(sizeof (topo_hdl_t), 0)) == NULL) 118 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 119 120 if ((tap = topo_zalloc(sizeof (topo_alloc_t), 0)) == NULL) 121 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 122 123 /* 124 * Install default allocators 125 */ 126 tap->ta_flags = 0; 127 tap->ta_alloc = topo_alloc; 128 tap->ta_zalloc = topo_zalloc; 129 tap->ta_free = topo_free; 130 tap->ta_nvops.nv_ao_alloc = topo_nv_alloc; 131 tap->ta_nvops.nv_ao_free = topo_nv_free; 132 (void) nv_alloc_init(&tap->ta_nva, &tap->ta_nvops); 133 thp->th_alloc = tap; 134 135 if ((thp->th_modhash = topo_modhash_create(thp)) == NULL) 136 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 137 138 if (rootdir == NULL) { 139 rootdir = topo_hdl_strdup(thp, "/"); 140 thp->th_rootdir = (char *)rootdir; 141 } else { 142 if (strlen(rootdir) > PATH_MAX) 143 return (set_open_errno(thp, errp, EINVAL)); 144 145 thp->th_rootdir = topo_hdl_strdup(thp, rootdir); 146 } 147 148 if (thp->th_rootdir == NULL) 149 return (set_open_errno(thp, errp, ETOPO_NOMEM)); 150 151 if (topo_builtin_create(thp, thp->th_rootdir) != 0) { 152 topo_dprintf(TOPO_DBG_ERR, "failed to load builtin modules: " 153 "%s\n", topo_hdl_errmsg(thp)); 154 return (NULL); 155 } 156 157 return (thp); 158 } 159 160 void 161 topo_close(topo_hdl_t *thp) 162 { 163 ttree_t *tp; 164 165 topo_hdl_lock(thp); 166 if (thp->th_rootdir != NULL) 167 topo_hdl_strfree(thp, thp->th_rootdir); 168 169 /* 170 * Clean-up snapshot 171 */ 172 topo_snap_destroy(thp); 173 174 /* 175 * Clean-up trees 176 */ 177 while ((tp = topo_list_next(&thp->th_trees)) != NULL) { 178 topo_list_delete(&thp->th_trees, tp); 179 topo_tree_destroy(thp, tp); 180 } 181 182 /* 183 * Unload all plugins 184 */ 185 topo_modhash_unload_all(thp); 186 187 if (thp->th_modhash != NULL) 188 topo_modhash_destroy(thp); 189 if (thp->th_alloc != NULL) 190 topo_free(thp->th_alloc, sizeof (topo_alloc_t)); 191 192 topo_hdl_unlock(thp); 193 194 topo_free(thp, sizeof (topo_hdl_t)); 195 } 196 197 static char * 198 topo_snap_create(topo_hdl_t *thp, int *errp) 199 { 200 uuid_t uuid; 201 char *ustr = NULL; 202 203 topo_hdl_lock(thp); 204 if (thp->th_uuid != NULL) { 205 *errp = ETOPO_HDL_UUID; 206 topo_hdl_unlock(thp); 207 return (NULL); 208 } 209 210 if ((thp->th_uuid = topo_hdl_zalloc(thp, TOPO_UUID_SIZE)) == NULL) { 211 *errp = ETOPO_NOMEM; 212 topo_dprintf(TOPO_DBG_ERR, "unable to allocate uuid: %s\n", 213 topo_strerror(*errp)); 214 topo_hdl_unlock(thp); 215 return (NULL); 216 } 217 218 uuid_generate(uuid); 219 uuid_unparse(uuid, thp->th_uuid); 220 221 if (topo_tree_enum_all(thp) < 0) { 222 topo_dprintf(TOPO_DBG_ERR, "enumeration failure: %s\n", 223 topo_hdl_errmsg(thp)); 224 if (topo_hdl_errno(thp) != ETOPO_ENUM_PARTIAL) { 225 *errp = thp->th_errno; 226 topo_hdl_unlock(thp); 227 return (NULL); 228 } 229 } 230 231 if ((ustr = topo_hdl_strdup(thp, thp->th_uuid)) == NULL) 232 *errp = ETOPO_NOMEM; 233 234 topo_hdl_unlock(thp); 235 236 return (ustr); 237 } 238 239 /*ARGSUSED*/ 240 static char * 241 topo_snap_log_create(topo_hdl_t *thp, const char *uuid, int *errp) 242 { 243 return ((char *)uuid); 244 } 245 246 /* 247 * Return snapshot id 248 */ 249 char * 250 topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp) 251 { 252 if (thp == NULL) 253 return (NULL); 254 255 if (uuid == NULL) 256 return (topo_snap_create(thp, errp)); 257 else 258 return (topo_snap_log_create(thp, uuid, errp)); 259 } 260 261 /*ARGSUSED*/ 262 static int 263 topo_walk_destroy(topo_hdl_t *thp, tnode_t *node, void *notused) 264 { 265 tnode_t *cnode; 266 267 cnode = topo_child_first(node); 268 269 if (cnode != NULL) 270 return (TOPO_WALK_NEXT); 271 272 topo_node_unbind(node); 273 274 return (TOPO_WALK_NEXT); 275 } 276 277 static void 278 topo_snap_destroy(topo_hdl_t *thp) 279 { 280 int i; 281 ttree_t *tp; 282 topo_walk_t *twp; 283 tnode_t *root; 284 topo_nodehash_t *nhp; 285 topo_mod_t *mod; 286 287 for (tp = topo_list_next(&thp->th_trees); tp != NULL; 288 tp = topo_list_next(tp)) { 289 290 root = tp->tt_root; 291 twp = tp->tt_walk; 292 /* 293 * Clean-up tree nodes from the bottom-up 294 */ 295 if ((twp->tw_node = topo_child_first(root)) != NULL) { 296 twp->tw_cb = topo_walk_destroy; 297 topo_node_hold(root); 298 topo_node_hold(twp->tw_node); /* released at walk end */ 299 (void) topo_walk_bottomup(twp, TOPO_WALK_CHILD); 300 topo_node_rele(root); 301 } 302 303 /* 304 * Tidy-up the root node 305 */ 306 while ((nhp = topo_list_next(&root->tn_children)) != NULL) { 307 for (i = 0; i < nhp->th_arrlen; i++) { 308 assert(nhp->th_nodearr[i] == NULL); 309 } 310 mod = nhp->th_enum; 311 topo_mod_strfree(mod, nhp->th_name); 312 topo_mod_free(mod, nhp->th_nodearr, 313 nhp->th_arrlen * sizeof (tnode_t *)); 314 topo_list_delete(&root->tn_children, nhp); 315 topo_mod_free(mod, nhp, sizeof (topo_nodehash_t)); 316 topo_mod_rele(mod); 317 } 318 319 /* 320 * Release the file handle 321 */ 322 if (tp->tt_file != NULL) 323 topo_file_unload(thp, tp); 324 325 } 326 327 } 328 329 void 330 topo_snap_release(topo_hdl_t *thp) 331 { 332 if (thp == NULL) 333 return; 334 335 topo_hdl_lock(thp); 336 if (thp->th_uuid != NULL) { 337 topo_hdl_free(thp, thp->th_uuid, TOPO_UUID_SIZE); 338 topo_snap_destroy(thp); 339 thp->th_uuid = NULL; 340 } 341 topo_hdl_unlock(thp); 342 343 } 344 345 topo_walk_t * 346 topo_walk_init(topo_hdl_t *thp, const char *scheme, topo_walk_cb_t cb_f, 347 void *pdata, int *errp) 348 { 349 tnode_t *child; 350 ttree_t *tp; 351 topo_walk_t *wp; 352 353 for (tp = topo_list_next(&thp->th_trees); tp != NULL; 354 tp = topo_list_next(tp)) { 355 if (strcmp(scheme, tp->tt_scheme) == 0) { 356 357 /* 358 * Hold the root node and start walk at the first 359 * child node 360 */ 361 assert(tp->tt_root != NULL); 362 363 topo_node_hold(tp->tt_root); 364 365 /* 366 * Nothing to walk 367 */ 368 if ((child = topo_child_first(tp->tt_root)) == NULL) { 369 *errp = ETOPO_WALK_EMPTY; 370 topo_node_rele(tp->tt_root); 371 return (NULL); 372 } 373 374 if ((wp = topo_hdl_zalloc(thp, sizeof (topo_walk_t))) 375 == NULL) { 376 *errp = ETOPO_NOMEM; 377 topo_node_rele(tp->tt_root); 378 return (NULL); 379 } 380 381 topo_node_hold(child); 382 383 wp->tw_root = tp->tt_root; 384 wp->tw_node = child; 385 wp->tw_cb = cb_f; 386 wp->tw_pdata = pdata; 387 wp->tw_thp = thp; 388 389 return (wp); 390 } 391 } 392 393 *errp = ETOPO_WALK_NOTFOUND; 394 return (NULL); 395 } 396 397 static int 398 step_child(tnode_t *cnp, topo_walk_t *wp, int bottomup) 399 { 400 int status; 401 tnode_t *nnp; 402 403 nnp = topo_child_first(cnp); 404 405 if (nnp == NULL) 406 return (TOPO_WALK_TERMINATE); 407 408 topo_dprintf(TOPO_DBG_WALK, "walk through child node %s=%d\n", 409 nnp->tn_name, nnp->tn_instance); 410 411 topo_node_hold(nnp); /* released on return from walk_step */ 412 wp->tw_node = nnp; 413 if (bottomup == 1) 414 status = topo_walk_bottomup(wp, TOPO_WALK_CHILD); 415 else 416 status = topo_walk_step(wp, TOPO_WALK_CHILD); 417 418 return (status); 419 } 420 421 static int 422 step_sibling(tnode_t *cnp, topo_walk_t *wp, int bottomup) 423 { 424 int status; 425 tnode_t *nnp; 426 427 nnp = topo_child_next(cnp->tn_parent, cnp); 428 429 if (nnp == NULL) 430 return (TOPO_WALK_TERMINATE); 431 432 topo_dprintf(TOPO_DBG_WALK, "walk through sibling node %s=%d\n", 433 nnp->tn_name, nnp->tn_instance); 434 435 topo_node_hold(nnp); /* released on return from walk_step */ 436 wp->tw_node = nnp; 437 if (bottomup == 1) 438 status = topo_walk_bottomup(wp, TOPO_WALK_CHILD); 439 else 440 status = topo_walk_step(wp, TOPO_WALK_CHILD); 441 442 return (status); 443 } 444 445 int 446 topo_walk_step(topo_walk_t *wp, int flag) 447 { 448 int status; 449 tnode_t *cnp = wp->tw_node; 450 451 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) { 452 topo_node_rele(cnp); 453 return (TOPO_WALK_ERR); 454 } 455 456 /* 457 * End of the line 458 */ 459 if (cnp == NULL) { 460 topo_dprintf(TOPO_DBG_WALK, "walk_step terminated\n"); 461 topo_node_rele(cnp); 462 return (TOPO_WALK_TERMINATE); 463 } 464 465 topo_dprintf(TOPO_DBG_WALK, "%s walk_step through node %s=%d\n", 466 (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"), 467 cnp->tn_name, cnp->tn_instance); 468 469 if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata)) 470 != TOPO_WALK_NEXT) { 471 topo_node_rele(cnp); 472 return (status); 473 } 474 475 if (flag == TOPO_WALK_CHILD) 476 status = step_child(cnp, wp, 0); 477 else 478 status = step_sibling(cnp, wp, 0); 479 480 /* 481 * End of the walk, try next child or sibling 482 */ 483 if (status == TOPO_WALK_TERMINATE) { 484 if (flag == TOPO_WALK_CHILD) 485 status = step_sibling(cnp, wp, 0); 486 else 487 status = step_child(cnp, wp, 0); 488 } 489 490 topo_node_rele(cnp); /* done with current node */ 491 492 return (status); 493 } 494 495 void 496 topo_walk_fini(topo_walk_t *wp) 497 { 498 if (wp == NULL) 499 return; 500 501 topo_node_rele(wp->tw_root); 502 503 topo_hdl_free(wp->tw_thp, wp, sizeof (topo_walk_t)); 504 } 505 506 int 507 topo_walk_bottomup(topo_walk_t *wp, int flag) 508 { 509 int status; 510 tnode_t *cnp; 511 512 if (wp == NULL) 513 return (TOPO_WALK_ERR); 514 515 cnp = wp->tw_node; 516 if (flag != TOPO_WALK_CHILD && flag != TOPO_WALK_SIBLING) { 517 topo_node_rele(cnp); 518 return (TOPO_WALK_ERR); 519 } 520 521 /* 522 * End of the line 523 */ 524 if (cnp == NULL) { 525 topo_dprintf(TOPO_DBG_WALK, "walk_bottomup terminated\n"); 526 topo_node_rele(cnp); 527 return (TOPO_WALK_TERMINATE); 528 } 529 530 topo_dprintf(TOPO_DBG_WALK, "%s walk_bottomup through node %s=%d\n", 531 (flag == TOPO_WALK_CHILD ? "TOPO_WALK_CHILD" : "TOPO_WALK_SIBLING"), 532 cnp->tn_name, cnp->tn_instance); 533 534 if (flag == TOPO_WALK_CHILD) 535 status = step_child(cnp, wp, 1); 536 else 537 status = step_sibling(cnp, wp, 1); 538 539 /* 540 * At a leaf, run the callback 541 */ 542 if (status == TOPO_WALK_TERMINATE) { 543 if ((status = wp->tw_cb(wp->tw_thp, cnp, wp->tw_pdata)) 544 != TOPO_WALK_NEXT) { 545 topo_node_rele(cnp); 546 return (status); 547 } 548 } 549 550 /* 551 * Try next child or sibling 552 */ 553 if (status == TOPO_WALK_NEXT) { 554 if (flag == TOPO_WALK_CHILD) 555 status = step_sibling(cnp, wp, 1); 556 else 557 status = step_child(cnp, wp, 1); 558 } 559 560 topo_node_rele(cnp); /* done with current node */ 561 562 return (status); 563 } 564