--- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-02-20 20:57:27.811368343 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-02-20 20:57:27.667367937 +0100 @@ -32,11 +32,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import jdk.internal.platform.cgroupv1.CgroupV1Subsystem; import jdk.internal.platform.cgroupv2.CgroupV2Subsystem; -class CgroupSubsystemFactory { +public class CgroupSubsystemFactory { private static final String CPU_CTRL = "cpu"; private static final String CPUACCT_CTRL = "cpuacct"; @@ -45,52 +46,27 @@ private static final String MEMORY_CTRL = "memory"; static CgroupMetrics create() { - Map infos = new HashMap<>(); + CgroupTypeResult result = null; try { - List lines = CgroupUtil.readAllLinesPrivileged(Paths.get("/proc/cgroups")); - for (String line : lines) { - if (line.startsWith("#")) { - continue; - } - CgroupInfo info = CgroupInfo.fromCgroupsLine(line); - switch (info.getName()) { - case CPU_CTRL: infos.put(CPU_CTRL, info); break; - case CPUACCT_CTRL: infos.put(CPUACCT_CTRL, info); break; - case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break; - case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break; - case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break; - } - } + result = determineType("/proc/self/mountinfo", "/proc/cgroups"); } catch (IOException e) { return null; } - // For cgroups v1 all controllers need to have non-zero hierarchy id - boolean isCgroupsV2 = true; - boolean anyControllersEnabled = false; - boolean anyCgroupsV2Controller = false; - boolean anyCgroupsV1Controller = false; - for (CgroupInfo info: infos.values()) { - anyCgroupsV1Controller = anyCgroupsV1Controller || info.getHierarchyId() != 0; - anyCgroupsV2Controller = anyCgroupsV2Controller || info.getHierarchyId() == 0; - isCgroupsV2 = isCgroupsV2 && info.getHierarchyId() == 0; - anyControllersEnabled = anyControllersEnabled || info.isEnabled(); - } - // If no controller is enabled, return no metrics. - if (!anyControllersEnabled) { + if (!result.isAnyControllersEnabled()) { return null; } // Warn about mixed cgroups v1 and cgroups v2 controllers. The code is // not ready to deal with that on a per-controller basis. Return no metrics // in that case - if (anyCgroupsV1Controller && anyCgroupsV2Controller) { + if (result.isAnyCgroupV1Controllers() && result.isAnyCgroupV2Controllers()) { Logger logger = System.getLogger("jdk.internal.platform"); logger.log(Level.DEBUG, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled."); return null; } - if (isCgroupsV2) { + if (result.isCgroupV2()) { CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(); return subsystem != null ? new CgroupMetrics(subsystem) : null; } else { @@ -98,4 +74,80 @@ return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null; } } + + public static CgroupTypeResult determineType(String mountInfo, String cgroups) throws IOException { + Map infos = new HashMap<>(); + List lines = CgroupUtil.readAllLinesPrivileged(Paths.get(cgroups)); + for (String line : lines) { + if (line.startsWith("#")) { + continue; + } + CgroupInfo info = CgroupInfo.fromCgroupsLine(line); + switch (info.getName()) { + case CPU_CTRL: infos.put(CPU_CTRL, info); break; + case CPUACCT_CTRL: infos.put(CPUACCT_CTRL, info); break; + case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break; + case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break; + case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break; + } + } + Stream mntInfo = CgroupUtil.readFilePrivileged(Paths.get(mountInfo)); + boolean anyCgroupMounted = mntInfo.anyMatch(line -> line.contains("cgroup")); + + // For cgroups v2 all controllers need to have zero hierarchy id + // and /proc/self/mountinfo needs to have at least one cgroup filesystem + // mounted. + boolean isCgroupsV2 = true; + boolean anyControllersEnabled = false; + boolean anyCgroupsV2Controller = false; + boolean anyCgroupsV1Controller = false; + for (CgroupInfo info: infos.values()) { + anyCgroupsV1Controller = anyCgroupsV1Controller || info.getHierarchyId() != 0; + anyCgroupsV2Controller = anyCgroupsV2Controller || info.getHierarchyId() == 0; + isCgroupsV2 = isCgroupsV2 && info.getHierarchyId() == 0; + anyControllersEnabled = anyControllersEnabled || info.isEnabled(); + } + + // If there are no mounted controllers in mountinfo, but we've only + // seen 0 hierarchy IDs in /proc/cgroups, we are on a cgroups v1 system. + if (!anyCgroupMounted && isCgroupsV2) { + isCgroupsV2 = false; + anyCgroupsV1Controller = true; + anyCgroupsV2Controller = false; + } + return new CgroupTypeResult(isCgroupsV2, anyControllersEnabled, anyCgroupsV2Controller, anyCgroupsV1Controller); + } + + public static final class CgroupTypeResult { + private final boolean isCgroupV2; + private final boolean anyControllersEnabled; + private final boolean anyCgroupV2Controllers; + private final boolean anyCgroupV1Controllers; + + private CgroupTypeResult(boolean isCgroupV2, + boolean anyControllersEnabled, + boolean anyCgroupV2Controllers, + boolean anyCgroupV1Controllers) { + this.isCgroupV2 = isCgroupV2; + this.anyControllersEnabled = anyControllersEnabled; + this.anyCgroupV1Controllers = anyCgroupV1Controllers; + this.anyCgroupV2Controllers = anyCgroupV2Controllers; + } + + public boolean isCgroupV2() { + return isCgroupV2; + } + + public boolean isAnyControllersEnabled() { + return anyControllersEnabled; + } + + public boolean isAnyCgroupV2Controllers() { + return anyCgroupV2Controllers; + } + + public boolean isAnyCgroupV1Controllers() { + return anyCgroupV1Controllers; + } + } }