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