mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 09:53:18 +00:00
8370492: [Linux] Update cpu shares to cpu.weight mapping function
Reviewed-by: cnorrbin, ayang, syan
This commit is contained in:
parent
4cc655a2f4
commit
5d65c23cd9
@ -26,6 +26,8 @@
|
||||
#include "cgroupUtil_linux.hpp"
|
||||
#include "cgroupV2Subsystem_linux.hpp"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Constructor
|
||||
CgroupV2Controller::CgroupV2Controller(char* mount_path,
|
||||
char *cgroup_path,
|
||||
@ -61,22 +63,39 @@ int CgroupV2CpuController::cpu_shares() {
|
||||
log_debug(os, container)("CPU Shares is: %d", -1);
|
||||
return -1;
|
||||
}
|
||||
// cg v2 values must be in range [1-10000]
|
||||
assert(shares_int >= 1 && shares_int <= 10000, "invariant");
|
||||
|
||||
// CPU shares (OCI) value needs to get translated into
|
||||
// a proper Cgroups v2 value. See:
|
||||
// https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
|
||||
// https://github.com/containers/crun/blob/1.24/crun.1.md#cpu-controller
|
||||
//
|
||||
// Use the inverse of (x == OCI value, y == cgroupsv2 value):
|
||||
// ((262142 * y - 1)/9999) + 2 = x
|
||||
// y = 10^(log2(x)^2/612 + 125/612 * log2(x) - 7.0/34.0)
|
||||
//
|
||||
int x = 262142 * shares_int - 1;
|
||||
double frac = x/9999.0;
|
||||
x = ((int)frac) + 2;
|
||||
// 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 log_multiplicand = log10(shares_int);
|
||||
double discriminant = 16129 + 2448 * log_multiplicand;
|
||||
double square_root = sqrt(discriminant);
|
||||
double exponent = (-125 + square_root)/2;
|
||||
double scaled_val = pow(2, exponent);
|
||||
int x = (int) scaled_val;
|
||||
log_trace(os, container)("Scaled CPU shares value is: %d", x);
|
||||
// Since the scaled value is not precise, return the closest
|
||||
// multiple of PER_CPU_SHARES for a more conservative mapping
|
||||
if ( x <= PER_CPU_SHARES ) {
|
||||
// will always map to 1 CPU
|
||||
// Don't do the multiples of PER_CPU_SHARES mapping since we
|
||||
// have a value <= PER_CPU_SHARES
|
||||
log_debug(os, container)("CPU Shares is: %d", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
@ -156,22 +156,39 @@ public class CgroupV2Subsystem implements CgroupSubsystem {
|
||||
@Override
|
||||
public long getCpuShares() {
|
||||
long sharesRaw = getLongVal("cpu.weight");
|
||||
if (sharesRaw == 100 || sharesRaw <= 0) {
|
||||
// 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/master/crun.1.md#cpu-controller
|
||||
// https://github.com/containers/crun/blob/1.24/crun.1.md#cpu-controller
|
||||
//
|
||||
// Use the inverse of (x == OCI value, y == cgroupsv2 value):
|
||||
// ((262142 * y - 1)/9999) + 2 = x
|
||||
// y = 10^(log2(x)^2/612 + 125/612 * log2(x) - 7.0/34.0)
|
||||
//
|
||||
int x = 262142 * shares - 1;
|
||||
double frac = x/9999.0;
|
||||
x = ((int)frac) + 2;
|
||||
// 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 PER_CPU_SHARES; // mimic cgroups v1
|
||||
// Return the back-mapped value.
|
||||
return x;
|
||||
}
|
||||
int f = x/PER_CPU_SHARES;
|
||||
int lower_multiple = f * PER_CPU_SHARES;
|
||||
|
||||
@ -29,20 +29,24 @@
|
||||
* @requires !vm.asan
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.platform
|
||||
* java.management
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* @build CheckContainerized jdk.test.whitebox.WhiteBox PrintContainerInfo
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox
|
||||
* @run driver TestMisc
|
||||
*/
|
||||
import jdk.internal.platform.Metrics;
|
||||
import jdk.test.lib.containers.docker.Common;
|
||||
import jdk.test.lib.containers.docker.DockerTestUtils;
|
||||
import jdk.test.lib.containers.docker.DockerRunOptions;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
|
||||
public class TestMisc {
|
||||
private static final Metrics metrics = Metrics.systemMetrics();
|
||||
private static final String imageName = Common.imageName("misc");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -58,6 +62,7 @@ public class TestMisc {
|
||||
testIsContainerized();
|
||||
testPrintContainerInfo();
|
||||
testPrintContainerInfoActiveProcessorCount();
|
||||
testPrintContainerInfoCPUShares();
|
||||
} finally {
|
||||
DockerTestUtils.removeDockerImage(imageName);
|
||||
}
|
||||
@ -94,8 +99,53 @@ public class TestMisc {
|
||||
checkContainerInfo(Common.run(opts));
|
||||
}
|
||||
|
||||
// Test the mapping function on cgroups v2. Should also pass on cgroups v1 as it's
|
||||
// a direct mapping there.
|
||||
private static void testPrintContainerInfoCPUShares() throws Exception {
|
||||
// Test won't work on cgv1 rootless podman since resource limits don't
|
||||
// work there.
|
||||
if ("cgroupv1".equals(metrics.getProvider()) &&
|
||||
DockerTestUtils.isPodman() &&
|
||||
DockerTestUtils.isRootless()) {
|
||||
throw new SkippedException("Resource limits required for testPrintContainerInfoCPUShares(). " +
|
||||
"This is cgv1 with podman in rootless mode. Test skipped.");
|
||||
}
|
||||
// Anything less than 1024 should return the back-mapped cpu-shares value without
|
||||
// rounding to next multiple of 1024 (on cg v2). Only ensure that we get
|
||||
// 'cpu_shares: <back-mapped-value>' over 'cpu_shares: no shares'.
|
||||
printContainerInfo(512, 1024, false);
|
||||
// Don't use 1024 exactly so as to avoid mapping to the unlimited/uset case.
|
||||
// Use a value > 100 post-mapping so as to hit the non-default branch: 1052 => 103
|
||||
printContainerInfo(1052, 1024, true);
|
||||
// need at least 2 CPU cores for this test to work
|
||||
if (Runtime.getRuntime().availableProcessors() >= 2) {
|
||||
printContainerInfo(2048, 2048, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printContainerInfo(int cpuShares, int expected, boolean numberMatch) throws Exception {
|
||||
Common.logNewTestCase("Test print_container_info() - cpu shares - given: " + cpuShares + ", expected: " + expected);
|
||||
|
||||
DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo");
|
||||
Common.addWhiteBoxOpts(opts);
|
||||
opts.addDockerOpts("--cpu-shares", Integer.valueOf(cpuShares).toString());
|
||||
|
||||
OutputAnalyzer out = Common.run(opts);
|
||||
String str = out.getOutput();
|
||||
boolean isCgroupV2 = str.contains("cgroupv2");
|
||||
// cg v1 maps cpu shares values verbatim. Only cg v2 uses the
|
||||
// mapping function.
|
||||
if (numberMatch) {
|
||||
int valueExpected = isCgroupV2 ? expected : cpuShares;
|
||||
out.shouldContain("cpu_shares: " + valueExpected);
|
||||
} else {
|
||||
// must not print "no shares"
|
||||
out.shouldNotContain("cpu_shares: no shares");
|
||||
}
|
||||
}
|
||||
|
||||
private static void testPrintContainerInfoActiveProcessorCount() throws Exception {
|
||||
Common.logNewTestCase("Test print_container_info()");
|
||||
Common.logNewTestCase("Test print_container_info() - ActiveProcessorCount");
|
||||
|
||||
DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo").addJavaOpts("-XX:ActiveProcessorCount=2");
|
||||
Common.addWhiteBoxOpts(opts);
|
||||
|
||||
@ -145,9 +145,13 @@ public class MetricsCpuTester {
|
||||
private static void testCpuShares(long shares) {
|
||||
Metrics metrics = Metrics.systemMetrics();
|
||||
if ("cgroupv2".equals(metrics.getProvider()) && shares < 1024) {
|
||||
// Adjust input shares for < 1024 cpu shares as the
|
||||
// impl. rounds up to the next multiple of 1024
|
||||
shares = 1024;
|
||||
// Don't assert for shares values less than 1024 as we don't
|
||||
// have a 1-to-1 mapping from the cgroup v2 value to the OCI
|
||||
// value.
|
||||
System.out.println("Debug: cgv2 - Got CPU shares of: " +
|
||||
metrics.getCpuShares() + " - Skipping assert.");
|
||||
System.out.println("TEST PASSED!!!");
|
||||
return;
|
||||
}
|
||||
long newShares = metrics.getCpuShares();
|
||||
if (newShares != shares) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user