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 #include <stdio.h> 27 #include <unistd.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <pthread.h> 32 #include <errno.h> 33 #include <libnvpair.h> 34 #include <dlfcn.h> 35 #include <link.h> 36 #include <assert.h> 37 38 #include <sys/processor.h> 39 #include <sys/stat.h> 40 #include <sys/mdesc.h> 41 #include <sys/param.h> 42 #include <sys/systeminfo.h> 43 #include <sys/mem.h> 44 #include <sys/bl.h> 45 #include <sys/fm/protocol.h> 46 #include <fm/fmd_fmri.h> 47 #include <fm/fmd_agent.h> 48 #include <sys/pri.h> 49 50 #include "ldom.h" 51 #include "ldmsvcs_utils.h" 52 53 #define MD_STR_PLATFORM "platform" 54 #define MD_STR_DOM_CAPABLE "domaining-enabled" 55 56 static int ldom_ldmd_is_up = 0; /* assume stays up if ever seen up */ 57 58 static void *ldom_dl_hp = (void *)NULL; 59 static const char *ldom_dl_path = "libpri.so.1"; 60 static int ldom_dl_mode = (RTLD_NOW | RTLD_LOCAL); 61 62 static pthread_mutex_t ldom_pri_lock = PTHREAD_MUTEX_INITIALIZER; 63 static int ldom_pri_ref_cnt = 0; /* num of outstanding ldom_pri_init()s */ 64 static int ldom_pri_init_done = 0; /* bool for real pri_init() done */ 65 static int (*ldom_pri_fp_init)(void) = (int (*)(void))NULL; 66 static void (*ldom_pri_fp_fini)(void) = (void (*)(void))NULL; 67 static ssize_t (*ldom_pri_fp_get)(uint8_t wait, uint64_t *token, uint64_t **buf, 68 void *(*allocp)(size_t), void (*freep)(void *, size_t)) = 69 (ssize_t (*)(uint8_t wait, uint64_t *token, uint64_t **buf, 70 void *(*allocp)(size_t), void (*freep)(void *, size_t)))NULL; 71 72 static void 73 ldom_pri_config(void) 74 { 75 char isa[MAXNAMELEN]; /* used to see if machine is sun4v */ 76 77 if (sysinfo(SI_MACHINE, isa, MAXNAMELEN) < 0) 78 return; 79 if (strcmp(isa, "sun4v") != 0) 80 return; 81 if ((ldom_dl_hp = dlopen(ldom_dl_path, ldom_dl_mode)) == NULL) 82 return; 83 84 ldom_pri_fp_init = (int (*)(void))dlsym(ldom_dl_hp, "pri_init"); 85 ldom_pri_fp_fini = (void (*)(void))dlsym(ldom_dl_hp, "pri_fini"); 86 ldom_pri_fp_get = (ssize_t (*)(uint8_t wait, uint64_t *token, 87 uint64_t **buf, void *(*allocp)(size_t), 88 void (*freep)(void *, size_t)))dlsym(ldom_dl_hp, "pri_get"); 89 } 90 91 static void 92 ldom_pri_unconfig(void) 93 { 94 if (ldom_dl_hp == NULL) 95 return; 96 97 ldom_pri_fp_init = (int (*)(void))NULL; 98 ldom_pri_fp_fini = (void (*)(void))NULL; 99 ldom_pri_fp_get = (ssize_t (*)(uint8_t wait, uint64_t *token, 100 uint64_t **buf, void *(*allocp)(size_t), 101 void (*freep)(void *, size_t)))NULL; 102 (void) dlclose(ldom_dl_hp); 103 ldom_dl_hp = (void *)NULL; 104 } 105 106 /* 107 * ldom_pri_lock is assumed already held by anyone accessing ldom_pri_ref_cnt 108 */ 109 110 static int 111 ldom_pri_init(void) 112 { 113 if (ldom_pri_ref_cnt == 0) { 114 ldom_pri_config(); 115 /* 116 * ldom_pri_init() is called before we know whether we 117 * have LDOMS FW or not; defer calling pri_init() via 118 * ldom_pri_fp_init until the first time we try to 119 * actually get a PRI 120 */ 121 } 122 ldom_pri_ref_cnt++; 123 124 assert(ldom_pri_ref_cnt > 0); 125 126 return (0); 127 } 128 129 static void 130 ldom_pri_fini(void) 131 { 132 assert(ldom_pri_ref_cnt > 0); 133 134 ldom_pri_ref_cnt--; 135 if (ldom_pri_ref_cnt == 0) { 136 if (ldom_pri_init_done && (ldom_pri_fp_fini != NULL)) { 137 (*ldom_pri_fp_fini)(); 138 ldom_pri_init_done = 0; 139 } 140 ldom_pri_unconfig(); 141 } 142 } 143 144 static ssize_t 145 ldom_pri_get(uint8_t wait, uint64_t *token, uint64_t **buf, 146 void *(*allocp)(size_t), void (*freep)(void *, size_t)) 147 { 148 assert(ldom_pri_ref_cnt > 0); 149 150 if ((!ldom_pri_init_done) && (ldom_pri_fp_init != NULL)) { 151 if ((*ldom_pri_fp_init)() < 0) 152 return (-1); 153 ldom_pri_init_done = 1; 154 } 155 156 if (ldom_pri_fp_get != NULL) 157 return ((*ldom_pri_fp_get)(wait, token, buf, allocp, freep)); 158 else 159 return (-1); 160 } 161 162 static ssize_t 163 get_local_core_md(ldom_hdl_t *lhp, uint64_t **buf) 164 { 165 int fh; 166 size_t size; 167 uint64_t *bufp; 168 169 if ((fh = open("/devices/pseudo/mdesc@0:mdesc", O_RDONLY, 0)) < 0) 170 return (-1); 171 172 if (ioctl(fh, MDESCIOCGSZ, &size) < 0) { 173 (void) close(fh); 174 return (-1); 175 } 176 177 bufp = (uint64_t *)lhp->allocp(size); 178 179 if (read(fh, bufp, size) < 0) { 180 lhp->freep(bufp, size); 181 (void) close(fh); 182 return (-1); 183 } 184 (void) close(fh); 185 186 *buf = bufp; 187 188 return ((ssize_t)size); 189 } 190 191 192 static int 193 get_local_md_prop_value(ldom_hdl_t *lhp, char *node, char *prop, uint64_t *val) 194 { 195 int rc = 1; 196 uint64_t *bufp; 197 ssize_t bufsiz; 198 199 if ((bufsiz = get_local_core_md(lhp, &bufp)) > 0) { 200 md_t *mdp; 201 202 if (mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) { 203 int num_nodes; 204 mde_cookie_t *listp; 205 206 num_nodes = md_node_count(mdp); 207 listp = lhp->allocp(sizeof (mde_cookie_t) * num_nodes); 208 209 if (md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, 210 md_find_name(mdp, node), 211 md_find_name(mdp, "fwd"), listp) > 0 && 212 md_get_prop_val(mdp, listp[0], prop, val) >= 0) { 213 /* found the property */ 214 rc = 0; 215 } 216 217 lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes); 218 (void) md_fini(mdp); 219 } 220 lhp->freep(bufp, bufsiz); 221 } 222 return (rc); 223 } 224 225 static int 226 ldom_getinfo(struct ldom_hdl *lhp) 227 { 228 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; 229 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 230 static int major_version = -1; 231 static int service_ldom = -1; 232 static int busy_init = 0; 233 234 int ier, rc = 0; 235 uint64_t domain_capable; 236 237 (void) pthread_mutex_lock(&mt); 238 239 while (busy_init == 1) 240 (void) pthread_cond_wait(&cv, &mt); 241 242 if (major_version != -1 && service_ldom != -1) { 243 lhp->major_version = major_version; 244 lhp->service_ldom = service_ldom; 245 (void) pthread_mutex_unlock(&mt); 246 return (0); 247 } 248 249 /* 250 * get to this point if major_version and service_ldom have not yet 251 * been determined 252 */ 253 busy_init = 1; 254 (void) pthread_mutex_unlock(&mt); 255 256 /* 257 * set defaults which correspond to the case of "LDOMS not 258 * available". note that these can (and will) also apply to 259 * non-sun4v machines. 260 */ 261 major_version = 0; 262 service_ldom = 0; 263 264 if (get_local_md_prop_value(lhp, MD_STR_PLATFORM, MD_STR_DOM_CAPABLE, 265 &domain_capable) == 0) { 266 267 /* 268 * LDOMS capable FW is installed; it should be ok to 269 * try to communicate with ldmd and if that fails/timesout 270 * then use libpri 271 */ 272 major_version = 1; 273 274 if ((ier = ldmsvcs_check_channel()) == 0) { 275 /* 276 * control ldom 277 * ldmfma channel between FMA and ldmd only exists 278 * on the control domain. 279 */ 280 service_ldom = 1; 281 } else if (ier == 1) { 282 /* 283 * guest ldom 284 * non-control ldom such as guest and io service ldom 285 */ 286 service_ldom = 0; 287 } 288 } 289 290 (void) pthread_mutex_lock(&mt); 291 lhp->major_version = major_version; 292 lhp->service_ldom = service_ldom; 293 busy_init = 0; 294 (void) pthread_mutex_unlock(&mt); 295 296 (void) pthread_cond_broadcast(&cv); 297 298 return (rc); 299 } 300 301 302 /* 303 * search the machine description for a "pid" entry (physical cpuid) and 304 * return the corresponding "id" entry (virtual cpuid). 305 * return -1 if not found. 306 * if the pid property does not exist in a cpu node, assume pid = id. 307 */ 308 static processorid_t 309 cpu_phys2virt(ldom_hdl_t *lhp, uint32_t cpuid) 310 { 311 char isa[MAXNAMELEN]; 312 md_t *mdp; 313 mde_cookie_t *listp; 314 ssize_t bufsize; 315 processorid_t vid; 316 uint64_t *bufp; 317 uint64_t pval, pid, id; 318 int num_nodes, ncpus, i; 319 320 (void) sysinfo(SI_MACHINE, isa, MAXNAMELEN); 321 322 if (strcmp(isa, "sun4v") != 0) 323 return ((processorid_t)cpuid); 324 325 /* 326 * convert the physical cpuid to a virtual cpuid 327 */ 328 if ((bufsize = get_local_core_md(lhp, &bufp)) < 1) 329 return (-1); 330 331 if ((mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) == NULL || 332 (num_nodes = md_node_count(mdp)) < 1) { 333 lhp->freep(bufp, bufsize); 334 return (-1); 335 } 336 337 listp = (mde_cookie_t *)lhp->allocp(sizeof (mde_cookie_t) * num_nodes); 338 ncpus = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, 339 md_find_name(mdp, "cpu"), md_find_name(mdp, "fwd"), listp); 340 341 vid = -1; 342 for (i = 0; i < ncpus; i++) { 343 if (md_get_prop_val(mdp, listp[i], "id", &pval) < 0) 344 pval = (uint64_t)-1; 345 id = pval; 346 347 /* if pid does not exist, assume pid=id */ 348 if (md_get_prop_val(mdp, listp[i], "pid", &pval) < 0) 349 pval = id; 350 pid = pval; 351 352 if (pid == (uint64_t)cpuid) { 353 /* Found the entry */ 354 vid = (processorid_t)id; 355 break; 356 } 357 } 358 359 lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes); 360 (void) md_fini(mdp); 361 lhp->freep(bufp, bufsize); 362 363 return (vid); 364 } 365 366 int 367 ldom_fmri_status(ldom_hdl_t *lhp, nvlist_t *nvl) 368 { 369 char *name; 370 int ret = ENOTSUP; 371 372 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 373 return (EINVAL); 374 375 /* 376 * ldom_ldmd_is_up can only be true if ldom_major_version() 377 * returned 1 earlier; the major version is constant for the 378 * life of the client process 379 */ 380 381 if (!ldom_ldmd_is_up) { 382 /* Zeus is unavail; use local routines for status/retire */ 383 384 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 385 processorid_t vid; 386 uint32_t cpuid; 387 388 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) 389 == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1) 390 return (p_online(vid, P_STATUS)); 391 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 392 fmd_agent_hdl_t *hdl; 393 int err; 394 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) { 395 err = errno; 396 } else { 397 err = fmd_agent_page_isretired(hdl, nvl); 398 if (err == FMD_AGENT_RETIRE_DONE) 399 err = 0; 400 else 401 err = fmd_agent_errno(hdl); 402 fmd_agent_close(hdl); 403 } 404 return (err); 405 } 406 407 return (EINVAL); 408 } else { 409 /* Zeus is avail; use Zeus for status/retire */ 410 411 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 412 uint32_t cpuid; 413 414 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 415 &cpuid) == 0) 416 ret = ldmsvcs_cpu_req_status(lhp, cpuid); 417 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 418 uint64_t pa; 419 420 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 421 &pa) == 0) 422 ret = ldmsvcs_mem_req_status(lhp, pa); 423 else 424 ret = EINVAL; 425 } 426 return (ret); 427 } 428 } 429 430 431 int 432 ldom_fmri_retire(ldom_hdl_t *lhp, nvlist_t *nvl) 433 { 434 char *name; 435 int ret = ENOTSUP; 436 437 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 438 return (EINVAL); 439 440 /* 441 * ldom_ldmd_is_up can only be true if ldom_major_version() 442 * returned 1 earlier; the major version is constant for the 443 * life of the client process 444 */ 445 446 if (!ldom_ldmd_is_up) { 447 /* Zeus is unavail; use local routines for status/retire */ 448 449 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 450 processorid_t vid; 451 uint32_t cpuid; 452 453 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) 454 == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1) 455 return (p_online(vid, P_FAULTED)); 456 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 457 fmd_agent_hdl_t *hdl; 458 int err; 459 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) { 460 err = errno; 461 } else { 462 err = fmd_agent_page_retire(hdl, nvl); 463 if (err == FMD_AGENT_RETIRE_DONE) 464 err = 0; 465 else 466 err = fmd_agent_errno(hdl); 467 fmd_agent_close(hdl); 468 } 469 return (err); 470 } 471 472 return (EINVAL); 473 } else { 474 /* Zeus is avail; use Zeus for status/retire */ 475 476 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 477 uint32_t cpuid; 478 479 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 480 &cpuid) == 0) 481 ret = ldmsvcs_cpu_req_offline(lhp, cpuid); 482 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 483 uint64_t pa; 484 485 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 486 &pa) == 0) 487 ret = ldmsvcs_mem_req_retire(lhp, pa); 488 else 489 ret = EINVAL; 490 } 491 return (ret); 492 } 493 } 494 495 int 496 ldom_fmri_unretire(ldom_hdl_t *lhp, nvlist_t *nvl) 497 { 498 char *name; 499 int ret = ENOTSUP; 500 501 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 502 return (EINVAL); 503 504 /* 505 * ldom_ldmd_is_up can only be true if ldom_major_version() 506 * returned 1 earlier; the major version is constant for the 507 * life of the client process 508 */ 509 510 if (!ldom_ldmd_is_up) { 511 /* Zeus is unavail; use local routines for status/retire */ 512 513 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 514 processorid_t vid; 515 uint32_t cpuid; 516 517 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) 518 == 0 && (vid = cpu_phys2virt(lhp, cpuid)) != -1) 519 return (p_online(vid, P_ONLINE)); 520 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 521 fmd_agent_hdl_t *hdl; 522 int err; 523 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) { 524 err = errno; 525 } else { 526 err = fmd_agent_page_unretire(hdl, nvl); 527 if (err == FMD_AGENT_RETIRE_DONE) 528 err = 0; 529 else 530 err = fmd_agent_errno(hdl); 531 fmd_agent_close(hdl); 532 } 533 return (err); 534 } 535 536 return (EINVAL); 537 } else { 538 /* Zeus is avail; use Zeus for status/retire */ 539 540 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 541 uint32_t cpuid; 542 543 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 544 &cpuid) == 0) 545 ret = ldmsvcs_cpu_req_online(lhp, cpuid); 546 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 547 uint64_t pa; 548 549 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 550 &pa) == 0) 551 ret = ldmsvcs_mem_req_unretire(lhp, pa); 552 else 553 ret = EINVAL; 554 } 555 return (ret); 556 } 557 } 558 559 static int 560 fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl, int cmd) 561 { 562 char *name; 563 564 if (ldom_major_version(lhp) != 0) 565 return (0); 566 567 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 568 return (EINVAL); 569 570 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 571 bl_req_t blr; 572 char *class; 573 int fd, rc, err; 574 575 if ((nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) || 576 (class == NULL) || (*class == '\0')) 577 return (EINVAL); 578 579 if ((fd = open("/dev/bl", O_RDONLY)) < 0) 580 return (EIO); 581 582 if (nvlist_size(nvl, &blr.bl_fmrisz, NV_ENCODE_NATIVE) != 0 || 583 blr.bl_fmrisz == 0 || 584 (blr.bl_fmri = (caddr_t)lhp->allocp(blr.bl_fmrisz)) == 585 NULL) { 586 (void) close(fd); 587 return (EINVAL); 588 } 589 590 blr.bl_class = class; 591 592 rc = ioctl(fd, cmd, &blr); 593 err = errno; 594 595 lhp->freep((void *)&blr.bl_fmri, blr.bl_fmrisz); 596 (void) close(fd); 597 598 if (rc < 0 && err != ENOTSUP) { 599 errno = err; 600 return (-1); 601 } 602 } 603 604 return (0); 605 } 606 607 /* 608 * blacklist cpus in a non-LDOMS environment 609 */ 610 int 611 ldom_fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl) 612 { 613 return (fmri_blacklist(lhp, nvl, BLIOC_INSERT)); 614 } 615 616 /* 617 * unblacklist cpus 618 */ 619 int 620 ldom_fmri_unblacklist(ldom_hdl_t *lhp, nvlist_t *nvl) 621 { 622 return (fmri_blacklist(lhp, nvl, BLIOC_DELETE)); 623 } 624 625 626 ssize_t 627 ldom_get_core_md(ldom_hdl_t *lhp, uint64_t **buf) 628 { 629 ssize_t rv; /* return value */ 630 uint64_t tok; /* opaque PRI token */ 631 632 switch (ldom_major_version(lhp)) { 633 case 0: 634 /* pre LDOMS */ 635 rv = get_local_core_md(lhp, buf); 636 break; 637 case 1: 638 /* LDOMS 1.0 - Zeus and libpri usable only on service dom */ 639 if (ldom_on_service(lhp) == 1) { 640 if ((rv = ldmsvcs_get_core_md(lhp, buf)) < 1) { 641 (void) pthread_mutex_lock(&ldom_pri_lock); 642 rv = ldom_pri_get(PRI_GET, &tok, 643 buf, lhp->allocp, lhp->freep); 644 (void) pthread_mutex_unlock(&ldom_pri_lock); 645 } else { 646 ldom_ldmd_is_up = 1; 647 } 648 } else { 649 rv = get_local_core_md(lhp, buf); 650 } 651 break; 652 default: 653 rv = -1; 654 break; 655 } 656 657 return (rv); 658 } 659 660 /* 661 * version 0 means no LDOMS 662 */ 663 int 664 ldom_major_version(ldom_hdl_t *lhp) 665 { 666 if (lhp == NULL) 667 return (-1); 668 669 if (ldom_getinfo(lhp) == 0) 670 return (lhp->major_version); 671 else 672 return (0); 673 } 674 675 /* 676 * in the absence of ldoms we are on a single OS instance which is the 677 * equivalent of the service ldom 678 */ 679 int 680 ldom_on_service(ldom_hdl_t *lhp) 681 { 682 if (lhp == NULL) 683 return (-1); 684 685 if (ldom_getinfo(lhp) == 0) 686 return (lhp->service_ldom); 687 else 688 return (1); 689 } 690 691 692 ldom_hdl_t * 693 ldom_init(void *(*allocp)(size_t size), 694 void (*freep)(void *addr, size_t size)) 695 { 696 struct ldom_hdl *lhp; 697 698 (void) pthread_mutex_lock(&ldom_pri_lock); 699 700 if (ldom_pri_init() < 0) { 701 (void) pthread_mutex_unlock(&ldom_pri_lock); 702 return (NULL); 703 } 704 705 if ((lhp = allocp(sizeof (struct ldom_hdl))) == NULL) { 706 ldom_pri_fini(); 707 (void) pthread_mutex_unlock(&ldom_pri_lock); 708 return (NULL); 709 } 710 711 (void) pthread_mutex_unlock(&ldom_pri_lock); 712 713 lhp->major_version = -1; /* version not yet determined */ 714 lhp->allocp = allocp; 715 lhp->freep = freep; 716 717 ldmsvcs_init(lhp); 718 719 return (lhp); 720 } 721 722 723 void 724 ldom_fini(ldom_hdl_t *lhp) 725 { 726 if (lhp == NULL) 727 return; 728 729 ldmsvcs_fini(lhp); 730 lhp->freep(lhp, sizeof (struct ldom_hdl)); 731 732 (void) pthread_mutex_lock(&ldom_pri_lock); 733 734 ldom_pri_fini(); 735 736 (void) pthread_mutex_unlock(&ldom_pri_lock); 737 } 738 739 /* end file */ 740