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);
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 }
|
1 /*
2 * Copyright (c) 2020, 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.io.IOException;
29 import java.nio.file.Paths;
30 import java.util.concurrent.TimeUnit;
31 import java.util.function.Function;
32 import java.util.stream.Collectors;
33
34 import jdk.internal.platform.CgroupSubsystem;
35 import jdk.internal.platform.CgroupSubsystemController;
36 import jdk.internal.platform.CgroupUtil;
37 import jdk.internal.platform.Metrics;
38
39 public class CgroupV2Subsystem implements CgroupSubsystem {
40
41 private final CgroupSubsystemController unified;
42 private static final String PROVIDER_NAME = "cgroupv2";
43 private static final int PER_CPU_SHARES = 1024;
44 private static final String MAX_VAL = "max";
45 private static final Object EMPTY_STR = "";
46
47 public CgroupV2Subsystem(CgroupSubsystemController unified) {
48 this.unified = unified;
49 }
50
51 private long getLongVal(String file) {
52 return CgroupSubsystemController.getLongValue(unified,
53 file,
54 CgroupV2SubsystemController::convertStringToLong);
55 }
56
57 @Override
58 public String getProvider() {
59 return PROVIDER_NAME;
60 }
61
62 @Override
63 public long getCpuUsage() {
64 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec");
65 return TimeUnit.MICROSECONDS.toNanos(micros);
66 }
67
68 @Override
69 public long[] getPerCpuUsage() {
70 return null; // Not supported.
71 }
72
73 @Override
74 public long getCpuUserUsage() {
75 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "user_usec");
76 return TimeUnit.MICROSECONDS.toNanos(micros);
77 }
78
79 @Override
80 public long getCpuSystemUsage() {
81 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "system_usec");
82 return TimeUnit.MICROSECONDS.toNanos(micros);
83 }
84
85 @Override
86 public long getCpuPeriod() {
87 return getFromCpuMax(1 /* $PERIOD index */);
88 }
89
90 @Override
91 public long getCpuQuota() {
92 return getFromCpuMax(0 /* $MAX index */);
93 }
94
95 private long getFromCpuMax(int tokenIdx) {
96 String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max");
97 if (cpuMaxRaw == null) {
98 // likely file not found
99 return Metrics.LONG_RETVAL_UNLIMITED;
100 }
101 // $MAX $PERIOD
102 String[] tokens = cpuMaxRaw.split("\\s+");
103 if (tokens.length != 2) {
104 return Metrics.LONG_RETVAL_UNLIMITED;
105 }
106 String quota = tokens[tokenIdx];
107 return limitFromString(quota);
108 }
109
110 private long limitFromString(String strVal) {
111 if (MAX_VAL.equals(strVal)) {
112 return Metrics.LONG_RETVAL_UNLIMITED;
113 }
114 return Long.parseLong(strVal);
115 }
116
117 @Override
118 public long getCpuShares() {
119 long sharesRaw = getLongVal("cpu.weight");
120 if (sharesRaw == 100 || sharesRaw == 0) {
121 return Metrics.LONG_RETVAL_UNLIMITED;
122 }
123 int shares = (int)sharesRaw;
124 // CPU shares (OCI) value needs to get translated into
125 // a proper Cgroups v2 value. See:
126 // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
127 //
128 // Use the inverse of (x == OCI value, y == cgroupsv2 value):
129 // ((262142 * y - 1)/9999) + 2 = x
130 //
131 int x = 262142 * shares - 1;
132 double frac = x/9999.0;
133 x = ((int)frac) + 2;
134 if ( x <= PER_CPU_SHARES ) {
135 return PER_CPU_SHARES; // mimic cgroups v1
136 }
137 int f = x/PER_CPU_SHARES;
138 int lower_multiple = f * PER_CPU_SHARES;
139 int upper_multiple = (f + 1) * PER_CPU_SHARES;
140 int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x);
141 int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x);
151 @Override
152 public long getCpuNumThrottled() {
153 return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled");
154 }
155
156 @Override
157 public long getCpuThrottledTime() {
158 long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec");
159 return TimeUnit.MICROSECONDS.toNanos(micros);
160 }
161
162 @Override
163 public long getEffectiveCpuCount() {
164 return Runtime.getRuntime().availableProcessors();
165 }
166
167 @Override
168 public int[] getCpuSetCpus() {
169 String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus");
170 if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) {
171 return null; // not available
172 }
173 return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal);
174 }
175
176 @Override
177 public int[] getEffectiveCpuSetCpus() {
178 String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective");
179 if (effCpuSetVal == null || EMPTY_STR.equals(effCpuSetVal)) {
180 return null; // not available
181 }
182 return CgroupSubsystemController.stringRangeToIntArray(effCpuSetVal);
183 }
184
185 @Override
186 public int[] getCpuSetMems() {
187 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems"));
188 }
189
190 @Override
191 public int[] getEffectiveCpuSetMems() {
192 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective"));
193 }
194
195 @Override
196 public double getCpuSetMemoryPressure() {
197 return Metrics.DOUBLE_RETVAL_NOT_SUPPORTED;
198 }
199
200 @Override
201 public Boolean isCpuSetMemoryPressureEnabled() {
202 return Metrics.BOOL_RETVAL_NOT_SUPPORTED;
203 }
204
205 @Override
206 public long getMemoryFailCount() {
207 return CgroupSubsystemController.getLongEntry(unified, "memory.events", "max");
208 }
209
210 @Override
211 public long getMemoryLimit() {
212 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max");
213 return limitFromString(strVal);
214 }
215
216 @Override
217 public long getMemoryMaxUsage() {
218 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
219 }
220
221 @Override
222 public long getMemoryUsage() {
223 return getLongVal("memory.current");
224 }
225
226 @Override
227 public long getKernelMemoryFailCount() {
228 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
229 }
230
231 @Override
232 public long getKernelMemoryLimit() {
233 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
234 }
235
236 @Override
237 public long getKernelMemoryMaxUsage() {
238 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
239 }
240
241 @Override
242 public long getKernelMemoryUsage() {
243 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
244 }
245
246 @Override
247 public long getTcpMemoryFailCount() {
248 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
249 }
250
251 @Override
252 public long getTcpMemoryLimit() {
253 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
254 }
255
256 @Override
257 public long getTcpMemoryMaxUsage() {
258 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
259 }
260
261 @Override
262 public long getTcpMemoryUsage() {
263 return CgroupSubsystemController.getLongEntry(unified, "memory.stat", "sock");
264 }
265
266 @Override
267 public long getMemoryAndSwapFailCount() {
268 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
269 }
270
271 @Override
272 public long getMemoryAndSwapLimit() {
273 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max");
274 return limitFromString(strVal);
275 }
276
277 @Override
278 public long getMemoryAndSwapMaxUsage() {
279 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
280 }
281
282 @Override
283 public long getMemoryAndSwapUsage() {
284 return getLongVal("memory.swap.current");
285 }
286
287 @Override
288 public Boolean isMemoryOOMKillEnabled() {
289 return Metrics.BOOL_RETVAL_NOT_SUPPORTED;
290 }
291
292 @Override
293 public long getMemorySoftLimit() {
294 String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.high");
295 return limitFromString(softLimitStr);
296 }
297
298 @Override
299 public long getBlkIOServiceCount() {
300 return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs);
301 }
302
303
304 @Override
305 public long getBlkIOServiced() {
306 return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO);
307 }
308
309 private long sumTokensIOStat(Function<String, Long> mapFunc) {
310 try {
311 return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat"))
312 .map(mapFunc)
313 .collect(Collectors.summingLong(e -> e));
314 } catch (IOException e) {
315 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
316 }
317 }
318
319 private static String[] getRWIOMatchTokenNames() {
320 return new String[] { "rios", "wios" };
321 }
322
323 private static String[] getRWBytesIOMatchTokenNames() {
324 return new String[] { "rbytes", "wbytes" };
325 }
326
327 public static Long lineToRandWIOs(String line) {
328 String[] matchNames = getRWIOMatchTokenNames();
329 return ioStatLineToLong(line, matchNames);
330 }
331
332 public static Long lineToRBytesAndWBytesIO(String line) {
333 String[] matchNames = getRWBytesIOMatchTokenNames();
334 return ioStatLineToLong(line, matchNames);
335 }
336
337 private static Long ioStatLineToLong(String line, String[] matchNames) {
338 if (line == null || EMPTY_STR.equals(line)) {
339 return Long.valueOf(0);
340 }
341 String[] tokens = line.split("\\s+");
342 long retval = 0;
343 for (String t: tokens) {
344 String[] valKeys = t.split("=");
345 if (valKeys.length != 2) {
346 // ignore device ids $MAJ:$MIN
347 continue;
348 }
349 for (String match: matchNames) {
350 if (match.equals(valKeys[0])) {
351 retval += longOrZero(valKeys[1]);
352 }
353 }
354 }
355 return Long.valueOf(retval);
356 }
357
358 private static long longOrZero(String val) {
359 long lVal = 0;
360 try {
361 lVal = Long.parseLong(val);
362 } catch (NumberFormatException e) {
363 // keep at 0
364 }
365 return lVal;
366 }
367 }
|