Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
8250984: Memory Docker tests fail on some Linux kernels w/o cgroupv1 …
…swap limit capabilities

Reviewed-by: yan
Backport-of: 0187567704d79ecf394d4cb656d0ba4c886351f1
  • Loading branch information
Ekaterina Vergizova committed May 13, 2021
1 parent 631defc commit 4d0f905
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 56 deletions.
Expand Up @@ -28,6 +28,7 @@
public class CgroupV1MemorySubSystemController extends CgroupV1SubsystemController {

private boolean hierarchical;
private boolean swapenabled;

public CgroupV1MemorySubSystemController(String root, String mountPoint) {
super(root, mountPoint);
Expand All @@ -41,4 +42,11 @@ void setHierarchical(boolean hierarchical) {
this.hierarchical = hierarchical;
}

}
boolean isSwapEnabled() {
return swapenabled;
}

void setSwapEnabled(boolean swapenabled) {
this.swapenabled = swapenabled;
}
}
Expand Up @@ -195,12 +195,20 @@ private static void setPath(CgroupV1Subsystem subsystem, CgroupV1SubsystemContro
CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller;
boolean isHierarchial = getHierarchical(memorySubSystem);
memorySubSystem.setHierarchical(isHierarchial);
boolean isSwapEnabled = getSwapEnabled(memorySubSystem);
memorySubSystem.setSwapEnabled(isSwapEnabled);
}
subsystem.setActiveSubSystems();
}
}


private static boolean getSwapEnabled(CgroupV1MemorySubSystemController controller) {
long retval = getLongValue(controller, "memory.memsw.limit_in_bytes");
return retval > 0;
}


