1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 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.cgroupv1; 27 28 import java.io.IOException; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.util.stream.Stream; 32 33 import jdk.internal.platform.CgroupSubsystem; 34 import jdk.internal.platform.CgroupSubsystemController; 35 import jdk.internal.platform.CgroupUtil; 36 37 public class CgroupV1Subsystem implements CgroupSubsystem { 38 private CgroupV1MemorySubSystemController memory; 39 private CgroupV1SubsystemController cpu; 40 private CgroupV1SubsystemController cpuacct; 41 private CgroupV1SubsystemController cpuset; 42 private CgroupV1SubsystemController blkio; 43 private boolean activeSubSystems; 44 45 private static final CgroupV1Subsystem INSTANCE = initSubSystem(); 46 47 private static final String PROVIDER_NAME = "cgroupv1"; 48 49 private CgroupV1Subsystem() { 50 activeSubSystems = false; 51 } 52 53 public static CgroupV1Subsystem getInstance() { 54 return INSTANCE; 55 } 56 57 private static CgroupV1Subsystem initSubSystem() { 58 CgroupV1Subsystem subsystem = new CgroupV1Subsystem(); 59 60 /** 61 * Find the cgroup mount points for subsystems 62 * by reading /proc/self/mountinfo 63 * 64 * Example for docker MemorySubSystem subsystem: 65 * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem 66 * 67 * Example for host: 68 * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem 69 */ 70 try (Stream<String> lines = 71 CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) { 72 73 lines.filter(line -> line.contains(" - cgroup ")) 74 .map(line -> line.split(" ")) 75 .forEach(entry -> createSubSystemController(subsystem, entry)); 76 77 } catch (IOException e) { 78 return null; 79 } 80 81 /** 82 * Read /proc/self/cgroup and map host mount point to 83 * local one via /proc/self/mountinfo content above 84 * 85 * Docker example: 86 * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 87 * 88 * Host example: 89 * 5:memory:/user.slice 90 * 91 * Construct a path to the process specific memory and cpuset 92 * cgroup directory. 93 * 94 * For a container running under Docker from memory example above 95 * the paths would be: 96 * 97 * /sys/fs/cgroup/memory 98 * 99 * For a Host from memory example above the path would be: 100 * 101 * /sys/fs/cgroup/memory/user.slice 102 * 103 */ 104 try (Stream<String> lines = 105 CgroupUtil.readFilePrivileged(Paths.get("/proc/self/cgroup"))) { 106 107 lines.map(line -> line.split(":")) 108 .filter(line -> (line.length >= 3)) 109 .forEach(line -> setSubSystemControllerPath(subsystem, line)); 110 111 } catch (IOException e) { 112 return null; 113 } 114 115 // Return Metrics object if we found any subsystems. 116 if (subsystem.activeSubSystems()) { 117 return subsystem; 118 } 119 120 return null; 121 } 122 123 /** 124 * createSubSystem objects and initialize mount points 125 */ 126 private static void createSubSystemController(CgroupV1Subsystem subsystem, String[] mountentry) { 127 if (mountentry.length < 5) return; 128 129 Path p = Paths.get(mountentry[4]); 130 String[] subsystemNames = p.getFileName().toString().split(","); 131 132 for (String subsystemName: subsystemNames) { 133 switch (subsystemName) { 134 case "memory": 135 subsystem.setMemorySubSystem(new CgroupV1MemorySubSystemController(mountentry[3], mountentry[4])); 136 break; 137 case "cpuset": 138 subsystem.setCpuSetController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 139 break; 140 case "cpuacct": 141 subsystem.setCpuAcctController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 142 break; 143 case "cpu": 144 subsystem.setCpuController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 145 break; 146 case "blkio": 147 subsystem.setBlkIOController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 148 break; 149 default: 150 // Ignore subsystems that we don't support 151 break; 152 } 153 } 154 } 155 156 /** 157 * setSubSystemPath based on the contents of /proc/self/cgroup 158 */ 159 private static void setSubSystemControllerPath(CgroupV1Subsystem subsystem, String[] entry) { 160 String controllerName; 161 String base; 162 CgroupV1SubsystemController controller = null; 163 CgroupV1SubsystemController controller2 = null; 164 165 controllerName = entry[1]; 166 base = entry[2]; 167 if (controllerName != null && base != null) { 168 switch (controllerName) { 169 case "memory": 170 controller = subsystem.memoryController(); 171 break; 172 case "cpuset": 173 controller = subsystem.cpuSetController(); 174 break; 175 case "cpu,cpuacct": 176 case "cpuacct,cpu": 177 controller = subsystem.cpuController(); 178 controller2 = subsystem.cpuAcctController(); 179 break; 180 case "cpuacct": 181 controller = subsystem.cpuAcctController(); 182 break; 183 case "cpu": 184 controller = subsystem.cpuController(); 185 break; 186 case "blkio": 187 controller = subsystem.blkIOController(); 188 break; 189 // Ignore subsystems that we don't support 190 default: 191 break; 192 } 193 } 194 195 if (controller != null) { 196 controller.setPath(base); 197 if (controller instanceof CgroupV1MemorySubSystemController) { 198 CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller; 199 boolean isHierarchial = getHierarchical(memorySubSystem); 200 memorySubSystem.setHierarchical(isHierarchial); 201 } 202 subsystem.setActiveSubSystems(); 203 } 204 if (controller2 != null) { 205 controller2.setPath(base); 206 } 207 } 208 209 210 private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) { 211 long hierarchical = getLongValue(controller, "memory.use_hierarchy"); 212 return hierarchical > 0; 213 } 214 215 private void setActiveSubSystems() { 216 activeSubSystems = true; 217 } 218 219 private boolean activeSubSystems() { 220 return activeSubSystems; 221 } 222 223 private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) { 224 this.memory = memory; 225 } 226 227 private void setCpuController(CgroupV1SubsystemController cpu) { 228 this.cpu = cpu; 229 } 230 231 private void setCpuAcctController(CgroupV1SubsystemController cpuacct) { 232 this.cpuacct = cpuacct; 233 } 234 235 private void setCpuSetController(CgroupV1SubsystemController cpuset) { 236 this.cpuset = cpuset; 237 } 238 239 private void setBlkIOController(CgroupV1SubsystemController blkio) { 240 this.blkio = blkio; 241 } 242 243 private CgroupV1SubsystemController memoryController() { 244 return memory; 245 } 246 247 private CgroupV1SubsystemController cpuController() { 248 return cpu; 249 } 250 251 private CgroupV1SubsystemController cpuAcctController() { 252 return cpuacct; 253 } 254 255 private CgroupV1SubsystemController cpuSetController() { 256 return cpuset; 257 } 258 259 private CgroupV1SubsystemController blkIOController() { 260 return blkio; 261 } 262 263 private static long getLongValue(CgroupSubsystemController controller, 264 String parm) { 265 return CgroupSubsystemController.getLongValue(controller, 266 parm, 267 CgroupV1SubsystemController::convertStringToLong); 268 } 269 270 public String getProvider() { 271 return PROVIDER_NAME; 272 } 273 274 /***************************************************************** 275 * CPU Accounting Subsystem 276 ****************************************************************/ 277 278 279 public long getCpuUsage() { 280 return getLongValue(cpuacct, "cpuacct.usage"); 281 } 282 283 public long[] getPerCpuUsage() { 284 String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu"); 285 if (usagelist == null) { 286 return new long[0]; 287 } 288 289 String list[] = usagelist.split(" "); 290 long percpu[] = new long[list.length]; 291 for (int i = 0; i < list.length; i++) { 292 percpu[i] = Long.parseLong(list[i]); 293 } 294 return percpu; 295 } 296 297 public long getCpuUserUsage() { 298 return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user"); 299 } 300 301 public long getCpuSystemUsage() { 302 return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system"); 303 } 304 305 306 /***************************************************************** 307 * CPU Subsystem 308 ****************************************************************/ 309 310 311 public long getCpuPeriod() { 312 return getLongValue(cpu, "cpu.cfs_period_us"); 313 } 314 315 public long getCpuQuota() { 316 return getLongValue(cpu, "cpu.cfs_quota_us"); 317 } 318 319 public long getCpuShares() { 320 long retval = getLongValue(cpu, "cpu.shares"); 321 if (retval == 0 || retval == 1024) 322 return -1; 323 else 324 return retval; 325 } 326 327 public long getCpuNumPeriods() { 328 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods"); 329 } 330 331 public long getCpuNumThrottled() { 332 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled"); 333 } 334 335 public long getCpuThrottledTime() { 336 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time"); 337 } 338 339 public long getEffectiveCpuCount() { 340 return Runtime.getRuntime().availableProcessors(); 341 } 342 343 344 /***************************************************************** 345 * CPUSet Subsystem 346 ****************************************************************/ 347 348 public int[] getCpuSetCpus() { 349 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus")); 350 } 351 352 public int[] getEffectiveCpuSetCpus() { 353 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus")); 354 } 355 356 public int[] getCpuSetMems() { 357 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems")); 358 } 359 360 public int[] getEffectiveCpuSetMems() { 361 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems")); 362 } 363 364 public double getCpuSetMemoryPressure() { 365 return CgroupSubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); 366 } 367 368 public boolean isCpuSetMemoryPressureEnabled() { 369 long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled"); 370 return (val == 1); 371 } 372 373 374 /***************************************************************** 375 * Memory Subsystem 376 ****************************************************************/ 377 378 379 public long getMemoryFailCount() { 380 return getLongValue(memory, "memory.failcnt"); 381 } 382 383 public long getMemoryLimit() { 384 long retval = getLongValue(memory, "memory.limit_in_bytes"); 385 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 386 if (memory.isHierarchical()) { 387 // memory.limit_in_bytes returned unlimited, attempt 388 // hierarchical memory limit 389 String match = "hierarchical_memory_limit"; 390 retval = CgroupSubsystemController.getLongValueMatchingLine(memory, 391 "memory.stat", 392 match, 393 CgroupV1Subsystem::convertHierachicalLimitLine); 394 } 395 } 396 return CgroupV1SubsystemController.longValOrUnlimited(retval); 397 } 398 399 public static long convertHierachicalLimitLine(String line) { 400 String[] tokens = line.split("\\s"); 401 if (tokens.length == 2) { 402 String strVal = tokens[1]; 403 return CgroupV1SubsystemController.convertStringToLong(strVal); 404 } 405 return CgroupV1SubsystemController.UNLIMITED_MIN + 1; // unlimited 406 } 407 408 public long getMemoryMaxUsage() { 409 return getLongValue(memory, "memory.max_usage_in_bytes"); 410 } 411 412 public long getMemoryUsage() { 413 return getLongValue(memory, "memory.usage_in_bytes"); 414 } 415 416 public long getKernelMemoryFailCount() { 417 return getLongValue(memory, "memory.kmem.failcnt"); 418 } 419 420 public long getKernelMemoryLimit() { 421 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes")); 422 } 423 424 public long getKernelMemoryMaxUsage() { 425 return getLongValue(memory, "memory.kmem.max_usage_in_bytes"); 426 } 427 428 public long getKernelMemoryUsage() { 429 return getLongValue(memory, "memory.kmem.usage_in_bytes"); 430 } 431 432 public long getTcpMemoryFailCount() { 433 return getLongValue(memory, "memory.kmem.tcp.failcnt"); 434 } 435 436 public long getTcpMemoryLimit() { 437 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes")); 438 } 439 440 public long getTcpMemoryMaxUsage() { 441 return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); 442 } 443 444 public long getTcpMemoryUsage() { 445 return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); 446 } 447 448 public long getMemoryAndSwapFailCount() { 449 return getLongValue(memory, "memory.memsw.failcnt"); 450 } 451 452 public long getMemoryAndSwapLimit() { 453 long retval = getLongValue(memory, "memory.memsw.limit_in_bytes"); 454 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 455 if (memory.isHierarchical()) { 456 // memory.memsw.limit_in_bytes returned unlimited, attempt 457 // hierarchical memory limit 458 String match = "hierarchical_memsw_limit"; 459 retval = CgroupSubsystemController.getLongValueMatchingLine(memory, 460 "memory.stat", 461 match, 462 CgroupV1Subsystem::convertHierachicalLimitLine); 463 } 464 } 465 return CgroupV1SubsystemController.longValOrUnlimited(retval); 466 } 467 468 public long getMemoryAndSwapMaxUsage() { 469 return getLongValue(memory, "memory.memsw.max_usage_in_bytes"); 470 } 471 472 public long getMemoryAndSwapUsage() { 473 return getLongValue(memory, "memory.memsw.usage_in_bytes"); 474 } 475 476 public boolean isMemoryOOMKillEnabled() { 477 long val = CgroupSubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); 478 return (val == 0); 479 } 480 481 public long getMemorySoftLimit() { 482 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes")); 483 } 484 485 486 /***************************************************************** 487 * BlKIO Subsystem 488 ****************************************************************/ 489 490 491 public long getBlkIOServiceCount() { 492 return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); 493 } 494 495 public long getBlkIOServiced() { 496 return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); 497 } 498 499 }