1 /*
   2  * Copyright (c) 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 #ifndef CGROUP_SUBSYSTEM_LINUX_HPP
  26 #define CGROUP_SUBSYSTEM_LINUX_HPP
  27 
  28 #include "memory/allocation.hpp"
  29 #include "runtime/os.hpp"
  30 #include "logging/log.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 #include "utilities/macros.hpp"
  33 #include "osContainer_linux.hpp"
  34 
  35 // Shared cgroups code (used by cgroup version 1 and version 2)
  36 
  37 /*
  38  * PER_CPU_SHARES has been set to 1024 because CPU shares' quota
  39  * is commonly used in cloud frameworks like Kubernetes[1],
  40  * AWS[2] and Mesos[3] in a similar way. They spawn containers with
  41  * --cpu-shares option values scaled by PER_CPU_SHARES. Thus, we do
  42  * the inverse for determining the number of possible available
  43  * CPUs to the JVM inside a container. See JDK-8216366.
  44  *
  45  * [1] https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
  46  *     In particular:
  47  *        When using Docker:
  48  *          The spec.containers[].resources.requests.cpu is converted to its core value, which is potentially
  49  *          fractional, and multiplied by 1024. The greater of this number or 2 is used as the value of the
  50  *          --cpu-shares flag in the docker run command.
  51  * [2] https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html
  52  * [3] https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/docker/docker.cpp#L648
  53  *     https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp#L30
  54  */
  55 #define PER_CPU_SHARES 1024
  56 
  57 typedef char * cptr;
  58 
  59 class CgroupController: CHeapObj<mtInternal> {
  60   friend class CgroupSubsystemFactory;
  61   public:
  62     virtual char *subsystem_path();
  63 };
  64 
  65 PRAGMA_DIAG_PUSH
  66 PRAGMA_FORMAT_NONLITERAL_IGNORED
  67 template <typename T> int subsystem_file_line_contents(CgroupController* c,
  68                                               const char *filename,
  69                                               const char *matchline,
  70                                               const char *scan_fmt,
  71                                               T returnval) {
  72   FILE *fp = NULL;
  73   char *p;
  74   char file[MAXPATHLEN+1];
  75   char buf[MAXPATHLEN+1];
  76   char discard[MAXPATHLEN+1];
  77   bool found_match = false;
  78 
  79   if (c == NULL) {
  80     log_debug(os, container)("subsystem_file_line_contents: CgroupController* is NULL");
  81     return OSCONTAINER_ERROR;
  82   }
  83   if (c->subsystem_path() == NULL) {
  84     log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL");
  85     return OSCONTAINER_ERROR;
  86   }
  87 
  88   strncpy(file, c->subsystem_path(), MAXPATHLEN);
  89   file[MAXPATHLEN-1] = '\0';
  90   int filelen = strlen(file);
  91   if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {
  92     log_debug(os, container)("File path too long %s, %s", file, filename);
  93     return OSCONTAINER_ERROR;
  94   }
  95   strncat(file, filename, MAXPATHLEN-filelen);
  96   log_trace(os, container)("Path to %s is %s", filename, file);
  97   fp = fopen(file, "r");
  98   if (fp != NULL) {
  99     int err = 0;
 100     while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) {
 101       found_match = false;
 102       if (matchline == NULL) {
 103         // single-line file case
 104         int matched = sscanf(p, scan_fmt, returnval);
 105         found_match = (matched == 1);
 106       } else {
 107         // multi-line file case
 108         if (strstr(p, matchline) != NULL) {
 109           // discard matchline string prefix
 110           int matched = sscanf(p, scan_fmt, discard, returnval);
 111           found_match = (matched == 2);
 112         } else {
 113           continue; // substring not found
 114         }
 115       }
 116       if (found_match) {
 117         fclose(fp);
 118         return 0;
 119       } else {
 120         err = 1;
 121         log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);
 122       }
 123     }
 124     if (err == 0) {
 125       log_debug(os, container)("Empty file %s", file);
 126     }
 127   } else {
 128     log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));
 129   }
 130   if (fp != NULL)
 131     fclose(fp);
 132   return OSCONTAINER_ERROR;
 133 }
 134 PRAGMA_DIAG_POP
 135 
 136 #define GET_CONTAINER_INFO(return_type, subsystem, filename,              \
 137                            logstring, scan_fmt, variable)                 \
 138   return_type variable;                                                   \
 139 {                                                                         \
 140   int err;                                                                \
 141   err = subsystem_file_line_contents(subsystem,                           \
 142                                      filename,                            \
 143                                      NULL,                                \
 144                                      scan_fmt,                            \
 145                                      &variable);                          \
 146   if (err != 0)                                                           \
 147     return (return_type) OSCONTAINER_ERROR;                               \
 148                                                                           \
 149   log_trace(os, container)(logstring, variable);                          \
 150 }
 151 
 152 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename,         \
 153                                logstring, scan_fmt, variable, bufsize)    \
 154   char variable[bufsize];                                                 \
 155 {                                                                         \
 156   int err;                                                                \
 157   err = subsystem_file_line_contents(subsystem,                           \
 158                                      filename,                            \
 159                                      NULL,                                \
 160                                      scan_fmt,                            \
 161                                      variable);                           \
 162   if (err != 0)                                                           \
 163     return (return_type) NULL;                                            \
 164                                                                           \
 165   log_trace(os, container)(logstring, variable);                          \
 166 }
 167 
 168 #define GET_CONTAINER_INFO_LINE(return_type, controller, filename,        \
 169                            matchline, logstring, scan_fmt, variable)      \
 170   return_type variable;                                                   \
 171 {                                                                         \
 172   int err;                                                                \
 173   err = subsystem_file_line_contents(controller,                          \
 174                                 filename,                                 \
 175                                 matchline,                                \
 176                                 scan_fmt,                                 \
 177                                 &variable);                               \
 178   if (err != 0)                                                           \
 179     return (return_type) OSCONTAINER_ERROR;                               \
 180                                                                           \
 181   log_trace(os, container)(logstring, variable);                          \
 182 }
 183 
 184 class CgroupSubsystem: CHeapObj<mtInternal> {
 185   friend class CgroupSubsystemFactory;
 186   public:
 187     int active_processor_count(int physical_proc_count);
 188 
 189     virtual int cpu_quota();
 190     virtual int cpu_period();
 191     virtual int cpu_shares();
 192     virtual jlong memory_usage_in_bytes();
 193     virtual jlong memory_and_swap_limit_in_bytes();
 194     virtual jlong memory_soft_limit_in_bytes();
 195     virtual jlong memory_max_usage_in_bytes();
 196     virtual char * cpu_cpuset_cpus();
 197     virtual char * cpu_cpuset_memory_nodes();
 198     virtual jlong memory_limit_in_bytes();
 199     virtual const char * container_type();
 200 };
 201 
 202 class CgroupSubsystemFactory: AllStatic {
 203   public:
 204     static CgroupSubsystem* create();
 205 };
 206 
 207 #endif // CGROUP_SUBSYSTEM_LINUX_HPP