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; 27 28 import java.io.IOException; 29 import java.lang.System.Logger; 30 import java.lang.System.Logger.Level; 31 import java.nio.file.Paths; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.stream.Stream; 36 37 import jdk.internal.platform.cgroupv1.CgroupV1Subsystem; 38 import jdk.internal.platform.cgroupv2.CgroupV2Subsystem; 39 40 public class CgroupSubsystemFactory { 41 42 private static final String CPU_CTRL = "cpu"; 43 private static final String CPUACCT_CTRL = "cpuacct"; 44 private static final String CPUSET_CTRL = "cpuset"; 45 private static final String BLKIO_CTRL = "blkio"; 46 private static final String MEMORY_CTRL = "memory"; 47 48 static CgroupMetrics create() { 49 CgroupTypeResult result = null; 50 try { 51 result = determineType("/proc/self/mountinfo", "/proc/cgroups"); 52 } catch (IOException e) { 53 return null; 54 } 55 56 // If no controller is enabled, return no metrics. 57 if (!result.isAnyControllersEnabled()) { 58 return null; 59 } 60 // Warn about mixed cgroups v1 and cgroups v2 controllers. The code is 61 // not ready to deal with that on a per-controller basis. Return no metrics 62 // in that case 63 if (result.isAnyCgroupV1Controllers() && result.isAnyCgroupV2Controllers()) { 64 Logger logger = System.getLogger("jdk.internal.platform"); 65 logger.log(Level.DEBUG, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled."); 66 return null; 67 } 68 69 if (result.isCgroupV2()) { 70 CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(); 71 return subsystem != null ? new CgroupMetrics(subsystem) : null; 72 } else { 73 CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance(); 74 return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null; 75 } 76 } 77 78 public static CgroupTypeResult determineType(String mountInfo, String cgroups) throws IOException { 79 Map<String, CgroupInfo> infos = new HashMap<>(); 80 List<String> lines = CgroupUtil.readAllLinesPrivileged(Paths.get(cgroups)); 81 for (String line : lines) { 82 if (line.startsWith("#")) { 83 continue; 84 } 85 CgroupInfo info = CgroupInfo.fromCgroupsLine(line); 86 switch (info.getName()) { 87 case CPU_CTRL: infos.put(CPU_CTRL, info); break; 88 case CPUACCT_CTRL: infos.put(CPUACCT_CTRL, info); break; 89 case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break; 90 case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break; 91 case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break; 92 } 93 } 94 Stream<String> mntInfo = CgroupUtil.readFilePrivileged(Paths.get(mountInfo)); 95 boolean anyCgroupMounted = mntInfo.anyMatch(line -> line.contains("cgroup")); 96 97 // For cgroups v2 all controllers need to have zero hierarchy id 98 // and /proc/self/mountinfo needs to have at least one cgroup filesystem 99 // mounted. 100 boolean isCgroupsV2 = true; 101 boolean anyControllersEnabled = false; 102 boolean anyCgroupsV2Controller = false; 103 boolean anyCgroupsV1Controller = false; 104 for (CgroupInfo info: infos.values()) { 105 anyCgroupsV1Controller = anyCgroupsV1Controller || info.getHierarchyId() != 0; 106 anyCgroupsV2Controller = anyCgroupsV2Controller || info.getHierarchyId() == 0; 107 isCgroupsV2 = isCgroupsV2 && info.getHierarchyId() == 0; 108 anyControllersEnabled = anyControllersEnabled || info.isEnabled(); 109 } 110 111 // If there are no mounted controllers in mountinfo, but we've only 112 // seen 0 hierarchy IDs in /proc/cgroups, we are on a cgroups v1 system. 113 if (!anyCgroupMounted && isCgroupsV2) { 114 isCgroupsV2 = false; 115 anyCgroupsV1Controller = true; 116 anyCgroupsV2Controller = false; 117 } 118 return new CgroupTypeResult(isCgroupsV2, anyControllersEnabled, anyCgroupsV2Controller, anyCgroupsV1Controller); 119 } 120 121 public static final class CgroupTypeResult { 122 private final boolean isCgroupV2; 123 private final boolean anyControllersEnabled; 124 private final boolean anyCgroupV2Controllers; 125 private final boolean anyCgroupV1Controllers; 126 127 private CgroupTypeResult(boolean isCgroupV2, 128 boolean anyControllersEnabled, 129 boolean anyCgroupV2Controllers, 130 boolean anyCgroupV1Controllers) { 131 this.isCgroupV2 = isCgroupV2; 132 this.anyControllersEnabled = anyControllersEnabled; 133 this.anyCgroupV1Controllers = anyCgroupV1Controllers; 134 this.anyCgroupV2Controllers = anyCgroupV2Controllers; 135 } 136 137 public boolean isCgroupV2() { 138 return isCgroupV2; 139 } 140 141 public boolean isAnyControllersEnabled() { 142 return anyControllersEnabled; 143 } 144 145 public boolean isAnyCgroupV2Controllers() { 146 return anyCgroupV2Controllers; 147 } 148 149 public boolean isAnyCgroupV1Controllers() { 150 return anyCgroupV1Controllers; 151 } 152 } 153 }