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 2007 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 #include <stdio.h> 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <pthread.h> 34 #include <errno.h> 35 #include <libnvpair.h> 36 37 #include <sys/processor.h> 38 #include <sys/stat.h> 39 #include <sys/mdesc.h> 40 #include <sys/param.h> 41 #include <sys/systeminfo.h> 42 #include <sys/mem.h> 43 #include <sys/bl.h> 44 #include <sys/fm/protocol.h> 45 #include <fm/fmd_fmri.h> 46 #include <sys/pri.h> 47 48 #include "ldom.h" 49 #include "ldmsvcs_utils.h" 50 51 52 static ssize_t 53 get_local_core_md(ldom_hdl_t *lhp, uint64_t **buf) 54 { 55 int fh; 56 size_t size; 57 ssize_t ssize; 58 uint64_t tok; 59 uint64_t *bufp; 60 61 if ((ssize = pri_get(PRI_GET, &tok, buf, lhp->allocp, lhp->freep)) >= 0) 62 return (ssize); 63 64 if ((fh = open("/devices/pseudo/mdesc@0:mdesc", O_RDONLY, 0)) < 0) 65 return (-1); 66 67 if (ioctl(fh, MDESCIOCGSZ, &size) < 0) { 68 (void) close(fh); 69 return (-1); 70 } 71 72 bufp = (uint64_t *)lhp->allocp(size); 73 74 if (read(fh, bufp, size) < 0) { 75 lhp->freep(bufp, size); 76 (void) close(fh); 77 return (-1); 78 } 79 (void) close(fh); 80 81 *buf = bufp; 82 83 return ((ssize_t)size); 84 } 85 86 87 static int 88 ldom_getinfo(struct ldom_hdl *lhp) 89 { 90 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; 91 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 92 static int major_version = -1; 93 static int service_ldom = -1; 94 static int busy_init = 0; 95 96 int ier, rc = 0; 97 98 (void) pthread_mutex_lock(&mt); 99 100 while (busy_init == 1) 101 (void) pthread_cond_wait(&cv, &mt); 102 103 if (major_version != -1 && service_ldom != -1) { 104 lhp->major_version = major_version; 105 lhp->service_ldom = service_ldom; 106 (void) pthread_mutex_unlock(&mt); 107 return (0); 108 } 109 110 /* 111 * get to this point if major_version and service_ldom have not yet 112 * been determined 113 */ 114 busy_init = 1; 115 (void) pthread_mutex_unlock(&mt); 116 117 /* 118 * set defaults which correspond to the case of "LDOMS not 119 * available". note that these can (and will) also apply to 120 * non-sun4v machines. 121 */ 122 major_version = 0; 123 service_ldom = 1; 124 125 /* figure out version */ 126 if ((ier = ldmsvcs_check_channel()) == 0) { 127 /* 128 * get into this block if vldc exists. LDOMS is available 129 * and we are on the service LDOM. 130 */ 131 major_version = 1; 132 service_ldom = 1; 133 } else if (ier == 1) { 134 /* 135 * get into this block if vldc does not exist 136 * 137 * if we do not get into the following if() block [i.e., 138 * if (bufsiz <= 0)] then we are on a non-sun4v machine. 139 */ 140 uint64_t *bufp; 141 ssize_t bufsiz; 142 143 if ((bufsiz = get_local_core_md(lhp, &bufp)) > 0) { 144 md_t *mdp; 145 146 if ((mdp = md_init_intern(bufp, lhp->allocp, 147 lhp->freep)) != NULL) { 148 mde_cookie_t *listp; 149 uint64_t dval; 150 int num_nodes; 151 152 num_nodes = md_node_count(mdp); 153 listp = lhp->allocp(sizeof (mde_cookie_t) * 154 num_nodes); 155 156 /* 157 * if we do not enter the following if block, 158 * we conclude that LDOMS is not available 159 */ 160 if (md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, 161 md_find_name(mdp, "platform"), 162 md_find_name(mdp, "fwd"), 163 listp) > 0 && 164 md_get_prop_val(mdp, listp[0], 165 "domaining-enabled", &dval) >= 0 && 166 dval == 1) { 167 /* 168 * LDOMS is available. an earlier 169 * block detected the situation of 170 * being on a service LDOM, so 171 * we get to this point only if we 172 * are not on a service LDOM. 173 */ 174 major_version = 1; 175 service_ldom = 0; 176 } 177 178 lhp->freep(listp, sizeof (mde_cookie_t) * 179 num_nodes); 180 (void) md_fini(mdp); 181 } 182 183 lhp->freep(bufp, bufsiz); 184 } 185 } else { 186 rc = 1; 187 } 188 189 (void) pthread_mutex_lock(&mt); 190 lhp->major_version = major_version; 191 lhp->service_ldom = service_ldom; 192 busy_init = 0; 193 (void) pthread_mutex_unlock(&mt); 194 195 (void) pthread_cond_broadcast(&cv); 196 197 return (rc); 198 } 199 200 201 /* 202 * search the machine description for a "pid" entry (physical cpuid) and 203 * return the corresponding "id" entry (virtual cpuid) 204 */ 205 static processorid_t 206 cpu_phys2virt(ldom_hdl_t *lhp, uint32_t cpuid) 207 { 208 char isa[MAXNAMELEN]; 209 md_t *mdp; 210 mde_cookie_t *listp; 211 ssize_t bufsize; 212 processorid_t vid; 213 uint64_t *bufp; 214 uint64_t pval; 215 int num_nodes, ncpus, i; 216 217 (void) sysinfo(SI_ARCHITECTURE, isa, MAXNAMELEN); 218 219 if (strcmp(isa, "sun4v") != 0) 220 return ((processorid_t)cpuid); 221 222 /* 223 * convert the physical cpuid to a virtual cpuid 224 */ 225 if ((bufsize = ldom_get_core_md(lhp, &bufp)) < 1) 226 return (-1); 227 228 if ((mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) == NULL || 229 (num_nodes = md_node_count(mdp)) < 1) { 230 lhp->freep(bufp, bufsize); 231 return (-1); 232 } 233 234 listp = (mde_cookie_t *)lhp->allocp(sizeof (mde_cookie_t) * num_nodes); 235 ncpus = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, 236 md_find_name(mdp, "cpu"), 237 md_find_name(mdp, "fwd"), listp); 238 239 vid = -1; 240 for (i = 0; i < ncpus; i++) { 241 if (md_get_prop_val(mdp, listp[i], "pid", &pval) >= 0 && 242 pval == (uint64_t)cpuid) { 243 if (md_get_prop_val(mdp, listp[i], "id", &pval) >= 0) 244 vid = (processorid_t)pval; 245 246 break; 247 } 248 } 249 250 lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes); 251 (void) md_fini(mdp); 252 lhp->freep(bufp, bufsize); 253 254 return (vid); 255 } 256 257 /* 258 * if checking for status of a retired page: 259 * 0 - page is retired 260 * EAGAIN - page is scheduled for retirement 261 * EIO - page not scheduled for retirement 262 * EINVAL - error 263 * 264 * if retiring a page: 265 * 0 - success in retiring page 266 * EIO - page is already retired 267 * EAGAIN - page is scheduled for retirement 268 * EINVAL - error 269 * 270 * the original decoder for ioctl() return values is 271 * http://fma.eng/documents/engineering/cpumem/page_retire_api.txt 272 */ 273 static int 274 os_mem_page_retire(ldom_hdl_t *lhp, int cmd, nvlist_t *nvl) 275 { 276 mem_page_t mpage; 277 char *fmribuf; 278 size_t fmrisz; 279 int fd, rc, err; 280 281 if (cmd != MEM_PAGE_RETIRE && cmd != MEM_PAGE_FMRI_RETIRE && 282 cmd != MEM_PAGE_ISRETIRED && cmd != MEM_PAGE_FMRI_ISRETIRED) 283 return (EINVAL); 284 285 if ((fd = open("/dev/mem", O_RDONLY)) < 0) 286 return (EINVAL); 287 288 if ((errno = nvlist_size(nvl, &fmrisz, NV_ENCODE_NATIVE)) != 0 || 289 fmrisz > MEM_FMRI_MAX_BUFSIZE || 290 (fmribuf = lhp->allocp(fmrisz)) == NULL) { 291 (void) close(fd); 292 return (EINVAL); 293 } 294 295 if ((errno = nvlist_pack(nvl, &fmribuf, &fmrisz, 296 NV_ENCODE_NATIVE, 0)) != 0) { 297 lhp->freep(fmribuf, fmrisz); 298 (void) close(fd); 299 return (EINVAL); 300 } 301 302 mpage.m_fmri = fmribuf; 303 mpage.m_fmrisz = fmrisz; 304 305 rc = ioctl(fd, cmd, &mpage); 306 err = errno; 307 308 lhp->freep(fmribuf, fmrisz); 309 (void) close(fd); 310 311 if (rc < 0) { 312 rc = err; 313 } 314 315 return (rc); 316 } 317 318 int 319 ldom_fmri_status(ldom_hdl_t *lhp, nvlist_t *nvl) 320 { 321 char *name; 322 int ret; 323 324 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 325 return (EINVAL); 326 327 switch (ldom_major_version(lhp)) { 328 case 0: 329 /* 330 * version == 0 means LDOMS support is not available 331 */ 332 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 333 processorid_t vid; 334 uint32_t cpuid; 335 336 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 337 &cpuid) == 0 && 338 (vid = cpu_phys2virt(lhp, cpuid)) != -1) 339 return (p_online(vid, P_STATUS)); 340 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 341 return (os_mem_page_retire(lhp, 342 MEM_PAGE_FMRI_ISRETIRED, nvl)); 343 } 344 345 return (EINVAL); 346 /*NOTREACHED*/ 347 break; 348 case 1: 349 /* LDOMS 1.0 */ 350 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 351 uint32_t cpuid; 352 353 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 354 &cpuid) == 0) 355 ret = ldmsvcs_cpu_req_status(lhp, cpuid); 356 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 357 uint64_t pa; 358 359 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 360 &pa) == 0) 361 ret = ldmsvcs_mem_req_status(lhp, pa); 362 else 363 ret = EINVAL; 364 } else { 365 ret = ENOTSUP; 366 } 367 return (ret); 368 369 /*NOTREACHED*/ 370 break; 371 default: 372 break; 373 } 374 375 return (ENOTSUP); 376 } 377 378 379 int 380 ldom_fmri_retire(ldom_hdl_t *lhp, nvlist_t *nvl) 381 { 382 char *name; 383 int ret; 384 385 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 386 return (EINVAL); 387 388 switch (ldom_major_version(lhp)) { 389 case 0: 390 /* 391 * version == 0 means LDOMS support is not available 392 */ 393 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 394 processorid_t vid; 395 uint32_t cpuid; 396 397 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, 398 &cpuid) == 0 && 399 (vid = cpu_phys2virt(lhp, cpuid)) != -1) 400 return (p_online(vid, P_FAULTED)); 401 } else if (strcmp(name, FM_FMRI_SCHEME_MEM) == 0) { 402 return (os_mem_page_retire(lhp, 403 MEM_PAGE_FMRI_RETIRE, nvl)); 404 } 405 406 return (EINVAL); 407 /*NOTREACHED*/ 408 break; 409 case 1: 410 /* LDOMS 1.0 */ 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_offline(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_retire(lhp, pa); 423 else 424 ret = EINVAL; 425 } else { 426 ret = ENOTSUP; 427 } 428 return (ret); 429 430 /*NOTREACHED*/ 431 break; 432 default: 433 break; 434 } 435 436 return (ENOTSUP); 437 } 438 439 440 /* 441 * blacklist cpus in a non-LDOMS environment 442 */ 443 int 444 ldom_fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl) 445 { 446 char *name; 447 448 if (ldom_major_version(lhp) != 0) 449 return (0); 450 451 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) 452 return (EINVAL); 453 454 if (strcmp(name, FM_FMRI_SCHEME_CPU) == 0) { 455 bl_req_t blr; 456 char *class; 457 int fd, rc, err; 458 459 if ((nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) || 460 (class == NULL) || (*class == '\0')) 461 return (EINVAL); 462 463 if ((fd = open("/dev/bl", O_RDONLY)) < 0) 464 return (EIO); 465 466 if (nvlist_size(nvl, &blr.bl_fmrisz, NV_ENCODE_NATIVE) != 0 || 467 blr.bl_fmrisz == 0 || 468 (blr.bl_fmri = (caddr_t)lhp->allocp(blr.bl_fmrisz)) == 469 NULL) { 470 (void) close(fd); 471 return (EINVAL); 472 } 473 474 blr.bl_class = class; 475 476 rc = ioctl(fd, BLIOC_INSERT, &blr); 477 err = errno; 478 479 lhp->freep((void *)&blr.bl_fmri, blr.bl_fmrisz); 480 (void) close(fd); 481 482 if (rc < 0 && err != ENOTSUP) { 483 errno = err; 484 return (-1); 485 } 486 } 487 488 return (0); 489 } 490 491 492 ssize_t 493 ldom_get_core_md(ldom_hdl_t *lhp, uint64_t **buf) 494 { 495 ssize_t rv; /* return value */ 496 497 switch (ldom_major_version(lhp)) { 498 case 0: 499 return (get_local_core_md(lhp, buf)); 500 /*NOTREACHED*/ 501 break; 502 case 1: 503 /* LDOMS 1.0 */ 504 if (ldom_on_service(lhp) == 1) { 505 if ((rv = ldmsvcs_get_core_md(lhp, buf)) < 0) 506 rv = get_local_core_md(lhp, buf); 507 return (rv); 508 } else { 509 return (get_local_core_md(lhp, buf)); 510 } 511 512 /*NOTREACHED*/ 513 break; 514 default: 515 *buf = NULL; 516 break; 517 } 518 519 return (-1); 520 } 521 522 /* 523 * version 0 means no LDOMS 524 */ 525 int 526 ldom_major_version(ldom_hdl_t *lhp) 527 { 528 if (lhp == NULL) 529 return (-1); 530 531 if (ldom_getinfo(lhp) == 0) 532 return (lhp->major_version); 533 else 534 return (0); 535 } 536 537 /* 538 * in the absence of ldoms we are on a single OS instance which is the 539 * equivalent of the service ldom 540 */ 541 int 542 ldom_on_service(ldom_hdl_t *lhp) 543 { 544 if (lhp == NULL) 545 return (-1); 546 547 if (ldom_getinfo(lhp) == 0) 548 return (lhp->service_ldom); 549 else 550 return (1); 551 } 552 553 554 ldom_hdl_t * 555 ldom_init(void *(*allocp)(size_t size), 556 void (*freep)(void *addr, size_t size)) 557 { 558 struct ldom_hdl *lhp; 559 560 if (pri_init() < 0) 561 return (NULL); 562 563 if ((lhp = allocp(sizeof (struct ldom_hdl))) == NULL) { 564 pri_fini(); 565 return (NULL); 566 } 567 568 lhp->major_version = -1; /* version not yet determined */ 569 lhp->allocp = allocp; 570 lhp->freep = freep; 571 572 ldmsvcs_init(lhp); 573 574 return (lhp); 575 } 576 577 578 void 579 ldom_fini(ldom_hdl_t *lhp) 580 { 581 if (lhp == NULL) 582 return; 583 584 ldmsvcs_fini(lhp); 585 lhp->freep(lhp, sizeof (struct ldom_hdl)); 586 587 pri_fini(); 588 } 589 590 /* end file */ 591