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