/
niceSOFT
/
openjdk21
Обзор
Документация
Войти
/
niceSOFT
/
openjdk21
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java
405 строк
14 KB
SendaoYan
8370492: [Linux] Update cpu shares to cpu.weight mapping function
17 мар 2026, 08:44
17 мар 2026, 08:44
7206cc3
Код
Авторство
О чём код?
/* * Copyright (c) 2020, 2022, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.internal.platform.cgroupv2; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import jdk.internal.platform.CgroupInfo; import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; import jdk.internal.platform.CgroupUtil; public class CgroupV2Subsystem implements CgroupSubsystem { private static volatile CgroupV2Subsystem INSTANCE; private static final long[] LONG_ARRAY_NOT_SUPPORTED = null; private static final int[] INT_ARRAY_UNAVAILABLE = null; private final CgroupSubsystemController unified; private static final String PROVIDER_NAME = "cgroupv2"; private static final int PER_CPU_SHARES = 1024; private static final Object EMPTY_STR = ""; private static final long NO_SWAP = 0; private CgroupV2Subsystem(CgroupSubsystemController unified) { this.unified = unified; } private long getLongVal(String file, long defaultValue) { return CgroupSubsystemController.getLongValue(unified, file, CgroupV2SubsystemController::convertStringToLong, defaultValue); } private long getLongVal(String file) { return getLongVal(file, CgroupSubsystem.LONG_RETVAL_UNLIMITED); } /** * Get the singleton instance of a cgroups v2 subsystem. On initialization, * a new object from the given cgroup information 'anyController' is being * created. Note that the cgroup information has been parsed from cgroup * interface files ahead of time. * * See CgroupSubsystemFactory.determineType() for the cgroup interface * files parsing logic. * * @return A singleton CgroupSubsystem instance, never null. */ public static CgroupSubsystem getInstance(CgroupInfo anyController) { if (INSTANCE == null) { CgroupSubsystemController unified = new CgroupV2SubsystemController( anyController.getMountPoint(), anyController.getCgroupPath()); CgroupV2Subsystem tmpCgroupSystem = new CgroupV2Subsystem(unified); synchronized (CgroupV2Subsystem.class) { if (INSTANCE == null) { INSTANCE = tmpCgroupSystem; } } } return INSTANCE; } @Override public String getProvider() { return PROVIDER_NAME; } @Override public long getCpuUsage() { long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec"); if (micros < 0) { return micros; } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long[] getPerCpuUsage() { return LONG_ARRAY_NOT_SUPPORTED; } @Override public long getCpuUserUsage() { long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "user_usec"); if (micros < 0) { return micros; } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long getCpuSystemUsage() { long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "system_usec"); if (micros < 0) { return micros; } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long getCpuPeriod() { return getFromCpuMax(1 /* $PERIOD index */); } @Override public long getCpuQuota() { return getFromCpuMax(0 /* $MAX index */); } private long getFromCpuMax(int tokenIdx) { String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max"); if (cpuMaxRaw == null) { // likely file not found return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } // $MAX $PERIOD String[] tokens = cpuMaxRaw.split("\\s+"); if (tokens.length != 2) { return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } String quota = tokens[tokenIdx]; return CgroupSubsystem.limitFromString(quota); } @Override public long getCpuShares() { long sharesRaw = getLongVal("cpu.weight"); // cg v2 value must be in range [1,10000] if (sharesRaw == 100 || sharesRaw <= 0 || sharesRaw > 10000) { return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } int shares = (int)sharesRaw; // CPU shares (OCI) value needs to get translated into // a proper Cgroups v2 value. See: // https://github.com/containers/crun/blob/1.24/crun.1.md#cpu-controller // // Use the inverse of (x == OCI value, y == cgroupsv2 value): // y = 10^(log2(x)^2/612 + 125/612 * log2(x) - 7.0/34.0) // // By re-arranging it to the standard quadratic form: // log2(x)^2 + 125 * log2(x) - (126 + 612 * log_10(y)) = 0 // // Therefore, log2(x) = (-125 + sqrt( 125^2 - 4 * (-(126 + 612 * log_10(y)))))/2 // // As a result we have the inverse (we can discount substraction of the // square root value since those values result in very small numbers and the // cpu shares values - OCI - are in range [2-262144]) // // x = 2^((-125 + sqrt(16129 + 2448* log10(y)))/2) // double logMultiplicand = Math.log10(shares); double discriminant = 16129 + 2448 * logMultiplicand; double squareRoot = Math.sqrt(discriminant); double exponent = (-125 + squareRoot)/2; double scaledValue = Math.pow(2, exponent); int x = (int)scaledValue; if ( x <= PER_CPU_SHARES ) { // Return the back-mapped value. return x; } int f = x/PER_CPU_SHARES; int lower_multiple = f * PER_CPU_SHARES; int upper_multiple = (f + 1) * PER_CPU_SHARES; int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x); int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x); x = distance_lower <= distance_upper ? lower_multiple : upper_multiple; return x; } @Override public long getCpuNumPeriods() { return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods"); } @Override public long getCpuNumThrottled() { return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled"); } @Override public long getCpuThrottledTime() { long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec"); if (micros < 0) { return micros; } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long getEffectiveCpuCount() { return Runtime.getRuntime().availableProcessors(); } @Override public int[] getCpuSetCpus() { String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus"); return getCpuSet(cpuSetVal); } @Override public int[] getEffectiveCpuSetCpus() { String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective"); return getCpuSet(effCpuSetVal); } @Override public int[] getCpuSetMems() { String cpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems"); return getCpuSet(cpuSetMems); } @Override public int[] getEffectiveCpuSetMems() { String effCpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective"); return getCpuSet(effCpuSetMems); } private int[] getCpuSet(String cpuSetVal) { if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) { return INT_ARRAY_UNAVAILABLE; } return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal); } @Override public long getMemoryFailCount() { return CgroupV2SubsystemController.getLongEntry(unified, "memory.events", "max"); } @Override public long getMemoryLimit() { String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max"); return CgroupSubsystem.limitFromString(strVal); } @Override public long getMemoryUsage() { return getLongVal("memory.current"); } @Override public long getTcpMemoryUsage() { return CgroupV2SubsystemController.getLongEntry(unified, "memory.stat", "sock"); } /** * Note that for cgroups v2 the actual limits set for swap and * memory live in two different files, memory.swap.max and memory.max * respectively. In order to properly report a cgroup v1 like * compound value we need to sum the two values. Setting a swap limit * without also setting a memory limit is not allowed. */ @Override public long getMemoryAndSwapLimit() { String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max"); // We only get a null string when file memory.swap.max doesn't exist. // In that case we return the memory limit without any swap. if (strVal == null) { return getMemoryLimit(); } long swapLimit = CgroupSubsystem.limitFromString(strVal); if (swapLimit >= 0) { long memoryLimit = getMemoryLimit(); assert memoryLimit >= 0; return memoryLimit + swapLimit; } return swapLimit; } /** * Note that for cgroups v2 the actual values set for swap usage and * memory usage live in two different files, memory.current and memory.swap.current * respectively. In order to properly report a cgroup v1 like * compound value we need to sum the two values. Setting a swap limit * without also setting a memory limit is not allowed. */ @Override public long getMemoryAndSwapUsage() { long memoryUsage = getMemoryUsage(); if (memoryUsage >= 0) { // If file memory.swap.current doesn't exist, only return the regular // memory usage (without swap). Thus, use default value of NO_SWAP. long swapUsage = getLongVal("memory.swap.current", NO_SWAP); return memoryUsage + swapUsage; } return memoryUsage; // case of no memory limits } @Override public long getMemorySoftLimit() { String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.low"); return CgroupSubsystem.limitFromString(softLimitStr); } @Override public long getPidsMax() { String pidsMaxStr = CgroupSubsystemController.getStringValue(unified, "pids.max"); return CgroupSubsystem.limitFromString(pidsMaxStr); } @Override public long getPidsCurrent() { return getLongVal("pids.current"); } @Override public long getBlkIOServiceCount() { return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs); } @Override public long getBlkIOServiced() { return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO); } private long sumTokensIOStat(Function<String, Long> mapFunc) { try { return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat")) .map(mapFunc) .collect(Collectors.summingLong(e -> e)); } catch (UncheckedIOException | IOException e) { return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } } private static String[] getRWIOMatchTokenNames() { return new String[] { "rios", "wios" }; } private static String[] getRWBytesIOMatchTokenNames() { return new String[] { "rbytes", "wbytes" }; } public static Long lineToRandWIOs(String line) { String[] matchNames = getRWIOMatchTokenNames(); return ioStatLineToLong(line, matchNames); } public static Long lineToRBytesAndWBytesIO(String line) { String[] matchNames = getRWBytesIOMatchTokenNames(); return ioStatLineToLong(line, matchNames); } private static Long ioStatLineToLong(String line, String[] matchNames) { if (line == null || EMPTY_STR.equals(line)) { return Long.valueOf(0); } String[] tokens = line.split("\\s+"); long retval = 0; for (String t: tokens) { String[] valKeys = t.split("="); if (valKeys.length != 2) { // ignore device ids $MAJ:$MIN continue; } for (String match: matchNames) { if (match.equals(valKeys[0])) { retval += longOrZero(valKeys[1]); } } } return Long.valueOf(retval); } private static long longOrZero(String val) { long lVal = 0; try { lVal = Long.parseLong(val); } catch (NumberFormatException e) { // keep at 0 } return lVal; } }