@  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 ---