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