o  rev 56646 : 8230848: OSContainer: Refactor container detection code
|  Summary: Move cgroups v1 implementation details out of osContainer_linux.cpp
~  Reviewed-by: bobv

   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 julong _unlimited_memory;
  58 
  59 class CgroupSubsystem: CHeapObj<mtInternal> {
  60  friend class OSContainer;
  61 
  62  private:
  63     /* mountinfo contents */
  64     char *_root;
  65     char *_mount_point;
  66 
  67     /* Constructed subsystem directory */
  68     char *_path;
  69 
  70  public:
  71     CgroupSubsystem(char *root, char *mountpoint) {
  72       _root = os::strdup(root);
  73       _mount_point = os::strdup(mountpoint);
  74       _path = NULL;
  75     }
  76 
  77     /*
  78      * Set directory to subsystem specific files based
  79      * on the contents of the mountinfo and cgroup files.
  80      */
  81     void set_subsystem_path(char *cgroup_path) {
  82       char buf[MAXPATHLEN+1];
  83       if (_root != NULL && cgroup_path != NULL) {
  84         if (strcmp(_root, "/") == 0) {
  85           int buflen;
  86           strncpy(buf, _mount_point, MAXPATHLEN);
  87           buf[MAXPATHLEN-1] = '\0';
  88           if (strcmp(cgroup_path,"/") != 0) {
  89             buflen = strlen(buf);
  90             if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) {
  91               return;
  92             }
  93             strncat(buf, cgroup_path, MAXPATHLEN-buflen);
  94             buf[MAXPATHLEN-1] = '\0';
  95           }
  96           _path = os::strdup(buf);
  97         } else {
  98           if (strcmp(_root, cgroup_path) == 0) {
  99             strncpy(buf, _mount_point, MAXPATHLEN);
 100             buf[MAXPATHLEN-1] = '\0';
 101             _path = os::strdup(buf);
 102           } else {
 103             char *p = strstr(cgroup_path, _root);
 104             if (p != NULL && p == _root) {
 105               if (strlen(cgroup_path) > strlen(_root)) {
 106                 int buflen;
 107                 strncpy(buf, _mount_point, MAXPATHLEN);
 108                 buf[MAXPATHLEN-1] = '\0';
 109                 buflen = strlen(buf);
 110                 if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) {
 111                   return;
 112                 }
 113                 strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen);
 114                 buf[MAXPATHLEN-1] = '\0';
 115                 _path = os::strdup(buf);
 116               }
 117             }
 118           }
 119         }
 120       }
 121     }
 122 
 123     char *subsystem_path() { return _path; }
 124 };
 125 
 126 class CgroupMemorySubsystem: CgroupSubsystem {
 127  friend class OSContainer;
 128 
 129  private:
 130     /* Some container runtimes set limits via cgroup
 131      * hierarchy. If set to true consider also memory.stat
 132      * file if everything else seems unlimited */
 133     bool _uses_mem_hierarchy;
 134     volatile jlong _memory_limit_in_bytes;
 135     volatile jlong _next_check_counter;
 136 
 137  public:
 138     CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) {
 139       _uses_mem_hierarchy = false;
 140       _memory_limit_in_bytes = -1;
 141       _next_check_counter = min_jlong;
 142 
 143     }
 144 
 145     bool is_hierarchical() { return _uses_mem_hierarchy; }
 146     void set_hierarchical(bool value) { _uses_mem_hierarchy = value; }
 147 
 148     bool should_check_memory_limit() {
 149       return os::elapsed_counter() > _next_check_counter;
 150     }
 151     jlong memory_limit_in_bytes() { return _memory_limit_in_bytes; }
 152     void set_memory_limit_in_bytes(jlong value) {
 153       _memory_limit_in_bytes = value;
 154       // max memory limit is unlikely to change, but we want to remain
 155       // responsive to configuration changes. A very short (20ms) grace time
 156       // between re-read avoids excessive overhead during startup without
 157       // significantly reducing the VMs ability to promptly react to reduced
 158       // memory availability
 159       _next_check_counter = os::elapsed_counter() + (NANOSECS_PER_SEC/50);
 160     }
 161 
 162 };
 163 
 164 CgroupMemorySubsystem* memory = NULL;
 165 CgroupSubsystem* cpuset = NULL;
 166 CgroupSubsystem* cpu = NULL;
 167 CgroupSubsystem* cpuacct = NULL;
 168 
 169 typedef char * cptr;
 170 
 171 PRAGMA_DIAG_PUSH
 172 PRAGMA_FORMAT_NONLITERAL_IGNORED
 173 template <typename T> int subsystem_file_line_contents(CgroupSubsystem* c,
 174                                               const char *filename,
 175                                               const char *matchline,
 176                                               const char *scan_fmt,
 177                                               T returnval) {
 178   FILE *fp = NULL;
 179   char *p;
 180   char file[MAXPATHLEN+1];
 181   char buf[MAXPATHLEN+1];
 182   char discard[MAXPATHLEN+1];
 183   bool found_match = false;
 184 
 185   if (c == NULL) {
 186     log_debug(os, container)("subsystem_file_line_contents: CgroupSubsytem* is NULL");
 187     return OSCONTAINER_ERROR;
 188   }
 189   if (c->subsystem_path() == NULL) {
 190     log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL");
 191     return OSCONTAINER_ERROR;
 192   }
 193 
 194   strncpy(file, c->subsystem_path(), MAXPATHLEN);
 195   file[MAXPATHLEN-1] = '\0';
 196   int filelen = strlen(file);
 197   if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {
 198     log_debug(os, container)("File path too long %s, %s", file, filename);
 199     return OSCONTAINER_ERROR;
 200   }
 201   strncat(file, filename, MAXPATHLEN-filelen);
 202   log_trace(os, container)("Path to %s is %s", filename, file);
 203   fp = fopen(file, "r");
 204   if (fp != NULL) {
 205     int err = 0;
 206     while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) {
 207       found_match = false;
 208       if (matchline == NULL) {
 209         // single-line file case
 210         int matched = sscanf(p, scan_fmt, returnval);
 211         found_match = (matched == 1);
 212       } else {
 213         // multi-line file case
 214         if (strstr(p, matchline) != NULL) {
 215           // discard matchline string prefix
 216           int matched = sscanf(p, scan_fmt, discard, returnval);
 217           found_match = (matched == 2);
 218         } else {
 219           continue; // substring not found
 220         }
 221       }
 222       if (found_match) {
 223         fclose(fp);
 224         return 0;
 225       } else {
 226         err = 1;
 227         log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);
 228       }
 229     }
 230     if (err == 0) {
 231       log_debug(os, container)("Empty file %s", file);
 232     }
 233   } else {
 234     log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));
 235   }
 236   if (fp != NULL)
 237     fclose(fp);
 238   return OSCONTAINER_ERROR;
 239 }
 240 PRAGMA_DIAG_POP
 241 
 242 #define GET_CONTAINER_INFO(return_type, subsystem, filename,              \
 243                            logstring, scan_fmt, variable)                 \
 244   return_type variable;                                                   \
 245 {                                                                         \
 246   int err;                                                                \
 247   err = subsystem_file_line_contents(subsystem,                           \
 248                                      filename,                            \
 249                                      NULL,                                \
 250                                      scan_fmt,                            \
 251                                      &variable);                          \
 252   if (err != 0)                                                           \
 253     return (return_type) OSCONTAINER_ERROR;                               \
 254                                                                           \
 255   log_trace(os, container)(logstring, variable);                          \
 256 }
 257 
 258 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename,         \
 259                                logstring, scan_fmt, variable, bufsize)    \
 260   char variable[bufsize];                                                 \
 261 {                                                                         \
 262   int err;                                                                \
 263   err = subsystem_file_line_contents(subsystem,                           \
 264                                      filename,                            \
 265                                      NULL,                                \
 266                                      scan_fmt,                            \
 267                                      variable);                           \
 268   if (err != 0)                                                           \
 269     return (return_type) NULL;                                            \
 270                                                                           \
 271   log_trace(os, container)(logstring, variable);                          \
 272 }
 273 
 274 #define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename,         \
 275                            matchline, logstring, scan_fmt, variable)      \
 276   return_type variable;                                                   \
 277 {                                                                         \
 278   int err;                                                                \
 279   err = subsystem_file_line_contents(subsystem,                           \
 280                                 filename,                                 \
 281                                 matchline,                                \
 282                                 scan_fmt,                                 \
 283                                 &variable);                               \
 284   if (err != 0)                                                           \
 285     return (return_type) OSCONTAINER_ERROR;                               \
 286                                                                           \
 287   log_trace(os, container)(logstring, variable);                          \
 288 }
 289 
 290 /* init
 291  *
 292  * Initialize the container support and determine if
 293  * we are running under cgroup control.
 294  */
 295 void OSContainer::init() {
 296   FILE *mntinfo = NULL;
 297   FILE *cgroup = NULL;
 298   char buf[MAXPATHLEN+1];
 299   char tmproot[MAXPATHLEN+1];
 300   char tmpmount[MAXPATHLEN+1];
 301   char *p;
 302   jlong mem_limit;
 303 
 304   assert(!_is_initialized, "Initializing OSContainer more than once");
 305 
 306   _is_initialized = true;
 307   _is_containerized = false;
 308 
 309   _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size();
 310 
 311   log_trace(os, container)("OSContainer::init: Initializing Container Support");
 312   if (!UseContainerSupport) {
 313     log_trace(os, container)("Container Support not enabled");
 314     return;
 315   }
 316 
 317   /*
 318    * Find the cgroup mount point for memory and cpuset
 319    * by reading /proc/self/mountinfo
 320    *
 321    * Example for docker:
 322    * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
 323    *
 324    * Example for host:
 325    * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
 326    */
 327   mntinfo = fopen("/proc/self/mountinfo", "r");
 328   if (mntinfo == NULL) {
 329       log_debug(os, container)("Can't open /proc/self/mountinfo, %s",
 330                                os::strerror(errno));
 331       return;
 332   }
 333 
 334   while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
 335     char tmpcgroups[MAXPATHLEN+1];
 336     char *cptr = tmpcgroups;
 337     char *token;
 338 
 339     // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
 340     if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) {
 341       continue;
 342     }
 343     while ((token = strsep(&cptr, ",")) != NULL) {
 344       if (strcmp(token, "memory") == 0) {
 345         memory = new CgroupMemorySubsystem(tmproot, tmpmount);
 346       } else if (strcmp(token, "cpuset") == 0) {
 347         cpuset = new CgroupSubsystem(tmproot, tmpmount);
 348       } else if (strcmp(token, "cpu") == 0) {
 349         cpu = new CgroupSubsystem(tmproot, tmpmount);
 350       } else if (strcmp(token, "cpuacct") == 0) {
 351         cpuacct= new CgroupSubsystem(tmproot, tmpmount);
 352       }
 353     }
 354   }
 355 
 356   fclose(mntinfo);
 357 
 358   if (memory == NULL) {
 359     log_debug(os, container)("Required cgroup memory subsystem not found");
 360     return;
 361   }
 362   if (cpuset == NULL) {
 363     log_debug(os, container)("Required cgroup cpuset subsystem not found");
 364     return;
 365   }
 366   if (cpu == NULL) {
 367     log_debug(os, container)("Required cgroup cpu subsystem not found");
 368     return;
 369   }
 370   if (cpuacct == NULL) {
 371     log_debug(os, container)("Required cgroup cpuacct subsystem not found");
 372     return;
 373   }
 374 
 375   /*
 376    * Read /proc/self/cgroup and map host mount point to
 377    * local one via /proc/self/mountinfo content above
 378    *
 379    * Docker example:
 380    * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044
 381    *
 382    * Host example:
 383    * 5:memory:/user.slice
 384    *
 385    * Construct a path to the process specific memory and cpuset
 386    * cgroup directory.
 387    *
 388    * For a container running under Docker from memory example above
 389    * the paths would be:
 390    *
 391    * /sys/fs/cgroup/memory
 392    *
 393    * For a Host from memory example above the path would be:
 394    *
 395    * /sys/fs/cgroup/memory/user.slice
 396    *
 397    */
 398   cgroup = fopen("/proc/self/cgroup", "r");
 399   if (cgroup == NULL) {
 400     log_debug(os, container)("Can't open /proc/self/cgroup, %s",
 401                              os::strerror(errno));
 402     return;
 403   }
 404 
 405   while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
 406     char *controllers;
 407     char *token;
 408     char *base;
 409 
 410     /* Skip cgroup number */
 411     strsep(&p, ":");
 412     /* Get controllers and base */
 413     controllers = strsep(&p, ":");
 414     base = strsep(&p, "\n");
 415 
 416     if (controllers == NULL) {
 417       continue;
 418     }
 419 
 420     while ((token = strsep(&controllers, ",")) != NULL) {
 421       if (strcmp(token, "memory") == 0) {
 422         memory->set_subsystem_path(base);
 423         jlong hierarchy = uses_mem_hierarchy();
 424         if (hierarchy > 0) {
 425           memory->set_hierarchical(true);
 426         }
 427       } else if (strcmp(token, "cpuset") == 0) {
 428         cpuset->set_subsystem_path(base);
 429       } else if (strcmp(token, "cpu") == 0) {
 430         cpu->set_subsystem_path(base);
 431       } else if (strcmp(token, "cpuacct") == 0) {
 432         cpuacct->set_subsystem_path(base);
 433       }
 434     }
 435   }
 436 
 437   fclose(cgroup);
 438 
 439   // We need to update the amount of physical memory now that
 440   // command line arguments have been processed.
 441   if ((mem_limit = memory_limit_in_bytes()) > 0) {
 442     os::Linux::set_physical_memory(mem_limit);
 443     log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit);
 444   }
 445 
 446   _is_containerized = true;
 447 
 448 }
 449 
 450 const char * OSContainer::container_type() {
 451   if (is_containerized()) {
 452     return "cgroupv1";
 453   } else {
 454     return NULL;
 455   }
 456 }
 457 
 458 /* uses_mem_hierarchy
 459  *
 460  * Return whether or not hierarchical cgroup accounting is being
 461  * done.
 462  *
 463  * return:
 464  *    A number > 0 if true, or
 465  *    OSCONTAINER_ERROR for not supported
 466  */
 467 jlong OSContainer::uses_mem_hierarchy() {
 468   GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy",
 469                     "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
 470   return use_hierarchy;
 471 }
 472 
 473 
 474 /* memory_limit_in_bytes
 475  *
 476  * Return the limit of available memory for this process.
 477  *
 478  * return:
 479  *    memory limit in bytes or
 480  *    -1 for unlimited
 481  *    OSCONTAINER_ERROR for not supported
 482  */
 483 jlong OSContainer::memory_limit_in_bytes() {
 484   if (!memory->should_check_memory_limit()) {
 485     return memory->memory_limit_in_bytes();
 486   }
 487   jlong memory_limit = read_memory_limit_in_bytes();
 488   // Update CgroupMemorySubsystem to avoid re-reading container settings too often
 489   memory->set_memory_limit_in_bytes(memory_limit);
 490   return memory_limit;
 491 }
 492 
 493 jlong OSContainer::read_memory_limit_in_bytes() {
 494   GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes",
 495                      "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
 496 
 497   if (memlimit >= _unlimited_memory) {
 498     log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
 499     if (memory->is_hierarchical()) {
 500       const char* matchline = "hierarchical_memory_limit";
 501       const char* format = "%s " JULONG_FORMAT;
 502       GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
 503                              "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit)
 504       if (hier_memlimit >= _unlimited_memory) {
 505         log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
 506       } else {
 507         return (jlong)hier_memlimit;
 508       }
 509     }
 510     return (jlong)-1;
 511   }
 512   else {
 513     return (jlong)memlimit;
 514   }
 515 }
 516 
 517 jlong OSContainer::memory_and_swap_limit_in_bytes() {
 518   GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes",
 519                      "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
 520   if (memswlimit >= _unlimited_memory) {
 521     log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
 522     if (memory->is_hierarchical()) {
 523       const char* matchline = "hierarchical_memsw_limit";
 524       const char* format = "%s " JULONG_FORMAT;
 525       GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
 526                              "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit)
 527       if (hier_memlimit >= _unlimited_memory) {
 528         log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
 529       } else {
 530         return (jlong)hier_memlimit;
 531       }
 532     }
 533     return (jlong)-1;
 534   } else {
 535     return (jlong)memswlimit;
 536   }
 537 }
 538 
 539 jlong OSContainer::memory_soft_limit_in_bytes() {
 540   GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes",
 541                      "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
 542   if (memsoftlimit >= _unlimited_memory) {
 543     log_trace(os, container)("Memory Soft Limit is: Unlimited");
 544     return (jlong)-1;
 545   } else {
 546     return (jlong)memsoftlimit;
 547   }
 548 }
 549 
 550 /* memory_usage_in_bytes
 551  *
 552  * Return the amount of used memory for this process.
 553  *
 554  * return:
 555  *    memory usage in bytes or
 556  *    -1 for unlimited
 557  *    OSCONTAINER_ERROR for not supported
 558  */
 559 jlong OSContainer::memory_usage_in_bytes() {
 560   GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes",
 561                      "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
 562   return memusage;
 563 }
 564 
 565 /* memory_max_usage_in_bytes
 566  *
 567  * Return the maximum amount of used memory for this process.
 568  *
 569  * return:
 570  *    max memory usage in bytes or
 571  *    OSCONTAINER_ERROR for not supported
 572  */
 573 jlong OSContainer::memory_max_usage_in_bytes() {
 574   GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes",
 575                      "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage);
 576   return memmaxusage;
 577 }
 578 
 579 /* active_processor_count
 580  *
 581  * Calculate an appropriate number of active processors for the
 582  * VM to use based on these three inputs.
 583  *
 584  * cpu affinity
 585  * cgroup cpu quota & cpu period
 586  * cgroup cpu shares
 587  *
 588  * Algorithm:
 589  *
 590  * Determine the number of available CPUs from sched_getaffinity
 591  *
 592  * If user specified a quota (quota != -1), calculate the number of
 593  * required CPUs by dividing quota by period.
 594  *
 595  * If shares are in effect (shares != -1), calculate the number
 596  * of CPUs required for the shares by dividing the share value
 597  * by PER_CPU_SHARES.
 598  *
 599  * All results of division are rounded up to the next whole number.
 600  *
 601  * If neither shares or quotas have been specified, return the
 602  * number of active processors in the system.
 603  *
 604  * If both shares and quotas have been specified, the results are
 605  * based on the flag PreferContainerQuotaForCPUCount.  If true,
 606  * return the quota value.  If false return the smallest value
 607  * between shares or quotas.
 608  *
 609  * If shares and/or quotas have been specified, the resulting number
 610  * returned will never exceed the number of active processors.
 611  *
 612  * return:
 613  *    number of CPUs
 614  */
 615 int OSContainer::active_processor_count() {
 616   int quota_count = 0, share_count = 0;
 617   int cpu_count, limit_count;
 618   int result;
 619 
 620   cpu_count = limit_count = os::Linux::active_processor_count();
 621   int quota  = cpu_quota();
 622   int period = cpu_period();
 623   int share  = cpu_shares();
 624 
 625   if (quota > -1 && period > 0) {
 626     quota_count = ceilf((float)quota / (float)period);
 627     log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
 628   }
 629   if (share > -1) {
 630     share_count = ceilf((float)share / (float)PER_CPU_SHARES);
 631     log_trace(os, container)("CPU Share count based on shares: %d", share_count);
 632   }
 633 
 634   // If both shares and quotas are setup results depend
 635   // on flag PreferContainerQuotaForCPUCount.
 636   // If true, limit CPU count to quota
 637   // If false, use minimum of shares and quotas
 638   if (quota_count !=0 && share_count != 0) {
 639     if (PreferContainerQuotaForCPUCount) {
 640       limit_count = quota_count;
 641     } else {
 642       limit_count = MIN2(quota_count, share_count);
 643     }
 644   } else if (quota_count != 0) {
 645     limit_count = quota_count;
 646   } else if (share_count != 0) {
 647     limit_count = share_count;
 648   }
 649 
 650   result = MIN2(cpu_count, limit_count);
 651   log_trace(os, container)("OSContainer::active_processor_count: %d", result);
 652   return result;
 653 }
 654 
 655 char * OSContainer::cpu_cpuset_cpus() {
 656   GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus",
 657                      "cpuset.cpus is: %s", "%1023s", cpus, 1024);
 658   return os::strdup(cpus);
 659 }
 660 
 661 char * OSContainer::cpu_cpuset_memory_nodes() {
 662   GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems",
 663                      "cpuset.mems is: %s", "%1023s", mems, 1024);
 664   return os::strdup(mems);





 665 }
 666 
 667 /* cpu_quota
 668  *
 669  * Return the number of milliseconds per period
 670  * process is guaranteed to run.
 671  *
 672  * return:
 673  *    quota time in milliseconds
 674  *    -1 for no quota
 675  *    OSCONTAINER_ERROR for not supported
 676  */
 677 int OSContainer::cpu_quota() {
 678   GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us",
 679                      "CPU Quota is: %d", "%d", quota);
 680   return quota;
 681 }
 682 
 683 int OSContainer::cpu_period() {
 684   GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us",
 685                      "CPU Period is: %d", "%d", period);
 686   return period;
 687 }
 688 
 689 /* cpu_shares
 690  *
 691  * Return the amount of cpu shares available to the process
 692  *
 693  * return:
 694  *    Share number (typically a number relative to 1024)
 695  *                 (2048 typically expresses 2 CPUs worth of processing)
 696  *    -1 for no share setup
 697  *    OSCONTAINER_ERROR for not supported
 698  */
 699 int OSContainer::cpu_shares() {
 700   GET_CONTAINER_INFO(int, cpu, "/cpu.shares",
 701                      "CPU Shares is: %d", "%d", shares);
 702   // Convert 1024 to no shares setup
 703   if (shares == 1024) return -1;
 704 
 705   return shares;
 706 }
--- EOF ---