private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) {
long hierarchical = getLongValue(controller, "memory.use_hierarchy");
return hierarchical > 0;
Expand Down Expand Up @@ -431,10 +439,16 @@ public long getTcpMemoryUsage() {
}

public long getMemoryAndSwapFailCount() {
if (!memory.isSwapEnabled()) {
return getMemoryFailCount();
}
return getLongValue(memory, "memory.memsw.failcnt");
}

public long getMemoryAndSwapLimit() {
if (!memory.isSwapEnabled()) {
return getMemoryLimit();
}
long retval = getLongValue(memory, "memory.memsw.limit_in_bytes");
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
if (memory.isHierarchical()) {
Expand All @@ -450,10 +464,16 @@ public long getMemoryAndSwapLimit() {
}

public long getMemoryAndSwapMaxUsage() {
if (!memory.isSwapEnabled()) {
return getMemoryMaxUsage();
}
return getLongValue(memory, "memory.memsw.max_usage_in_bytes");
}

public long getMemoryAndSwapUsage() {
if (!memory.isSwapEnabled()) {
return getMemoryUsage();
}
return getLongValue(memory, "memory.memsw.usage_in_bytes");
}

Expand Down
2 changes: 1 addition & 1 deletion test/hotspot/jtreg/containers/cgroup/PlainRead.java
Expand Up @@ -45,7 +45,7 @@ static public void noMatch(OutputAnalyzer oa, String what, String value) {
oa.shouldNotMatch("^.*" + what + " *" + value + ".*$");
}

static final String good_value = "(\\d+|-1|Unlimited)";
static final String good_value = "(\\d+|-1|-2|Unlimited)";
static final String bad_value = "(failed)";

static final String[] variables = {"Memory Limit is:", "CPU Shares is:", "CPU Quota is:", "CPU Period is:", "active_processor_count:"};
Expand Down
20 changes: 13 additions & 7 deletions test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java
Expand Up @@ -153,18 +153,24 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation,
out.shouldHaveExitValue(0)
.shouldContain("Checking OperatingSystemMXBean")
.shouldContain("OperatingSystemMXBean.getTotalPhysicalMemorySize: " + expectedMemory)
.shouldMatch("OperatingSystemMXBean\\.getFreePhysicalMemorySize: [1-9][0-9]+")
.shouldContain("OperatingSystemMXBean.getTotalMemorySize: " + expectedMemory)
.shouldMatch("OperatingSystemMXBean\\.getFreeMemorySize: [1-9][0-9]+")
.shouldMatch("OperatingSystemMXBean\\.getFreeSwapSpaceSize: [1-9][0-9]+");
// in case of warnings like : "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap."
// the getTotalSwapSpaceSize returns the system values as the container setup isn't supported in that case.
.shouldMatch("OperatingSystemMXBean\\.getFreePhysicalMemorySize: [1-9][0-9]+");

// in case of warnings like : "Your kernel does not support swap limit capabilities
// or the cgroup is not mounted. Memory limited without swap."
// the getTotalSwapSpaceSize and getFreeSwapSpaceSize return the system
// values as the container setup isn't supported in that case.
try {
out.shouldContain("OperatingSystemMXBean.getTotalSwapSpaceSize: " + expectedSwap);
} catch(RuntimeException ex) {
out.shouldMatch("OperatingSystemMXBean.getTotalSwapSpaceSize: [1-9][0-9]+");
out.shouldContain("Metrics.getMemoryLimit() == " + expectedMemory);
out.shouldContain("Metrics.getMemoryAndSwapLimit() == -1");
out.shouldMatch("OperatingSystemMXBean.getTotalSwapSpaceSize: [0-9]+");
}

try {
out.shouldMatch("OperatingSystemMXBean\\.getFreeSwapSpaceSize: [1-9][0-9]+");
} catch(RuntimeException ex) {
out.shouldMatch("OperatingSystemMXBean\\.getFreeSwapSpaceSize: 0");
}
}

Expand Down
64 changes: 37 additions & 27 deletions test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
Expand Up @@ -66,34 +66,42 @@ private static void testMemoryLimit(String value) {
}

private static void testMemoryFailCount() {
long count = Metrics.systemMetrics().getMemoryFailCount();

// Allocate 512M of data
byte[][] bytes = new byte[64][];
boolean atLeastOneAllocationWorked = false;
for (int i = 0; i < 64; i++) {
try {
bytes[i] = new byte[8 * 1024 * 1024];
atLeastOneAllocationWorked = true;
// Break out as soon as we see an increase in failcount
// to avoid getting killed by the OOM killer.
if (Metrics.systemMetrics().getMemoryFailCount() > count) {
long memAndSwapLimit = Metrics.systemMetrics().getMemoryAndSwapLimit();
long memLimit = Metrics.systemMetrics().getMemoryLimit();

// We need swap to execute this test or will SEGV
if (memAndSwapLimit <= memLimit) {
System.out.println("No swap memory limits, test case skipped");
} else {
long count = Metrics.systemMetrics().getMemoryFailCount();

// Allocate 512M of data
byte[][] bytes = new byte[64][];
boolean atLeastOneAllocationWorked = false;
for (int i = 0; i < 64; i++) {
try {
bytes[i] = new byte[8 * 1024 * 1024];
atLeastOneAllocationWorked = true;
// Break out as soon as we see an increase in failcount
// to avoid getting killed by the OOM killer.
if (Metrics.systemMetrics().getMemoryFailCount() > count) {
break;
}
} catch (Error e) { // OOM error
break;
}
} catch (Error e) { // OOM error
break;
}
}
if (!atLeastOneAllocationWorked) {
System.out.println("Allocation failed immediately. Ignoring test!");
return;
}
// Be sure bytes allocations don't get optimized out
System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length);
if (Metrics.systemMetrics().getMemoryFailCount() <= count) {
throw new RuntimeException("Memory fail count : new : ["
+ Metrics.systemMetrics().getMemoryFailCount() + "]"
+ ", old : [" + count + "]");
if (!atLeastOneAllocationWorked) {
System.out.println("Allocation failed immediately. Ignoring test!");
return;
}
// Be sure bytes allocations don't get optimized out
System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length);
if (Metrics.systemMetrics().getMemoryFailCount() <= count) {
throw new RuntimeException("Memory fail count : new : ["
+ Metrics.systemMetrics().getMemoryFailCount() + "]"
+ ", old : [" + count + "]");
}
}
System.out.println("TEST PASSED!!!");
}
Expand Down Expand Up @@ -131,10 +139,12 @@ private static void testKernelMemoryLimit(String value) {
private static void testMemoryAndSwapLimit(String memory, String memAndSwap) {
long expectedMem = getMemoryValue(memory);
long expectedMemAndSwap = getMemoryValue(memAndSwap);
long actualMemAndSwap = Metrics.systemMetrics().getMemoryAndSwapLimit();

if (expectedMem != Metrics.systemMetrics().getMemoryLimit()
|| expectedMemAndSwap != Metrics.systemMetrics().getMemoryAndSwapLimit()) {
System.err.println("Memory and swap limit not equal, expected : ["
|| (expectedMemAndSwap != actualMemAndSwap
&& expectedMem != actualMemAndSwap)) {
throw new RuntimeException("Memory and swap limit not equal, expected : ["
+ expectedMem + ", " + expectedMemAndSwap + "]"
+ ", got : [" + Metrics.systemMetrics().getMemoryLimit()
+ ", " + Metrics.systemMetrics().getMemoryAndSwapLimit() + "]");
Expand Down
44 changes: 24 additions & 20 deletions test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV1.java
Expand Up @@ -293,29 +293,33 @@ public void testMemorySubsystem() {
}

// Memory and Swap
oldVal = metrics.getMemoryAndSwapFailCount();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.failcnt");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.failcnt", oldVal, newVal);
}

oldVal = metrics.getMemoryAndSwapLimit();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.limit_in_bytes");
newVal = newVal > unlimited_minimum ? CgroupSubsystem.LONG_RETVAL_UNLIMITED : newVal;
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.limit_in_bytes", oldVal, newVal);
}
// Skip swap tests if no swap is configured.
if (metrics.getMemoryAndSwapLimit() > metrics.getMemoryLimit()) {
oldVal = metrics.getMemoryAndSwapFailCount();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.failcnt");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.failcnt", oldVal, newVal);
}

oldVal = metrics.getMemoryAndSwapMaxUsage();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.max_usage_in_bytes");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.max_usage_in_bytes", oldVal, newVal);
}
oldVal = metrics.getMemoryAndSwapLimit();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.limit_in_bytes");
newVal = newVal > unlimited_minimum ? CgroupSubsystem.LONG_RETVAL_UNLIMITED : newVal;
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.limit_in_bytes", oldVal, newVal);
}

oldVal = metrics.getMemoryAndSwapUsage();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.usage_in_bytes");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.usage_in_bytes", oldVal, newVal);
oldVal = metrics.getMemoryAndSwapMaxUsage();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.max_usage_in_bytes");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.max_usage_in_bytes", oldVal, newVal);
}

oldVal = metrics.getMemoryAndSwapUsage();
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.usage_in_bytes");
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
fail(Controller.MEMORY, "memory.memsw.usage_in_bytes", oldVal, newVal);
}
}

oldVal = metrics.getMemorySoftLimit();
Expand Down

1 comment on commit 4d0f905

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.