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 #include <string.h>
  26 #include <math.h>
  27 #include <errno.h>
  28 #include "cgroupV1Subsystem_linux.hpp"
  29 #include "logging/log.hpp"
  30 #include "memory/allocation.hpp"
  31 #include "runtime/globals.hpp"
  32 #include "runtime/os.hpp"
  33 #include "utilities/globalDefinitions.hpp"
  34 
  35 /*
  36  * Set directory to subsystem specific files based
  37  * on the contents of the mountinfo and cgroup files.
  38  */
  39 void CgroupV1Controller::set_subsystem_path(char *cgroup_path) {
  40   char buf[MAXPATHLEN+1];
  41   if (_root != NULL && cgroup_path != NULL) {
  42     if (strcmp(_root, "/") == 0) {
  43       int buflen;
  44       strncpy(buf, _mount_point, MAXPATHLEN);
  45       buf[MAXPATHLEN-1] = '\0';
  46       if (strcmp(cgroup_path,"/") != 0) {
  47         buflen = strlen(buf);
  48         if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) {
  49           return;
  50         }
  51         strncat(buf, cgroup_path, MAXPATHLEN-buflen);
  52         buf[MAXPATHLEN-1] = '\0';
  53       }
  54       _path = os::strdup(buf);
  55     } else {
  56       if (strcmp(_root, cgroup_path) == 0) {
  57         strncpy(buf, _mount_point, MAXPATHLEN);
  58         buf[MAXPATHLEN-1] = '\0';
  59         _path = os::strdup(buf);
  60       } else {
  61         char *p = strstr(cgroup_path, _root);
  62         if (p != NULL && p == _root) {
  63           if (strlen(cgroup_path) > strlen(_root)) {
  64             int buflen;
  65             strncpy(buf, _mount_point, MAXPATHLEN);
  66             buf[MAXPATHLEN-1] = '\0';
  67             buflen = strlen(buf);
  68             if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) {
  69               return;
  70             }
  71             strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen);
  72             buf[MAXPATHLEN-1] = '\0';
  73             _path = os::strdup(buf);
  74           }
  75         }
  76       }
  77     }
  78   }
  79 }
  80 
  81 /* uses_mem_hierarchy
  82  *
  83  * Return whether or not hierarchical cgroup accounting is being
  84  * done.
  85  *
  86  * return:
  87  *    A number > 0 if true, or
  88  *    OSCONTAINER_ERROR for not supported
  89  */
  90 jlong CgroupV1MemoryController::uses_mem_hierarchy() {
  91   GET_CONTAINER_INFO(jlong, this, "/memory.use_hierarchy",
  92                     "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
  93   return use_hierarchy;
  94 }
  95 
  96 void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) {
  97   CgroupV1Controller::set_subsystem_path(cgroup_path);
  98   jlong hierarchy = uses_mem_hierarchy();
  99   if (hierarchy > 0) {
 100     set_hierarchical(true);
 101   }
 102 }
 103 
 104 jlong CgroupV1Subsystem::read_memory_limit_in_bytes() {
 105   GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.limit_in_bytes",
 106                      "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
 107 
 108   if (memlimit >= _unlimited_memory) {
 109     log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
 110     CgroupV1MemoryController* mem_controller = reinterpret_cast<CgroupV1MemoryController*>(_memory->controller());
 111     if (mem_controller->is_hierarchical()) {
 112       const char* matchline = "hierarchical_memory_limit";
 113       const char* format = "%s " JULONG_FORMAT;
 114       GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline,
 115                              "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit)
 116       if (hier_memlimit >= _unlimited_memory) {
 117         log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
 118       } else {
 119         return (jlong)hier_memlimit;
 120       }
 121     }
 122     return (jlong)-1;
 123   }
 124   else {
 125     return (jlong)memlimit;
 126   }
 127 }
 128 
 129 jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() {
 130   GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.limit_in_bytes",
 131                      "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
 132   if (memswlimit >= _unlimited_memory) {
 133     log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
 134     CgroupV1MemoryController* mem_controller = reinterpret_cast<CgroupV1MemoryController*>(_memory->controller());
 135     if (mem_controller->is_hierarchical()) {
 136       const char* matchline = "hierarchical_memsw_limit";
 137       const char* format = "%s " JULONG_FORMAT;
 138       GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline,
 139                              "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit)
 140       if (hier_memlimit >= _unlimited_memory) {
 141         log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
 142       } else {
 143         return (jlong)hier_memlimit;
 144       }
 145     }
 146     return (jlong)-1;
 147   } else {
 148     return (jlong)memswlimit;
 149   }
 150 }
 151 
 152 jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() {
 153   GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.soft_limit_in_bytes",
 154                      "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
 155   if (memsoftlimit >= _unlimited_memory) {
 156     log_trace(os, container)("Memory Soft Limit is: Unlimited");
 157     return (jlong)-1;
 158   } else {
 159     return (jlong)memsoftlimit;
 160   }
 161 }
 162 
 163 /* memory_usage_in_bytes
 164  *
 165  * Return the amount of used memory for this process.
 166  *
 167  * return:
 168  *    memory usage in bytes or
 169  *    -1 for unlimited
 170  *    OSCONTAINER_ERROR for not supported
 171  */
 172 jlong CgroupV1Subsystem::memory_usage_in_bytes() {
 173   GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.usage_in_bytes",
 174                      "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
 175   return memusage;
 176 }
 177 
 178 /* memory_max_usage_in_bytes
 179  *
 180  * Return the maximum amount of used memory for this process.
 181  *
 182  * return:
 183  *    max memory usage in bytes or
 184  *    OSCONTAINER_ERROR for not supported
 185  */
 186 jlong CgroupV1Subsystem::memory_max_usage_in_bytes() {
 187   GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.max_usage_in_bytes",
 188                      "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage);
 189   return memmaxusage;
 190 }
 191 
 192 char * CgroupV1Subsystem::cpu_cpuset_cpus() {
 193   GET_CONTAINER_INFO_CPTR(cptr, _cpuset, "/cpuset.cpus",
 194                      "cpuset.cpus is: %s", "%1023s", cpus, 1024);
 195   return os::strdup(cpus);
 196 }
 197 
 198 char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() {
 199   GET_CONTAINER_INFO_CPTR(cptr, _cpuset, "/cpuset.mems",
 200                      "cpuset.mems is: %s", "%1023s", mems, 1024);
 201   return os::strdup(mems);
 202 }
 203 
 204 /* cpu_quota
 205  *
 206  * Return the number of milliseconds per period
 207  * process is guaranteed to run.
 208  *
 209  * return:
 210  *    quota time in milliseconds
 211  *    -1 for no quota
 212  *    OSCONTAINER_ERROR for not supported
 213  */
 214 int CgroupV1Subsystem::cpu_quota() {
 215   GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_quota_us",
 216                      "CPU Quota is: %d", "%d", quota);
 217   return quota;
 218 }
 219 
 220 int CgroupV1Subsystem::cpu_period() {
 221   GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_period_us",
 222                      "CPU Period is: %d", "%d", period);
 223   return period;
 224 }
 225 
 226 /* cpu_shares
 227  *
 228  * Return the amount of cpu shares available to the process
 229  *
 230  * return:
 231  *    Share number (typically a number relative to 1024)
 232  *                 (2048 typically expresses 2 CPUs worth of processing)
 233  *    -1 for no share setup
 234  *    OSCONTAINER_ERROR for not supported
 235  */
 236 int CgroupV1Subsystem::cpu_shares() {
 237   GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.shares",
 238                      "CPU Shares is: %d", "%d", shares);
 239   // Convert 1024 to no shares setup
 240   if (shares == 1024) return -1;
 241 
 242   return shares;
 243 }