Skip to content

Commit 1a01a9a

Browse files
committed
Backport 824af4cc8d7810d337098c05d748ec6b41caa802
1 parent 524170d commit 1a01a9a

File tree

5 files changed

+119
-44
lines changed

5 files changed

+119
-44
lines changed

src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) {
7676
*/
7777
jlong CgroupV1MemoryController::uses_mem_hierarchy() {
7878
GET_CONTAINER_INFO(jlong, this, "/memory.use_hierarchy",
79-
"Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
79+
"Use Hierarchy is: ", JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
8080
return use_hierarchy;
8181
}
8282

@@ -90,16 +90,14 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) {
9090

9191
jlong CgroupV1Subsystem::read_memory_limit_in_bytes() {
9292
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.limit_in_bytes",
93-
"Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
93+
"Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memlimit);
9494

9595
if (memlimit >= os::Linux::physical_memory()) {
9696
log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
9797
CgroupV1MemoryController* mem_controller = reinterpret_cast<CgroupV1MemoryController*>(_memory->controller());
9898
if (mem_controller->is_hierarchical()) {
99-
const char* matchline = "hierarchical_memory_limit";
100-
const char* format = "%s " JULONG_FORMAT;
101-
GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline,
102-
"Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit)
99+
GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", "hierarchical_memory_limit",
100+
"Hierarchical Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, hier_memlimit)
103101
if (hier_memlimit >= os::Linux::physical_memory()) {
104102
log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
105103
} else {
@@ -113,54 +111,72 @@ jlong CgroupV1Subsystem::read_memory_limit_in_bytes() {
113111
}
114112
}
115113

116-
jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() {
114+
/* read_mem_swap
115+
*
116+
* Determine the memory and swap limit metric. Returns a positive limit value strictly
117+
* lower than the physical memory and swap limit iff there is a limit. Otherwise a
118+
* negative value is returned indicating the determined status.
119+
*
120+
* returns:
121+
* * A number > 0 if the limit is available and lower than a physical upper bound.
122+
* * OSCONTAINER_ERROR if the limit cannot be retrieved (i.e. not supported) or
123+
* * -1 if there isn't any limit in place (note: includes values which exceed a physical
124+
* upper bound)
125+
*/
126+
jlong CgroupV1Subsystem::read_mem_swap() {
117127
julong host_total_memsw;
118128
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.limit_in_bytes",
119-
"Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
129+
"Memory and Swap Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memswlimit);
120130
host_total_memsw = os::Linux::host_swap() + os::Linux::physical_memory();
121131
if (memswlimit >= host_total_memsw) {
122132
log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
123133
CgroupV1MemoryController* mem_controller = reinterpret_cast<CgroupV1MemoryController*>(_memory->controller());
124134
if (mem_controller->is_hierarchical()) {
125135
const char* matchline = "hierarchical_memsw_limit";
126-
const char* format = "%s " JULONG_FORMAT;
127136
GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline,
128-
"Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memswlimit)
137+
"Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, JULONG_FORMAT, hier_memswlimit)
129138
if (hier_memswlimit >= host_total_memsw) {
130139
log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
131140
} else {
132-
jlong swappiness = read_mem_swappiness();
133-
if (swappiness == 0) {
134-
const char* matchmemline = "hierarchical_memory_limit";
135-
GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchmemline,
136-
"Hierarchical Memory Limit is : " JULONG_FORMAT, format, hier_memlimit)
137-
log_trace(os, container)("Memory and Swap Limit has been reset to " JULONG_FORMAT " because swappiness is 0", hier_memlimit);
138-
return (jlong)hier_memlimit;
139-
}
140141
return (jlong)hier_memswlimit;
141142
}
142143
}
143144
return (jlong)-1;
144145
} else {
145-
jlong swappiness = read_mem_swappiness();
146-
if (swappiness == 0) {
147-
jlong memlimit = read_memory_limit_in_bytes();
148-
log_trace(os, container)("Memory and Swap Limit has been reset to " JULONG_FORMAT " because swappiness is 0", memlimit);
149-
return memlimit;
150-
}
151146
return (jlong)memswlimit;
152147
}
153148
}
154149

150+
jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() {
151+
jlong memory_swap = read_mem_swap();
152+
if (memory_swap == -1) {
153+
return memory_swap;
154+
}
155+
// If there is a swap limit, but swappiness == 0, reset the limit
156+
// to the memory limit. Do the same for cases where swap isn't
157+
// supported.
158+
jlong swappiness = read_mem_swappiness();
159+
if (swappiness == 0 || memory_swap == OSCONTAINER_ERROR) {
160+
jlong memlimit = read_memory_limit_in_bytes();
161+
if (memory_swap == OSCONTAINER_ERROR) {
162+
log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swap is not supported", memlimit);
163+
} else {
164+
log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swappiness is 0", memlimit);
165+
}
166+
return memlimit;
167+
}
168+
return memory_swap;
169+
}
170+
155171
jlong CgroupV1Subsystem::read_mem_swappiness() {
156172
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.swappiness",
157-
"Swappiness is: " JULONG_FORMAT, JULONG_FORMAT, swappiness);
173+
"Swappiness is: ", JULONG_FORMAT, JULONG_FORMAT, swappiness);
158174
return swappiness;
159175
}
160176

161177
jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() {
162178
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.soft_limit_in_bytes",
163-
"Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
179+
"Memory Soft Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
164180
if (memsoftlimit >= os::Linux::physical_memory()) {
165181
log_trace(os, container)("Memory Soft Limit is: Unlimited");
166182
return (jlong)-1;
@@ -180,7 +196,7 @@ jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() {
180196
*/
181197
jlong CgroupV1Subsystem::memory_usage_in_bytes() {
182198
GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.usage_in_bytes",
183-
"Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
199+
"Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memusage);
184200
return memusage;
185201
}
186202

@@ -194,20 +210,20 @@ jlong CgroupV1Subsystem::memory_usage_in_bytes() {
194210
*/
195211
jlong CgroupV1Subsystem::memory_max_usage_in_bytes() {
196212
GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.max_usage_in_bytes",
197-
"Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage);
213+
"Maximum Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memmaxusage);
198214
return memmaxusage;
199215
}
200216

201217

202218
jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() {
203219
GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.usage_in_bytes",
204-
"Kernel Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, kmem_usage);
220+
"Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_usage);
205221
return kmem_usage;
206222
}
207223

208224
jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() {
209225
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.kmem.limit_in_bytes",
210-
"Kernel Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, kmem_limit);
226+
"Kernel Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, kmem_limit);
211227
if (kmem_limit >= os::Linux::physical_memory()) {
212228
return (jlong)-1;
213229
}
@@ -216,7 +232,7 @@ jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() {
216232

217233
jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() {
218234
GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.max_usage_in_bytes",
219-
"Maximum Kernel Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, kmem_max_usage);
235+
"Maximum Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_max_usage);
220236
return kmem_max_usage;
221237
}
222238

@@ -254,13 +270,13 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() {
254270
*/
255271
int CgroupV1Subsystem::cpu_quota() {
256272
GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_quota_us",
257-
"CPU Quota is: %d", "%d", quota);
273+
"CPU Quota is: ", "%d", "%d", quota);
258274
return quota;
259275
}
260276

261277
int CgroupV1Subsystem::cpu_period() {
262278
GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_period_us",
263-
"CPU Period is: %d", "%d", period);
279+
"CPU Period is: ", "%d", "%d", period);
264280
return period;
265281
}
266282

@@ -276,7 +292,7 @@ int CgroupV1Subsystem::cpu_period() {
276292
*/
277293
int CgroupV1Subsystem::cpu_shares() {
278294
GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.shares",
279-
"CPU Shares is: %d", "%d", shares);
295+
"CPU Shares is: ", "%d", "%d", shares);
280296
// Convert 1024 to no shares setup
281297
if (shares == 1024) return -1;
282298

@@ -286,10 +302,7 @@ int CgroupV1Subsystem::cpu_shares() {
286302

287303
char* CgroupV1Subsystem::pids_max_val() {
288304
GET_CONTAINER_INFO_CPTR(cptr, _pids, "/pids.max",
289-
"Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024);
290-
if (pidsmax == NULL) {
291-
return NULL;
292-
}
305+
"Maximum number of tasks is: %s", "%1023s", pidsmax, 1024);
293306
return os::strdup(pidsmax);
294307
}
295308

@@ -319,6 +332,6 @@ jlong CgroupV1Subsystem::pids_max() {
319332
jlong CgroupV1Subsystem::pids_current() {
320333
if (_pids == NULL) return OSCONTAINER_ERROR;
321334
GET_CONTAINER_INFO(jlong, _pids, "/pids.current",
322-
"Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current);
335+
"Current number of tasks is: ", JLONG_FORMAT, JLONG_FORMAT, pids_current);
323336
return pids_current;
324337
}

src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class CgroupV1Subsystem: public CgroupSubsystem {
114114
char * pids_max_val();
115115

116116
jlong read_mem_swappiness();
117+
jlong read_mem_swap();
117118

118119
public:
119120
CgroupV1Subsystem(CgroupV1Controller* cpuset,

src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ char* CgroupV2Subsystem::mem_soft_limit_val() {
164164
// without also setting a memory limit is not allowed.
165165
jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() {
166166
char* mem_swp_limit_str = mem_swp_limit_val();
167+
if (mem_swp_limit_str == nullptr) {
168+
// Some container tests rely on this trace logging to happen.
169+
log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR);
170+
// swap disabled at kernel level, treat it as no swap
171+
return read_memory_limit_in_bytes();
172+
}
167173
jlong swap_limit = limit_from_str(mem_swp_limit_str);
168174
if (swap_limit >= 0) {
169175
jlong memory_limit = read_memory_limit_in_bytes();

test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@
3333
* java.base/jdk.internal.platform
3434
* java.management
3535
* jdk.jartool/sun.tools.jar
36-
* @build AttemptOOM sun.hotspot.WhiteBox PrintContainerInfo CheckOperatingSystemMXBean
37-
* @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
36+
* @build AttemptOOM jdk.test.whitebox.WhiteBox PrintContainerInfo CheckOperatingSystemMXBean
37+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox
3838
* @run main/othervm -Xbootclasspath/a:whitebox.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestMemoryAwareness
3939
*/
4040
import java.util.function.Consumer;
4141
import jdk.test.lib.containers.docker.Common;
4242
import jdk.test.lib.containers.docker.DockerRunOptions;
4343
import jdk.test.lib.containers.docker.DockerTestUtils;
44-
import sun.hotspot.WhiteBox;
44+
import jdk.test.whitebox.WhiteBox;
4545
import jdk.test.lib.process.OutputAnalyzer;
4646

4747
import static jdk.test.lib.Asserts.assertNotNull;
@@ -75,6 +75,9 @@ public static void main(String[] args) throws Exception {
7575

7676
testMemorySoftLimit("500m", "524288000");
7777
testMemorySoftLimit("1g", "1073741824");
78+
testMemorySwapLimitSanity();
79+
80+
testMemorySwapNotSupported("500m", "520m", "512000 k", "532480 k");
7881

7982
// Add extra 10 Mb to allocator limit, to be sure to cause OOM
8083
testOOM("256m", 256 + 10);
@@ -153,6 +156,54 @@ private static void testMemorySoftLimit(String valueToSet, String expectedTraceV
153156
.shouldMatch("Memory Soft Limit.*" + expectedTraceValue);
154157
}
155158

159+
/*
160+
* Verifies that PrintContainerInfo prints the memory
161+
* limit - without swap - iff swap is disabled (e.g. via swapaccount=0). It must
162+
* not print 'not supported' for that value in that case. It'll always pass
163+
* on systems with swap accounting enabled.
164+
*/
165+
private static void testMemorySwapNotSupported(String valueToSet, String swapToSet, String expectedMem, String expectedSwap)
166+
throws Exception {
167+
Common.logNewTestCase("memory swap not supported: " + valueToSet);
168+
169+
DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo");
170+
Common.addWhiteBoxOpts(opts);
171+
opts.addDockerOpts("--memory=" + valueToSet);
172+
opts.addDockerOpts("--memory-swap=" + swapToSet);
173+
174+
Common.run(opts)
175+
.shouldMatch("memory_limit_in_bytes:.*" + expectedMem)
176+
.shouldNotMatch("memory_and_swap_limit_in_bytes:.*not supported")
177+
// On systems with swapaccount=0 this returns the memory limit.
178+
// On systems with swapaccount=1 this returns the set memory+swap value.
179+
.shouldMatch("memory_and_swap_limit_in_bytes:.*(" + expectedMem + "|" + expectedSwap + ")");
180+
}
181+
182+
/*
183+
* This test verifies that no confusingly large positive numbers get printed on
184+
* systems with swapaccount=0 kernel option. On some systems -2 were converted
185+
* to unsigned long and printed that way. Ensure this oddity doesn't occur.
186+
*/
187+
private static void testMemorySwapLimitSanity() throws Exception {
188+
String valueToSet = "500m";
189+
String expectedTraceValue = "524288000";
190+
Common.logNewTestCase("memory swap sanity: " + valueToSet);
191+
192+
DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo");
193+
Common.addWhiteBoxOpts(opts);
194+
opts.addDockerOpts("--memory=" + valueToSet);
195+
opts.addDockerOpts("--memory-swap=" + valueToSet);
196+
197+
String neg2InUnsignedLong = "18446744073709551614";
198+
199+
Common.run(opts)
200+
.shouldMatch("Memory Limit is:.*" + expectedTraceValue)
201+
// Either for cgroup v1: a_1) same as memory limit, or b_1) -2 on systems with swapaccount=0
202+
// Either for cgroup v2: a_2) 0, or b_2) -2 on systems with swapaccount=0
203+
.shouldMatch("Memory and Swap Limit is:.*(" + expectedTraceValue + "|-2|0)")
204+
.shouldNotMatch("Memory and Swap Limit is:.*" + neg2InUnsignedLong);
205+
}
206+
156207

157208
// provoke OOM inside the container, see how VM reacts
158209
private static void testOOM(String dockerMemLimit, int sizeToAllocInMb) throws Exception {
@@ -215,7 +266,10 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation,
215266
out.shouldHaveExitValue(0)
216267
.shouldContain("Checking OperatingSystemMXBean")
217268
.shouldContain("OperatingSystemMXBean.getTotalPhysicalMemorySize: " + expectedMemory)
269+
.shouldContain("OperatingSystemMXBean.getTotalMemorySize: " + expectedMemory)
270+
.shouldMatch("OperatingSystemMXBean\\.getFreeMemorySize: [1-9][0-9]+")
218271
.shouldMatch("OperatingSystemMXBean\\.getFreePhysicalMemorySize: [1-9][0-9]+");
272+
219273
// in case of warnings like : "Your kernel does not support swap limit capabilities
220274
// or the cgroup is not mounted. Memory limited without swap."
221275
// the getTotalSwapSpaceSize either returns the system (or host) values, or 0
@@ -230,6 +284,7 @@ private static void testOperatingSystemMXBeanAwareness(String memoryAllocation,
230284
String hostSwap = getHostSwap();
231285
out.shouldMatch("OperatingSystemMXBean.getTotalSwapSpaceSize: (0|" + hostSwap + ")");
232286
}
287+
233288
try {
234289
out.shouldMatch("OperatingSystemMXBean\\.getFreeSwapSpaceSize: [1-9][0-9]+");
235290
} catch(RuntimeException ex) {

test/hotspot/jtreg/containers/docker/TestMemoryWithCgroupV1.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ private static void testMemoryLimitWithSwappiness(String dockerMemLimit, String
8585
// capabilities or the cgroup is not mounted. Memory limited without swap."
8686
// we only have Memory and Swap Limit is: <huge integer> in the output
8787
try {
88-
if (out.getOutput().contains("memory_and_swap_limit_in_bytes: not supported")) {
89-
System.out.println("memory_and_swap_limit_in_bytes not supported, avoiding Memory and Swap Limit check");
88+
if (out.getOutput().contains("Memory and Swap Limit is: -2")) {
89+
System.out.println("System doesn't seem to allow swap, avoiding Memory and Swap Limit check");
9090
} else {
9191
out.shouldContain("Memory and Swap Limit is: " + expectedReadLimit)
9292
.shouldContain(

0 commit comments

Comments
 (0)