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 }