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 // Four controllers: cpu, cpuset, cpuacct, memory 185 #define CG_INFO_LENGTH 4 186 187 class CgroupSubsystem: CHeapObj<mtInternal> { 188 friend class CgroupSubsystemFactory; 189 public: 190 int active_processor_count(int physical_proc_count); 191 192 virtual int cpu_quota(); 193 virtual int cpu_period(); 194 virtual int cpu_shares(); 195 virtual jlong memory_usage_in_bytes(); 196 virtual jlong memory_and_swap_limit_in_bytes(); 197 virtual jlong memory_soft_limit_in_bytes(); 198 virtual jlong memory_max_usage_in_bytes(); 199 virtual char * cpu_cpuset_cpus(); 200 virtual char * cpu_cpuset_memory_nodes(); 201 virtual jlong memory_limit_in_bytes(); 202 virtual const char * container_type(); 203 }; 204 205 class CgroupSubsystemFactory: AllStatic { 206 public: 207 static CgroupSubsystem* create(); 208 }; 209 210 // Class representing info in /proc/self/cgroup. 211 // See man 7 cgroups 212 class CgroupInfo : public StackObj { 213 friend class CgroupSubsystemFactory; 214 215 private: 216 char* _name; 217 int _hierarchy_id; 218 bool _enabled; 219 char* _cgroup_path; 220 221 }; 222 223 224 #endif // CGROUP_SUBSYSTEM_LINUX_HPP