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