1 /* 2 * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include <string.h> 26 #include <math.h> 27 #include <errno.h> 28 #include "utilities/globalDefinitions.hpp" 29 #include "memory/allocation.hpp" 30 #include "runtime/globals.hpp" 31 #include "runtime/os.hpp" 32 #include "logging/log.hpp" 33 #include "osContainer_linux.hpp" 34 35 /* 36 * PER_CPU_SHARES has been set to 1024 because CPU shares' quota 37 * is commonly used in cloud frameworks like Kubernetes[1], 38 * AWS[2] and Mesos[3] in a similar way. They spawn containers with 39 * --cpu-shares option values scaled by PER_CPU_SHARES. Thus, we do 40 * the inverse for determining the number of possible available 41 * CPUs to the JVM inside a container. See JDK-8216366. 42 * 43 * [1] https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu 44 * In particular: 45 * When using Docker: 46 * The spec.containers[].resources.requests.cpu is converted to its core value, which is potentially 47 * fractional, and multiplied by 1024. The greater of this number or 2 is used as the value of the 48 * --cpu-shares flag in the docker run command. 49 * [2] https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html 50 * [3] https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/docker/docker.cpp#L648 51 * https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp#L30 52 */ 53 #define PER_CPU_SHARES 1024 54 55 bool OSContainer::_is_initialized = false; 56 bool OSContainer::_is_containerized = false; 57 int OSContainer::_active_processor_count = 1; 58 julong _unlimited_memory; 59 60 class CgroupSubsystem: CHeapObj<mtInternal> { 61 friend class OSContainer; 62 63 64 private: 65 volatile jlong _next_check_counter; 66 67 /* mountinfo contents */ 68 char *_root; 69 char *_mount_point; 70 71 /* Constructed subsystem directory */ 72 char *_path; 73 74 public: 75 CgroupSubsystem(char *root, char *mountpoint) { 76 _root = os::strdup(root); 77 _mount_point = os::strdup(mountpoint); 78 _path = NULL; 79 _next_check_counter = min_jlong; 80 } 81 82 /* 83 * Set directory to subsystem specific files based 84 * on the contents of the mountinfo and cgroup files. 85 */ 86 void set_subsystem_path(char *cgroup_path) { 87 char buf[MAXPATHLEN+1]; 88 if (_root != NULL && cgroup_path != NULL) { 89 if (strcmp(_root, "/") == 0) { 90 int buflen; 91 strncpy(buf, _mount_point, MAXPATHLEN); 92 buf[MAXPATHLEN-1] = '\0'; 93 if (strcmp(cgroup_path,"/") != 0) { 94 buflen = strlen(buf); 95 if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { 96 return; 97 } 98 strncat(buf, cgroup_path, MAXPATHLEN-buflen); 99 buf[MAXPATHLEN-1] = '\0'; 100 } 101 _path = os::strdup(buf); 102 } else { 103 if (strcmp(_root, cgroup_path) == 0) { 104 strncpy(buf, _mount_point, MAXPATHLEN); 105 buf[MAXPATHLEN-1] = '\0'; 106 _path = os::strdup(buf); 107 } else { 108 char *p = strstr(cgroup_path, _root); 109 if (p != NULL && p == _root) { 110 if (strlen(cgroup_path) > strlen(_root)) { 111 int buflen; 112 strncpy(buf, _mount_point, MAXPATHLEN); 113 buf[MAXPATHLEN-1] = '\0'; 114 buflen = strlen(buf); 115 if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) { 116 return; 117 } 118 strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); 119 buf[MAXPATHLEN-1] = '\0'; 120 _path = os::strdup(buf); 121 } 122 } 123 } 124 } 125 } 126 } 127 128 char *subsystem_path() { return _path; } 129 130 bool check_cache_timeout() { 131 return os::elapsed_counter() > _next_check_counter; 132 } 133 134 void set_cache_timeout(jlong timeout) { 135 _next_check_counter = os::elapsed_counter() + timeout; 136 } 137 138 }; 139 140 class CgroupMemorySubsystem: CgroupSubsystem { 141 friend class OSContainer; 142 143 private: 144 /* Some container runtimes set limits via cgroup 145 * hierarchy. If set to true consider also memory.stat 146 * file if everything else seems unlimited */ 147 bool _uses_mem_hierarchy; 148 volatile jlong _memory_limit_in_bytes; 149 150 public: 151 CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) { 152 _uses_mem_hierarchy = false; 153 _memory_limit_in_bytes = -1; 154 155 } 156 157 bool is_hierarchical() { return _uses_mem_hierarchy; } 158 void set_hierarchical(bool value) { _uses_mem_hierarchy = value; } 159 160 jlong memory_limit_in_bytes() { return _memory_limit_in_bytes; } 161 void set_memory_limit_in_bytes(jlong value) { 162 _memory_limit_in_bytes = value; 163 // max memory limit is unlikely to change, but we want to remain 164 // responsive to configuration changes. A very short (20ms) grace time 165 // between re-read avoids excessive overhead during startup without 166 // significantly reducing the VMs ability to promptly react to reduced 167 // memory availability 168 set_cache_timeout(OSCONTAINER_CACHE_TIMEOUT); 169 } 170 171 }; 172 173 CgroupMemorySubsystem* memory = NULL; 174 CgroupSubsystem* cpuset = NULL; 175 CgroupSubsystem* cpu = NULL; 176 CgroupSubsystem* cpuacct = NULL; 177 178 typedef char * cptr; 179 180 PRAGMA_DIAG_PUSH 181 PRAGMA_FORMAT_NONLITERAL_IGNORED 182 template <typename T> int subsystem_file_line_contents(CgroupSubsystem* c, 183 const char *filename, 184 const char *matchline, 185 const char *scan_fmt, 186 T returnval) { 187 FILE *fp = NULL; 188 char *p; 189 char file[MAXPATHLEN+1]; 190 char buf[MAXPATHLEN+1]; 191 char discard[MAXPATHLEN+1]; 192 bool found_match = false; 193 194 if (c == NULL) { 195 log_debug(os, container)("subsystem_file_line_contents: CgroupSubsytem* is NULL"); 196 return OSCONTAINER_ERROR; 197 } 198 if (c->subsystem_path() == NULL) { 199 log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL"); 200 return OSCONTAINER_ERROR; 201 } 202 203 strncpy(file, c->subsystem_path(), MAXPATHLEN); 204 file[MAXPATHLEN-1] = '\0'; 205 int filelen = strlen(file); 206 if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { 207 log_debug(os, container)("File path too long %s, %s", file, filename); 208 return OSCONTAINER_ERROR; 209 } 210 strncat(file, filename, MAXPATHLEN-filelen); 211 log_trace(os, container)("Path to %s is %s", filename, file); 212 fp = fopen(file, "r"); 213 if (fp != NULL) { 214 int err = 0; 215 while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) { 216 found_match = false; 217 if (matchline == NULL) { 218 // single-line file case 219 int matched = sscanf(p, scan_fmt, returnval); 220 found_match = (matched == 1); 221 } else { 222 // multi-line file case 223 if (strstr(p, matchline) != NULL) { 224 // discard matchline string prefix 225 int matched = sscanf(p, scan_fmt, discard, returnval); 226 found_match = (matched == 2); 227 } else { 228 continue; // substring not found 229 } 230 } 231 if (found_match) { 232 fclose(fp); 233 return 0; 234 } else { 235 err = 1; 236 log_debug(os, container)("Type %s not found in file %s", scan_fmt, file); 237 } 238 } 239 if (err == 0) { 240 log_debug(os, container)("Empty file %s", file); 241 } 242 } else { 243 log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno)); 244 } 245 if (fp != NULL) 246 fclose(fp); 247 return OSCONTAINER_ERROR; 248 } 249 PRAGMA_DIAG_POP 250 251 #define GET_CONTAINER_INFO(return_type, subsystem, filename, \ 252 logstring, scan_fmt, variable) \ 253 return_type variable; \ 254 { \ 255 int err; \ 256 err = subsystem_file_line_contents(subsystem, \ 257 filename, \ 258 NULL, \ 259 scan_fmt, \ 260 &variable); \ 261 if (err != 0) \ 262 return (return_type) OSCONTAINER_ERROR; \ 263 \ 264 log_trace(os, container)(logstring, variable); \ 265 } 266 267 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ 268 logstring, scan_fmt, variable, bufsize) \ 269 char variable[bufsize]; \ 270 { \ 271 int err; \ 272 err = subsystem_file_line_contents(subsystem, \ 273 filename, \ 274 NULL, \ 275 scan_fmt, \ 276 variable); \ 277 if (err != 0) \ 278 return (return_type) NULL; \ 279 \ 280 log_trace(os, container)(logstring, variable); \ 281 } 282 283 #define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename, \ 284 matchline, logstring, scan_fmt, variable) \ 285 return_type variable; \ 286 { \ 287 int err; \ 288 err = subsystem_file_line_contents(subsystem, \ 289 filename, \ 290 matchline, \ 291 scan_fmt, \ 292 &variable); \ 293 if (err != 0) \ 294 return (return_type) OSCONTAINER_ERROR; \ 295 \ 296 log_trace(os, container)(logstring, variable); \ 297 } 298 299 /* init 300 * 301 * Initialize the container support and determine if 302 * we are running under cgroup control. 303 */ 304 void OSContainer::init() { 305 FILE *mntinfo = NULL; 306 FILE *cgroup = NULL; 307 char buf[MAXPATHLEN+1]; 308 char tmproot[MAXPATHLEN+1]; 309 char tmpmount[MAXPATHLEN+1]; 310 char *p; 311 jlong mem_limit; 312 313 assert(!_is_initialized, "Initializing OSContainer more than once"); 314 315 _is_initialized = true; 316 _is_containerized = false; 317 318 _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); 319 320 log_trace(os, container)("OSContainer::init: Initializing Container Support"); 321 if (!UseContainerSupport) { 322 log_trace(os, container)("Container Support not enabled"); 323 return; 324 } 325 326 /* 327 * Find the cgroup mount point for memory and cpuset 328 * by reading /proc/self/mountinfo 329 * 330 * Example for docker: 331 * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory 332 * 333 * Example for host: 334 * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory 335 */ 336 mntinfo = fopen("/proc/self/mountinfo", "r"); 337 if (mntinfo == NULL) { 338 log_debug(os, container)("Can't open /proc/self/mountinfo, %s", 339 os::strerror(errno)); 340 return; 341 } 342 343 while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { 344 char tmpcgroups[MAXPATHLEN+1]; 345 char *cptr = tmpcgroups; 346 char *token; 347 348 // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt 349 if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) { 350 continue; 351 } 352 while ((token = strsep(&cptr, ",")) != NULL) { 353 if (strcmp(token, "memory") == 0) { 354 memory = new CgroupMemorySubsystem(tmproot, tmpmount); 355 } else if (strcmp(token, "cpuset") == 0) { 356 cpuset = new CgroupSubsystem(tmproot, tmpmount); 357 } else if (strcmp(token, "cpu") == 0) { 358 cpu = new CgroupSubsystem(tmproot, tmpmount); 359 } else if (strcmp(token, "cpuacct") == 0) { 360 cpuacct= new CgroupSubsystem(tmproot, tmpmount); 361 } 362 } 363 } 364 365 fclose(mntinfo); 366 367 if (memory == NULL) { 368 log_debug(os, container)("Required cgroup memory subsystem not found"); 369 return; 370 } 371 if (cpuset == NULL) { 372 log_debug(os, container)("Required cgroup cpuset subsystem not found"); 373 return; 374 } 375 if (cpu == NULL) { 376 log_debug(os, container)("Required cgroup cpu subsystem not found"); 377 return; 378 } 379 if (cpuacct == NULL) { 380 log_debug(os, container)("Required cgroup cpuacct subsystem not found"); 381 return; 382 } 383 384 /* 385 * Read /proc/self/cgroup and map host mount point to 386 * local one via /proc/self/mountinfo content above 387 * 388 * Docker example: 389 * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 390 * 391 * Host example: 392 * 5:memory:/user.slice 393 * 394 * Construct a path to the process specific memory and cpuset 395 * cgroup directory. 396 * 397 * For a container running under Docker from memory example above 398 * the paths would be: 399 * 400 * /sys/fs/cgroup/memory 401 * 402 * For a Host from memory example above the path would be: 403 * 404 * /sys/fs/cgroup/memory/user.slice 405 * 406 */ 407 cgroup = fopen("/proc/self/cgroup", "r"); 408 if (cgroup == NULL) { 409 log_debug(os, container)("Can't open /proc/self/cgroup, %s", 410 os::strerror(errno)); 411 return; 412 } 413 414 while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { 415 char *controllers; 416 char *token; 417 char *base; 418 419 /* Skip cgroup number */ 420 strsep(&p, ":"); 421 /* Get controllers and base */ 422 controllers = strsep(&p, ":"); 423 base = strsep(&p, "\n"); 424 425 if (controllers == NULL) { 426 continue; 427 } 428 429 while ((token = strsep(&controllers, ",")) != NULL) { 430 if (strcmp(token, "memory") == 0) { 431 memory->set_subsystem_path(base); 432 jlong hierarchy = uses_mem_hierarchy(); 433 if (hierarchy > 0) { 434 memory->set_hierarchical(true); 435 } 436 } else if (strcmp(token, "cpuset") == 0) { 437 cpuset->set_subsystem_path(base); 438 } else if (strcmp(token, "cpu") == 0) { 439 cpu->set_subsystem_path(base); 440 } else if (strcmp(token, "cpuacct") == 0) { 441 cpuacct->set_subsystem_path(base); 442 } 443 } 444 } 445 446 fclose(cgroup); 447 448 // We need to update the amount of physical memory now that 449 // command line arguments have been processed. 450 if ((mem_limit = memory_limit_in_bytes()) > 0) { 451 os::Linux::set_physical_memory(mem_limit); 452 log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit); 453 } 454 455 _is_containerized = true; 456 457 } 458 459 const char * OSContainer::container_type() { 460 if (is_containerized()) { 461 return "cgroupv1"; 462 } else { 463 return NULL; 464 } 465 } 466 467 /* uses_mem_hierarchy 468 * 469 * Return whether or not hierarchical cgroup accounting is being 470 * done. 471 * 472 * return: 473 * A number > 0 if true, or 474 * OSCONTAINER_ERROR for not supported 475 */ 476 jlong OSContainer::uses_mem_hierarchy() { 477 GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy", 478 "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); 479 return use_hierarchy; 480 } 481 482 483 /* memory_limit_in_bytes 484 * 485 * Return the limit of available memory for this process. 486 * 487 * return: 488 * memory limit in bytes or 489 * -1 for unlimited 490 * OSCONTAINER_ERROR for not supported 491 */ 492 jlong OSContainer::memory_limit_in_bytes() { 493 if (!memory->check_cache_timeout()) { 494 return memory->memory_limit_in_bytes(); 495 } 496 jlong memory_limit = read_memory_limit_in_bytes(); 497 // Update CgroupMemorySubsystem to avoid re-reading container settings too often 498 memory->set_memory_limit_in_bytes(memory_limit); 499 return memory_limit; 500 } 501 502 jlong OSContainer::read_memory_limit_in_bytes() { 503 GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes", 504 "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); 505 506 if (memlimit >= _unlimited_memory) { 507 log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); 508 if (memory->is_hierarchical()) { 509 const char* matchline = "hierarchical_memory_limit"; 510 const char* format = "%s " JULONG_FORMAT; 511 GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, 512 "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit) 513 if (hier_memlimit >= _unlimited_memory) { 514 log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); 515 } else { 516 return (jlong)hier_memlimit; 517 } 518 } 519 return (jlong)-1; 520 } 521 else { 522 return (jlong)memlimit; 523 } 524 } 525 526 jlong OSContainer::memory_and_swap_limit_in_bytes() { 527 GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes", 528 "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); 529 if (memswlimit >= _unlimited_memory) { 530 log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); 531 if (memory->is_hierarchical()) { 532 const char* matchline = "hierarchical_memsw_limit"; 533 const char* format = "%s " JULONG_FORMAT; 534 GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, 535 "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit) 536 if (hier_memlimit >= _unlimited_memory) { 537 log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); 538 } else { 539 return (jlong)hier_memlimit; 540 } 541 } 542 return (jlong)-1; 543 } else { 544 return (jlong)memswlimit; 545 } 546 } 547 548 jlong OSContainer::memory_soft_limit_in_bytes() { 549 GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes", 550 "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); 551 if (memsoftlimit >= _unlimited_memory) { 552 log_trace(os, container)("Memory Soft Limit is: Unlimited"); 553 return (jlong)-1; 554 } else { 555 return (jlong)memsoftlimit; 556 } 557 } 558 559 /* memory_usage_in_bytes 560 * 561 * Return the amount of used memory for this process. 562 * 563 * return: 564 * memory usage in bytes or 565 * -1 for unlimited 566 * OSCONTAINER_ERROR for not supported 567 */ 568 jlong OSContainer::memory_usage_in_bytes() { 569 GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes", 570 "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); 571 return memusage; 572 } 573 574 /* memory_max_usage_in_bytes 575 * 576 * Return the maximum amount of used memory for this process. 577 * 578 * return: 579 * max memory usage in bytes or 580 * OSCONTAINER_ERROR for not supported 581 */ 582 jlong OSContainer::memory_max_usage_in_bytes() { 583 GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes", 584 "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage); 585 return memmaxusage; 586 } 587 588 /* active_processor_count 589 * 590 * Calculate an appropriate number of active processors for the 591 * VM to use based on these three inputs. 592 * 593 * cpu affinity 594 * cgroup cpu quota & cpu period 595 * cgroup cpu shares 596 * 597 * Algorithm: 598 * 599 * Determine the number of available CPUs from sched_getaffinity 600 * 601 * If user specified a quota (quota != -1), calculate the number of 602 * required CPUs by dividing quota by period. 603 * 604 * If shares are in effect (shares != -1), calculate the number 605 * of CPUs required for the shares by dividing the share value 606 * by PER_CPU_SHARES. 607 * 608 * All results of division are rounded up to the next whole number. 609 * 610 * If neither shares or quotas have been specified, return the 611 * number of active processors in the system. 612 * 613 * If both shares and quotas have been specified, the results are 614 * based on the flag PreferContainerQuotaForCPUCount. If true, 615 * return the quota value. If false return the smallest value 616 * between shares or quotas. 617 * 618 * If shares and/or quotas have been specified, the resulting number 619 * returned will never exceed the number of active processors. 620 * 621 * return: 622 * number of CPUs 623 */ 624 int OSContainer::active_processor_count() { 625 int quota_count = 0, share_count = 0; 626 int cpu_count, limit_count; 627 int result; 628 629 // We use a cache with a timeout to avoid performing expensive 630 // computations in the event this function is called frequently. 631 // [See 8227006]. 632 if (!cpu->check_cache_timeout()) { 633 log_trace(os, container)("OSContainer::active_processor_count (cached): %d", OSContainer::_active_processor_count); 634 return OSContainer::_active_processor_count; 635 } 636 637 cpu_count = limit_count = os::Linux::active_processor_count(); 638 int quota = cpu_quota(); 639 int period = cpu_period(); 640 int share = cpu_shares(); 641 642 if (quota > -1 && period > 0) { 643 quota_count = ceilf((float)quota / (float)period); 644 log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count); 645 } 646 if (share > -1) { 647 share_count = ceilf((float)share / (float)PER_CPU_SHARES); 648 log_trace(os, container)("CPU Share count based on shares: %d", share_count); 649 } 650 651 // If both shares and quotas are setup results depend 652 // on flag PreferContainerQuotaForCPUCount. 653 // If true, limit CPU count to quota 654 // If false, use minimum of shares and quotas 655 if (quota_count !=0 && share_count != 0) { 656 if (PreferContainerQuotaForCPUCount) { 657 limit_count = quota_count; 658 } else { 659 limit_count = MIN2(quota_count, share_count); 660 } 661 } else if (quota_count != 0) { 662 limit_count = quota_count; 663 } else if (share_count != 0) { 664 limit_count = share_count; 665 } 666 667 result = MIN2(cpu_count, limit_count); 668 log_trace(os, container)("OSContainer::active_processor_count: %d", result); 669 670 // Update the value and set the cache timeout to 20ms. 671 OSContainer::_active_processor_count = result; 672 cpu->set_cache_timeout(OSCONTAINER_CACHE_TIMEOUT); 673 674 return result; 675 } 676 677 char * OSContainer::cpu_cpuset_cpus() { 678 GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus", 679 "cpuset.cpus is: %s", "%1023s", cpus, 1024); 680 return os::strdup(cpus); 681 } 682 683 char * OSContainer::cpu_cpuset_memory_nodes() { 684 GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems", 685 "cpuset.mems is: %s", "%1023s", mems, 1024); 686 return os::strdup(mems); 687 } 688 689 /* cpu_quota 690 * 691 * Return the number of milliseconds per period 692 * process is guaranteed to run. 693 * 694 * return: 695 * quota time in milliseconds 696 * -1 for no quota 697 * OSCONTAINER_ERROR for not supported 698 */ 699 int OSContainer::cpu_quota() { 700 GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us", 701 "CPU Quota is: %d", "%d", quota); 702 return quota; 703 } 704 705 int OSContainer::cpu_period() { 706 GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us", 707 "CPU Period is: %d", "%d", period); 708 return period; 709 } 710 711 /* cpu_shares 712 * 713 * Return the amount of cpu shares available to the process 714 * 715 * return: 716 * Share number (typically a number relative to 1024) 717 * (2048 typically expresses 2 CPUs worth of processing) 718 * -1 for no share setup 719 * OSCONTAINER_ERROR for not supported 720 */ 721 int OSContainer::cpu_shares() { 722 GET_CONTAINER_INFO(int, cpu, "/cpu.shares", 723 "CPU Shares is: %d", "%d", shares); 724 // Convert 1024 to no shares setup 725 if (shares == 1024) return -1; 726 727 return shares; 728 }