1 /*
   2  * Copyright (c) 2019, Red Hat Inc.
   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 "cgroupV2Subsystem_linux.hpp"
  26 
  27 /* cpu_shares
  28  *
  29  * Return the amount of cpu shares available to the process
  30  *
  31  * return:
  32  *    Share number (typically a number relative to 1024)
  33  *                 (2048 typically expresses 2 CPUs worth of processing)
  34  *    -1 for no share setup
  35  *    OSCONTAINER_ERROR for not supported
  36  */
  37 int CgroupV2Subsystem::cpu_shares() {
  38   GET_CONTAINER_INFO(int, _unified, "/cpu.weight",
  39                      "CPU Shares is: %d", "%d", shares);
  40   // Convert default value of 100 to no shares setup
  41   if (shares == 100) return -1;
  42 
  43   // CPU shares (OCI) value needs to get translated into
  44   // a proper Cgroups v2 value. See:
  45   // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
  46   //
  47   // Use the inverse of (x == OCI value, y == cgroupsv2 value):
  48   // ((262142 * y - 1)/9999) + 2 = x
  49   //
  50   int x = 262142 * shares - 1;
  51   double frac = x/9999.0;
  52   x = ((int)frac) + 2;
  53   log_trace(os, container)("Scaled CPU Shares value is: %d", x);
  54   // Since the scaled value is not precise, return the closest
  55   // multiple of PER_CPU_SHARES for a more conservative mapping
  56   if ( x <= PER_CPU_SHARES ) {
  57      // will always map to 1 CPU
  58      return x;
  59   }
  60   int f = x/PER_CPU_SHARES;
  61   int lower_multiple = f * PER_CPU_SHARES;
  62   int upper_multiple = (f + 1) * PER_CPU_SHARES;
  63   int distance_lower = MAX2(lower_multiple, x) - MIN2(lower_multiple, x);
  64   int distance_upper = MAX2(upper_multiple, x) - MIN2(upper_multiple, x);
  65   x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
  66   log_trace(os, container)("Closest multiple of %d of the CPU Shares value is: %d", PER_CPU_SHARES, x);
  67   return x;
  68 }
  69 
  70 /* cpu_quota
  71  *
  72  * Return the number of milliseconds per period
  73  * process is guaranteed to run.
  74  *
  75  * return:
  76  *    quota time in milliseconds
  77  *    -1 for no quota
  78  *    OSCONTAINER_ERROR for not supported
  79  */
  80 int CgroupV2Subsystem::cpu_quota() {
  81   char * cpu_quota_str = cpu_quota_val();
  82   return (int)limit_from_str(cpu_quota_str);
  83 }
  84 
  85 char * CgroupV2Subsystem::cpu_cpuset_cpus() {
  86   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.cpus",
  87                      "cpuset.cpus is: %s", "%1023s", cpus, 1024);
  88   if (cpus == NULL) {
  89     return NULL;
  90   }
  91   return os::strdup(cpus);
  92 }
  93 
  94 char* CgroupV2Subsystem::cpu_quota_val() {
  95   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpu.max",
  96                      "CPU Quota is: %s", "%s %*d", quota, 1024);
  97   if (quota == NULL) {
  98     return NULL;
  99   }
 100   return os::strdup(quota);
 101 }
 102 
 103 char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() {
 104   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.mems",
 105                      "cpuset.mems is: %s", "%1023s", mems, 1024);
 106   if (mems == NULL) {
 107     return NULL;
 108   }
 109   return os::strdup(mems);
 110 }
 111 
 112 int CgroupV2Subsystem::cpu_period() {
 113   GET_CONTAINER_INFO(int, _unified, "/cpu.max",
 114                      "CPU Period is: %d", "%*s %d", period);
 115   return period;
 116 }
 117 
 118 /* memory_usage_in_bytes
 119  *
 120  * Return the amount of used memory used by this cgroup and decendents
 121  *
 122  * return:
 123  *    memory usage in bytes or
 124  *    -1 for unlimited
 125  *    OSCONTAINER_ERROR for not supported
 126  */
 127 jlong CgroupV2Subsystem::memory_usage_in_bytes() {
 128   GET_CONTAINER_INFO(jlong, _unified, "/memory.current",
 129                      "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
 130   return memusage;
 131 }
 132 
 133 jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() {
 134   char* mem_soft_limit_str = mem_soft_limit_val();
 135   return limit_from_str(mem_soft_limit_str);
 136 }
 137 
 138 jlong CgroupV2Subsystem::memory_max_usage_in_bytes() {
 139   return OSCONTAINER_ERROR; // Not supported for Cgroups V2.
 140 }
 141 
 142 char* CgroupV2Subsystem::mem_soft_limit_val() {
 143   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.high",
 144                          "Memory Soft Limit is: %s", "%s", mem_soft_limit_str, 1024);
 145   if (mem_soft_limit_str == NULL) {
 146     return NULL;
 147   }
 148   return os::strdup(mem_soft_limit_str);
 149 }
 150 
 151 jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() {
 152   char* mem_swp_limit_str = mem_swp_limit_val();
 153   return limit_from_str(mem_swp_limit_str);
 154 }
 155 
 156 char* CgroupV2Subsystem::mem_swp_limit_val() {
 157   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max",
 158                          "Memory and Swap Limit is: %s", "%s", mem_swp_limit_str, 1024);
 159   if (mem_swp_limit_str == NULL) {
 160     return NULL;
 161   }
 162   return os::strdup(mem_swp_limit_str);
 163 }
 164 
 165 /* memory_limit_in_bytes
 166  *
 167  * Return the limit of available memory for this process.
 168  *
 169  * return:
 170  *    memory limit in bytes or
 171  *    -1 for unlimited, OSCONTAINER_ERROR for an error
 172  */
 173 jlong CgroupV2Subsystem::memory_limit_in_bytes() {
 174   char * mem_limit_str = mem_limit_val();
 175   return limit_from_str(mem_limit_str);
 176 }
 177 
 178 jlong CgroupV2Subsystem::limit_from_str(char* limit_str) {
 179   if (limit_str == NULL) {
 180     return OSCONTAINER_ERROR;
 181   }
 182   // Unlimited memory in Cgroups V2 is the literal string 'max'
 183   if (strcmp("max", limit_str) == 0) {
 184     os::free(limit_str);
 185     return (jlong)-1;
 186   }
 187   julong limit;
 188   if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) {
 189     os::free(limit_str);
 190     return OSCONTAINER_ERROR;
 191   }
 192   os::free(limit_str);
 193   return (jlong)limit;
 194 }
 195 
 196 char* CgroupV2Subsystem::mem_limit_val() {
 197   GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max",
 198                          "Memory Limit is: %s", "%s", mem_limit_str, 1024);
 199   if (mem_limit_str == NULL) {
 200     return NULL;
 201   }
 202   return os::strdup(mem_limit_str);
 203 }
 204 
 205 char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) {
 206   char buf[MAXPATHLEN+1];
 207   int buflen;
 208   strncpy(buf, mount_path, MAXPATHLEN);
 209   buf[MAXPATHLEN] = '\0';
 210   buflen = strlen(buf);
 211   if ((buflen + strlen(cgroup_path)) > MAXPATHLEN) {
 212     return NULL;
 213   }
 214   strncat(buf, cgroup_path, MAXPATHLEN-buflen);
 215   buf[MAXPATHLEN] = '\0';
 216   return os::strdup(buf);
 217 }
 218