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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.platform.cgroupv2; 27 28 import java.util.concurrent.TimeUnit; 29 30 import jdk.internal.platform.CgroupSubsystem; 31 import jdk.internal.platform.CgroupSubsystemController; 32 33 public class CgroupV2Subsystem implements CgroupSubsystem { 34 35 private final CgroupSubsystemController unified; 36 private static final String PROVIDER_NAME = "cgroupv2"; 37 private static final int PER_CPU_SHARES = 1024; 38 private static final String MAX_VAL = "max"; 39 private static final Object EMPTY_STR = ""; 40 41 private volatile long memoryMaxUsage = Long.MIN_VALUE; 42 43 public CgroupV2Subsystem(CgroupSubsystemController unified) { 44 this.unified = unified; 45 } 46 47 private long getLongVal(String file) { 48 return CgroupSubsystemController.getLongValue(unified, 49 file, 50 CgroupV2SubsystemController::convertStringToLong); 51 } 52 53 @Override 54 public String getProvider() { 55 return PROVIDER_NAME; 56 } 57 58 @Override 59 public long getCpuUsage() { 60 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec"); 61 return TimeUnit.MICROSECONDS.toNanos(micros); 62 } 63 64 @Override 65 public long[] getPerCpuUsage() { 66 // Not supported 67 return new long[0]; 68 } 69 70 @Override 71 public long getCpuUserUsage() { 72 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "user_usec"); 73 return TimeUnit.MICROSECONDS.toNanos(micros); 74 } 75 76 @Override 77 public long getCpuSystemUsage() { 78 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "system_usec"); 79 return TimeUnit.MICROSECONDS.toNanos(micros); 80 } 81 82 @Override 83 public long getCpuPeriod() { 84 return getFromCpuMax(1 /* $PERIOD index */); 85 } 86 87 @Override 88 public long getCpuQuota() { 89 return getFromCpuMax(0 /* $MAX index */); 90 } 91 92 private long getFromCpuMax(int tokenIdx) { 93 String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max"); 94 if (cpuMaxRaw == null) { 95 // likely file not found 96 return CgroupSubsystemController.RETVAL_UNLIMITED; 97 } 98 // $MAX $PERIOD 99 String[] tokens = cpuMaxRaw.split("\\s+"); 100 if (tokens.length != 2) { 101 return CgroupSubsystemController.RETVAL_ERROR; 102 } 103 String quota = tokens[tokenIdx]; 104 return limitFromString(quota); 105 } 106 107 private long limitFromString(String strVal) { 108 if (MAX_VAL.equals(strVal)) { 109 return CgroupSubsystemController.RETVAL_UNLIMITED; 110 } 111 return Long.parseLong(strVal); 112 } 113 114 @Override 115 public long getCpuShares() { 116 long sharesRaw = getLongVal("cpu.weight"); 117 if (sharesRaw == 100 || sharesRaw == 0) { 118 return CgroupSubsystemController.RETVAL_UNLIMITED; 119 } 120 int shares = (int)sharesRaw; 121 // CPU shares (OCI) value needs to get translated into 122 // a proper Cgroups v2 value. See: 123 // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller 124 // 125 // Use the inverse of (x == OCI value, y == cgroupsv2 value): 126 // ((262142 * y - 1)/9999) + 2 = x 127 // 128 int x = 262142 * shares - 1; 129 double frac = x/9999.0; 130 x = ((int)frac) + 2; 131 if ( x <= PER_CPU_SHARES ) { 132 return PER_CPU_SHARES; // mimic cgroups v1 133 } 134 int f = x/PER_CPU_SHARES; 135 int lower_multiple = f * PER_CPU_SHARES; 136 int upper_multiple = (f + 1) * PER_CPU_SHARES; 137 int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x); 138 int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x); 139 x = distance_lower <= distance_upper ? lower_multiple : upper_multiple; 140 return x; 141 } 142 143 @Override 144 public long getCpuNumPeriods() { 145 return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods"); 146 } 147 148 @Override 149 public long getCpuNumThrottled() { 150 return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled"); 151 } 152 153 @Override 154 public long getCpuThrottledTime() { 155 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec"); 156 return TimeUnit.MICROSECONDS.toNanos(micros); 157 } 158 159 @Override 160 public long getEffectiveCpuCount() { 161 return Runtime.getRuntime().availableProcessors(); 162 } 163 164 @Override 165 public int[] getCpuSetCpus() { 166 String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus"); 167 if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) { 168 return new int[0]; // not available 169 } 170 return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal); 171 } 172 173 @Override 174 public int[] getEffectiveCpuSetCpus() { 175 String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective"); 176 if (effCpuSetVal == null || EMPTY_STR.equals(effCpuSetVal)) { 177 return new int[0]; // not available 178 } 179 return CgroupSubsystemController.stringRangeToIntArray(effCpuSetVal); 180 } 181 182 @Override 183 public int[] getCpuSetMems() { 184 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems")); 185 } 186 187 @Override 188 public int[] getEffectiveCpuSetMems() { 189 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective")); 190 } 191 192 @Override 193 public double getCpuSetMemoryPressure() { 194 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 195 } 196 197 @Override 198 public boolean isCpuSetMemoryPressureEnabled() { 199 return false; // not supported 200 } 201 202 @Override 203 public long getMemoryFailCount() { 204 return CgroupSubsystemController.getLongEntry(unified, "memory.events", "max"); 205 } 206 207 @Override 208 public long getMemoryLimit() { 209 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max"); 210 return limitFromString(strVal); 211 } 212 213 @Override 214 public long getMemoryMaxUsage() { 215 return memoryMaxUsage; 216 } 217 218 @Override 219 public long getMemoryUsage() { 220 long memUsage = getLongVal("memory.current"); 221 // synthesize memory max usage by piggy-backing on current usage 222 if (memoryMaxUsage < memUsage) { 223 memoryMaxUsage = memUsage; 224 } 225 return memUsage; 226 } 227 228 @Override 229 public long getKernelMemoryFailCount() { 230 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 231 } 232 233 @Override 234 public long getKernelMemoryLimit() { 235 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 236 } 237 238 @Override 239 public long getKernelMemoryMaxUsage() { 240 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 241 } 242 243 @Override 244 public long getKernelMemoryUsage() { 245 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 246 } 247 248 @Override 249 public long getTcpMemoryFailCount() { 250 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 251 } 252 253 @Override 254 public long getTcpMemoryLimit() { 255 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 256 } 257 258 @Override 259 public long getTcpMemoryMaxUsage() { 260 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 261 } 262 263 @Override 264 public long getTcpMemoryUsage() { 265 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 266 } 267 268 @Override 269 public long getMemoryAndSwapFailCount() { 270 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 271 } 272 273 @Override 274 public long getMemoryAndSwapLimit() { 275 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max"); 276 return limitFromString(strVal); 277 } 278 279 @Override 280 public long getMemoryAndSwapMaxUsage() { 281 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 282 } 283 284 @Override 285 public long getMemoryAndSwapUsage() { 286 return getLongVal("memory.swap.current"); 287 } 288 289 @Override 290 public boolean isMemoryOOMKillEnabled() { 291 return false; // Not supported. 292 } 293 294 @Override 295 public long getMemorySoftLimit() { 296 String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.high"); 297 return limitFromString(softLimitStr); 298 } 299 300 @Override 301 public long getBlkIOServiceCount() { 302 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 303 } 304 305 @Override 306 public long getBlkIOServiced() { 307 return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; 308 } 309 }