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);
142 x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
143 return x;
144 }
145
146 @Override
147 public long getCpuNumPeriods() {
148 return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods");
149 }
150
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 }
|
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
38 public class CgroupV2Subsystem implements CgroupSubsystem {
39
40 private static final long[] LONG_ARRAY_NOT_SUPPORTED = null;
41 private static final int[] INT_ARRAY_UNAVAILABLE = null;
42 private final CgroupSubsystemController unified;
43 private static final String PROVIDER_NAME = "cgroupv2";
44 private static final int PER_CPU_SHARES = 1024;
45 private static final String MAX_VAL = "max";
46 private static final Object EMPTY_STR = "";
47
48 public CgroupV2Subsystem(CgroupSubsystemController unified) {
49 this.unified = unified;
50 }
51
52 private long getLongVal(String file) {
53 return CgroupSubsystemController.getLongValue(unified,
54 file,
55 CgroupV2SubsystemController::convertStringToLong,
56 CgroupSubsystem.LONG_RETVAL_UNLIMITED);
57 }
58
59 @Override
60 public String getProvider() {
61 return PROVIDER_NAME;
62 }
63
64 @Override
65 public long getCpuUsage() {
66 long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec");
67 if (micros < 0) {
68 return micros;
69 }
70 return TimeUnit.MICROSECONDS.toNanos(micros);
71 }
72
73 @Override
74 public long[] getPerCpuUsage() {
75 return LONG_ARRAY_NOT_SUPPORTED;
76 }
77
78 @Override
79 public long getCpuUserUsage() {
80 long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "user_usec");
81 if (micros < 0) {
82 return micros;
83 }
84 return TimeUnit.MICROSECONDS.toNanos(micros);
85 }
86
87 @Override
88 public long getCpuSystemUsage() {
89 long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "system_usec");
90 if (micros < 0) {
91 return micros;
92 }
93 return TimeUnit.MICROSECONDS.toNanos(micros);
94 }
95
96 @Override
97 public long getCpuPeriod() {
98 return getFromCpuMax(1 /* $PERIOD index */);
99 }
100
101 @Override
102 public long getCpuQuota() {
103 return getFromCpuMax(0 /* $MAX index */);
104 }
105
106 private long getFromCpuMax(int tokenIdx) {
107 String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max");
108 if (cpuMaxRaw == null) {
109 // likely file not found
110 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
111 }
112 // $MAX $PERIOD
113 String[] tokens = cpuMaxRaw.split("\\s+");
114 if (tokens.length != 2) {
115 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
116 }
117 String quota = tokens[tokenIdx];
118 return limitFromString(quota);
119 }
120
121 private long limitFromString(String strVal) {
122 if (strVal == null || MAX_VAL.equals(strVal)) {
123 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
124 }
125 return Long.parseLong(strVal);
126 }
127
128 @Override
129 public long getCpuShares() {
130 long sharesRaw = getLongVal("cpu.weight");
131 if (sharesRaw == 100 || sharesRaw <= 0) {
132 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
133 }
134 int shares = (int)sharesRaw;
135 // CPU shares (OCI) value needs to get translated into
136 // a proper Cgroups v2 value. See:
137 // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
138 //
139 // Use the inverse of (x == OCI value, y == cgroupsv2 value):
140 // ((262142 * y - 1)/9999) + 2 = x
141 //
142 int x = 262142 * shares - 1;
143 double frac = x/9999.0;
144 x = ((int)frac) + 2;
145 if ( x <= PER_CPU_SHARES ) {
146 return PER_CPU_SHARES; // mimic cgroups v1
147 }
148 int f = x/PER_CPU_SHARES;
149 int lower_multiple = f * PER_CPU_SHARES;
150 int upper_multiple = (f + 1) * PER_CPU_SHARES;
151 int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x);
152 int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x);
153 x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
154 return x;
155 }
156
157 @Override
158 public long getCpuNumPeriods() {
159 return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods");
160 }
161
162 @Override
163 public long getCpuNumThrottled() {
164 return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled");
165 }
166
167 @Override
168 public long getCpuThrottledTime() {
169 long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec");
170 if (micros < 0) {
171 return micros;
172 }
173 return TimeUnit.MICROSECONDS.toNanos(micros);
174 }
175
176 @Override
177 public long getEffectiveCpuCount() {
178 return Runtime.getRuntime().availableProcessors();
179 }
180
181 @Override
182 public int[] getCpuSetCpus() {
183 String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus");
184 return getCpuSet(cpuSetVal);
185 }
186
187 @Override
188 public int[] getEffectiveCpuSetCpus() {
189 String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective");
190 return getCpuSet(effCpuSetVal);
191 }
192
193 @Override
194 public int[] getCpuSetMems() {
195 String cpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems");
196 return getCpuSet(cpuSetMems);
197 }
198
199 @Override
200 public int[] getEffectiveCpuSetMems() {
201 String effCpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective");
202 return getCpuSet(effCpuSetMems);
203 }
204
205 private int[] getCpuSet(String cpuSetVal) {
206 if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) {
207 return INT_ARRAY_UNAVAILABLE;
208 }
209 return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal);
210 }
211
212 @Override
213 public long getMemoryFailCount() {
214 return CgroupV2SubsystemController.getLongEntry(unified, "memory.events", "max");
215 }
216
217 @Override
218 public long getMemoryLimit() {
219 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max");
220 return limitFromString(strVal);
221 }
222
223 @Override
224 public long getMemoryUsage() {
225 return getLongVal("memory.current");
226 }
227
228 @Override
229 public long getTcpMemoryUsage() {
230 return CgroupV2SubsystemController.getLongEntry(unified, "memory.stat", "sock");
231 }
232
233 @Override
234 public long getMemoryAndSwapLimit() {
235 String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max");
236 return limitFromString(strVal);
237 }
238
239 @Override
240 public long getMemoryAndSwapUsage() {
241 return getLongVal("memory.swap.current");
242 }
243
244 @Override
245 public long getMemorySoftLimit() {
246 String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.high");
247 return limitFromString(softLimitStr);
248 }
249
250 @Override
251 public long getBlkIOServiceCount() {
252 return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs);
253 }
254
255
256 @Override
257 public long getBlkIOServiced() {
258 return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO);
259 }
260
261 private long sumTokensIOStat(Function<String, Long> mapFunc) {
262 try {
263 return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat"))
264 .map(mapFunc)
265 .collect(Collectors.summingLong(e -> e));
266 } catch (IOException e) {
267 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
268 }
269 }
270
271 private static String[] getRWIOMatchTokenNames() {
272 return new String[] { "rios", "wios" };
273 }
274
275 private static String[] getRWBytesIOMatchTokenNames() {
276 return new String[] { "rbytes", "wbytes" };
277 }
278
279 public static Long lineToRandWIOs(String line) {
280 String[] matchNames = getRWIOMatchTokenNames();
281 return ioStatLineToLong(line, matchNames);
282 }
283
284 public static Long lineToRBytesAndWBytesIO(String line) {
285 String[] matchNames = getRWBytesIOMatchTokenNames();
286 return ioStatLineToLong(line, matchNames);
287 }
|