[LTP] [PATCH] mmap22: Improve MAP_DROPPABLE test stability using mincore()
Li Wang
liwang@redhat.com
Wed Sep 17 09:57:29 CEST 2025
The current mmap22 test validates MAP_DROPPABLE by writing to the
allocated memory and checking for zero-filled pages under memory
pressure. However, this approach is unreliable because:
- Child process memory pressure (malloc + single writes) may not
reliably trigger cgroup memory reclaim.
- Checking memory content (alloc[i] == 0) is insufficient to
confirm kernel reclamation, as pages may remain resident but not
overwritten.
- Race conditions during child cleanup could leave residual cgroup
processes.
Error logs:
command: mmap22
tst_tmpdir.c:316: TINFO: Using /tmp/LTP_mmaxrtyKb as tmpdir (xfs filesystem)
tst_test.c:1953: TINFO: LTP version: 20250530
tst_kconfig.c:88: TINFO: Parsing kernel config '/lib/modules/6.12.0-130.1445_2041086229.el10.x86_64+rt/build/.config'
tst_test.c:1774: TINFO: Overall timeout per run is 0h 05m 54s
tst_test.c:1837: TINFO: Killed the leftover descendant processes
tst_test.c:1846: TINFO: If you are running on slow machine, try exporting LTP_TIMEOUT_MUL > 1
tst_test.c:1848: TBROK: Test killed! (timeout?)
tst_cgroup.c:1043: TBROK: unlinkat(5</sys/fs/cgroup/ltp>, 'test-92902', AT_REMOVEDIR): EBUSY (16)
...
This patch improves the test in the following ways:
* Use mincore() to precisely detect if MAP_DROPPABLE pages have
been reclaimed by the kernel.
* Replace the old child loop with a stronger memory pressure loop
(malloc + memset), ensuring cgroup limits are hit quickly.
* Use SAFE_KILL + SAFE_WAITPID for robust child cleanup.
* Extend runtime and add short sleeps to reduce busy looping and
stabilize test results.
* Record page size during setup for consistent use across functions.
As a result, the test becomes more stable, deterministic, and easier
to reproduce under different kernels and configurations.
Signed-off-by: Li Wang <liwang@redhat.com>
---
testcases/kernel/syscalls/mmap/mmap22.c | 78 ++++++++++++++++---------
1 file changed, 52 insertions(+), 26 deletions(-)
diff --git a/testcases/kernel/syscalls/mmap/mmap22.c b/testcases/kernel/syscalls/mmap/mmap22.c
index 1507fdfa7..b9db8e1b6 100644
--- a/testcases/kernel/syscalls/mmap/mmap22.c
+++ b/testcases/kernel/syscalls/mmap/mmap22.c
@@ -9,12 +9,19 @@
* Test mmap(2) with MAP_DROPPABLE flag.
*
* Test base on kernel selftests/mm/droppable.c
+ *
+ * Ensure that memory allocated with MAP_DROPPABLE can be reclaimed
+ * under memory pressure within a cgroup.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
#include "tst_test.h"
#include "lapi/mmap.h"
@@ -22,13 +29,24 @@
#define ALLOC_SIZE (128 * TST_MB)
static struct tst_cg_group *cg_child;
+static pid_t child;
+static int page_size;
+
+static void stress_child(void)
+{
+ for (;;) {
+ char *buf = malloc(page_size);
+ if (!buf)
+ exit(1);
+ memset(buf, 'B', page_size);
+ }
+}
static void test_mmap(void)
{
- size_t alloc_size = ALLOC_SIZE;
- size_t page_size = getpagesize();
char *alloc;
- pid_t child;
+ unsigned char *vec;
+ size_t npages = ALLOC_SIZE / page_size;
cg_child = tst_cg_group_mk(tst_cg, "child");
SAFE_CG_PRINTF(tst_cg, "memory.max", "%d", MEM_LIMIT);
@@ -38,38 +56,45 @@ static void test_mmap(void)
SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%d", MEM_LIMIT);
SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
- alloc = SAFE_MMAP(0, alloc_size, PROT_READ | PROT_WRITE,
+ alloc = SAFE_MMAP(0, ALLOC_SIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0);
- memset(alloc, 'A', alloc_size);
- for (size_t i = 0; i < alloc_size; i += page_size) {
- if (alloc[i] != 'A')
- tst_res(TFAIL, "memset failed");
- }
+ memset(alloc, 'A', ALLOC_SIZE);
+
+ vec = SAFE_MALLOC(npages);
child = SAFE_FORK();
- if (!child) {
- for (;;)
- *(char *)malloc(page_size) = 'B';
- }
+ if (!child)
+ stress_child();
- while (1) {
- for (size_t i = 0; i < alloc_size; i += page_size) {
- if (!tst_remaining_runtime()) {
- tst_res(TFAIL, "MAP_DROPPABLE did not drop memory within the timeout period.");
- goto kill_child;
- }
- if (!alloc[i]) {
- tst_res(TPASS, "MAP_DROPPABLE test pass.");
- goto kill_child;
+ for (;;) {
+ if (!tst_remaining_runtime()) {
+ tst_res(TFAIL, "MAP_DROPPABLE did not drop pages within timeout");
+ goto cleanup;
+ }
+
+ if (mincore(alloc, ALLOC_SIZE, vec) == -1)
+ tst_brk(TBROK | TERRNO, "mincore failed");
+
+ for (size_t i = 0; i < npages; i++) {
+ if (!(vec[i] & 1)) {
+ tst_res(TPASS, "MAP_DROPPABLE page reclaimed by kernel");
+ goto cleanup;
}
}
+
+ usleep(100000);
}
-kill_child:
- SAFE_KILL(child, SIGKILL);
- SAFE_WAITPID(child, NULL, 0);
- SAFE_MUNMAP(alloc, alloc_size);
+cleanup:
+ if (child > 0) {
+ SAFE_KILL(child, SIGKILL);
+ SAFE_WAITPID(child, NULL, 0);
+ }
+ SAFE_MUNMAP(alloc, ALLOC_SIZE);
+ free(vec);
+ SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid());
+ cg_child = tst_cg_group_rm(cg_child);
}
static void setup(void)
@@ -84,6 +109,7 @@ static void setup(void)
tst_brk(TBROK | TERRNO, "mmap() MAP_DROPPABLE failed");
SAFE_MUNMAP(addr, 1);
+ page_size = getpagesize();
}
static void cleanup(void)
--
2.51.0
More information about the ltp
mailing list