mem/oom: check for multiple outcomes if overcommit_memory is 0 or 1
There is no guarantee that mmap() will fail before OOM tries to kill the
process if overcommit is set to 0 or 1. So allow such tests to
PASS if child is killed with SIGKILL.
This patch adds 2 parameters to oom()/testoom() functions:
retcode - expected child return code
allow_sigkill - flag which specifies if child killed by SIGKILL
is also PASS result
Child process will return 0 if all allocations succeeded,
otherwise it returns last errno.
Signed-off-by: Jan Stancek <jstancek@redhat.com>
Acked-by: Cyril Hrubis <chrubis@suse.cz>
diff --git a/testcases/kernel/mem/include/mem.h b/testcases/kernel/mem/include/mem.h
index 746164c..8fa2094 100644
--- a/testcases/kernel/mem/include/mem.h
+++ b/testcases/kernel/mem/include/mem.h
@@ -39,8 +39,8 @@
#define KSM 4
long overcommit;
-void oom(int testcase, int lite);
-void testoom(int mempolicy, int lite);
+void oom(int testcase, int lite, int retcode, int allow_sigkill);
+void testoom(int mempolicy, int lite, int retcode, int allow_sigkill);
/* KSM */
diff --git a/testcases/kernel/mem/lib/mem.c b/testcases/kernel/mem/lib/mem.c
index c7910db..61b1ee6 100644
--- a/testcases/kernel/mem/lib/mem.c
+++ b/testcases/kernel/mem/lib/mem.c
@@ -31,19 +31,17 @@
void *s;
tst_resm(TINFO, "allocating %ld bytes.", length);
+
s = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if (s == MAP_FAILED) {
- if (testcase == OVERCOMMIT && errno == ENOMEM)
- return 1;
- else
- tst_brkm(TBROK | TERRNO, cleanup, "mmap");
- }
+ if (s == MAP_FAILED)
+ return errno;
+
if (testcase == MLOCK && mlock(s, length) == -1)
- tst_brkm(TBROK | TERRNO, cleanup, "mlock");
+ return errno;
#ifdef HAVE_MADV_MERGEABLE
if (testcase == KSM && madvise(s, length, MADV_MERGEABLE) == -1)
- tst_brkm(TBROK | TERRNO, cleanup, "madvise");
+ return errno;
#endif
memset(s, '\a', length);
@@ -52,15 +50,34 @@
static void test_alloc(int testcase, int lite)
{
- if (lite)
- alloc_mem(TESTMEM + MB, testcase);
- else
- while (1)
- if (alloc_mem(LENGTH, testcase))
- return;
+ int ret;
+
+ if (lite) {
+ ret = alloc_mem(TESTMEM + MB, testcase);
+ } else {
+ ret = 0;
+ while (!ret)
+ ret = alloc_mem(LENGTH, testcase);
+ }
+ exit(ret);
}
-void oom(int testcase, int lite)
+/*
+ * oom - allocates memory according to specified testcase and checks
+ * desired outcome (e.g. child killed, operation failed with ENOMEM)
+ * @testcase: selects how child allocates memory
+ * valid choices are: OVERCOMMIT, NORMAL, MLOCK and KSM
+ * @lite: if non-zero, child makes only single TESTMEM+MB allocation
+ * if zero, child keeps allocating memory until it gets killed
+ * or some operation fails
+ * @retcode: expected return code of child process
+ * if matches child ret code, this function reports PASS,
+ * otherwise it reports FAIL
+ * @allow_sigkill: if zero and child is killed, this function reports FAIL
+ * if non-zero, then if child is killed by SIGKILL
+ * it is considered as PASS
+ */
+void oom(int testcase, int lite, int retcode, int allow_sigkill)
{
pid_t pid;
int status;
@@ -70,7 +87,6 @@
tst_brkm(TBROK | TERRNO, cleanup, "fork");
case 0:
test_alloc(testcase, lite);
- exit(0);
default:
break;
}
@@ -79,14 +95,22 @@
if (waitpid(-1, &status, 0) == -1)
tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
- if (testcase == OVERCOMMIT) {
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
- tst_resm(TFAIL, "the victim unexpectedly failed: %d",
- status);
+ if (WIFSIGNALED(status)) {
+ if (allow_sigkill && WTERMSIG(status) == SIGKILL) {
+ tst_resm(TPASS, "victim signalled: (%d) %s",
+ SIGKILL,
+ tst_strsig(SIGKILL));
+ } else {
+ tst_resm(TFAIL, "victim signalled: (%d) %s",
+ WTERMSIG(status),
+ tst_strsig(WTERMSIG(status)));
+ }
+ } else if (WIFEXITED(status) && WEXITSTATUS(status) == retcode) {
+ tst_resm(TPASS, "victim retcode: (%d) %s",
+ retcode, strerror(retcode));
} else {
- if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
- tst_resm(TFAIL, "the victim unexpectedly failed: %d",
- status);
+ tst_resm(TFAIL, "victim unexpectedly ended with retcode: %d, "
+ "expected: %d", WEXITSTATUS(status), retcode);
}
}
@@ -135,22 +159,22 @@
#endif
}
-void testoom(int mempolicy, int lite)
+void testoom(int mempolicy, int lite, int retcode, int allow_sigkill)
{
set_global_mempolicy(mempolicy);
tst_resm(TINFO, "start normal OOM testing.");
- oom(NORMAL, lite);
+ oom(NORMAL, lite, retcode, allow_sigkill);
tst_resm(TINFO, "start OOM testing for mlocked pages.");
- oom(MLOCK, lite);
+ oom(MLOCK, lite, retcode, allow_sigkill);
if (access(PATH_KSM, F_OK) == -1) {
tst_resm(TINFO, "KSM configuration is not enabled, "
"skip OOM test for KSM pags");
} else {
tst_resm(TINFO, "start OOM testing for KSM pages.");
- oom(KSM, lite);
+ oom(KSM, lite, retcode, allow_sigkill);
}
}
diff --git a/testcases/kernel/mem/oom/oom01.c b/testcases/kernel/mem/oom/oom01.c
index 076427c..4f57a9a 100644
--- a/testcases/kernel/mem/oom/oom01.c
+++ b/testcases/kernel/mem/oom/oom01.c
@@ -60,14 +60,17 @@
for (lc = 0; TEST_LOOPING(lc); lc++) {
tst_count = 0;
+ /* we expect mmap to fail before OOM is hit */
set_sys_tune("overcommit_memory", 2, 1);
- oom(OVERCOMMIT, 0);
+ oom(OVERCOMMIT, 0, ENOMEM, 0);
+ /* with overcommit_memory set to 0 or 1 there's no
+ * guarantee that mmap fails before OOM */
set_sys_tune("overcommit_memory", 0, 1);
- oom(OVERCOMMIT, 0);
+ oom(OVERCOMMIT, 0, ENOMEM, 1);
set_sys_tune("overcommit_memory", 1, 1);
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
}
cleanup();
tst_exit();
diff --git a/testcases/kernel/mem/oom/oom02.c b/testcases/kernel/mem/oom/oom02.c
index 3f17400..7a726c8 100644
--- a/testcases/kernel/mem/oom/oom02.c
+++ b/testcases/kernel/mem/oom/oom02.c
@@ -65,13 +65,13 @@
tst_count = 0;
tst_resm(TINFO, "OOM on MPOL_BIND mempolicy...");
- testoom(MPOL_BIND, 0);
+ testoom(MPOL_BIND, 0, ENOMEM, 1);
tst_resm(TINFO, "OOM on MPOL_INTERLEAVE mempolicy...");
- testoom(MPOL_INTERLEAVE, 0);
+ testoom(MPOL_INTERLEAVE, 0, ENOMEM, 1);
tst_resm(TINFO, "OOM on MPOL_PREFERRED mempolicy...");
- testoom(MPOL_PREFERRED, 0);
+ testoom(MPOL_PREFERRED, 0, ENOMEM, 1);
}
cleanup();
tst_exit();
diff --git a/testcases/kernel/mem/oom/oom03.c b/testcases/kernel/mem/oom/oom03.c
index dcb493c..dfc2a64 100644
--- a/testcases/kernel/mem/oom/oom03.c
+++ b/testcases/kernel/mem/oom/oom03.c
@@ -66,7 +66,7 @@
"%d", getpid());
SAFE_FILE_PRINTF(cleanup, MEMCG_LIMIT, "%ld", TESTMEM);
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
if (access(MEMCG_SW_LIMIT, F_OK) == -1) {
if (errno == ENOENT)
@@ -77,15 +77,15 @@
} else {
SAFE_FILE_PRINTF(cleanup, MEMCG_SW_LIMIT,
"%ld", TESTMEM);
- testoom(0, 1);
+ testoom(0, 1, ENOMEM, 1);
}
/* OOM for MEMCG with mempolicy */
if (is_numa(cleanup)) {
tst_resm(TINFO, "OOM on MEMCG & mempolicy...");
- testoom(MPOL_BIND, 0);
- testoom(MPOL_INTERLEAVE, 0);
- testoom(MPOL_PREFERRED, 0);
+ testoom(MPOL_BIND, 0, ENOMEM, 1);
+ testoom(MPOL_INTERLEAVE, 0, ENOMEM, 1);
+ testoom(MPOL_PREFERRED, 0, ENOMEM, 1);
}
}
cleanup();
diff --git a/testcases/kernel/mem/oom/oom04.c b/testcases/kernel/mem/oom/oom04.c
index 40360c3..c0f1f92 100644
--- a/testcases/kernel/mem/oom/oom04.c
+++ b/testcases/kernel/mem/oom/oom04.c
@@ -65,7 +65,7 @@
tst_count = 0;
tst_resm(TINFO, "OOM on CPUSET...");
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
if (is_numa(cleanup)) {
/*
@@ -76,7 +76,7 @@
write_cpuset_files(CPATH_NEW,
"memory_migrate", "1");
tst_resm(TINFO, "OOM on CPUSET with mem migrate:");
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
}
}
cleanup();
diff --git a/testcases/kernel/mem/oom/oom05.c b/testcases/kernel/mem/oom/oom05.c
index 7ca53ad..e12c2dd 100644
--- a/testcases/kernel/mem/oom/oom05.c
+++ b/testcases/kernel/mem/oom/oom05.c
@@ -66,7 +66,7 @@
tst_count = 0;
tst_resm(TINFO, "OOM on CPUSET & MEMCG...");
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
/*
* Under NUMA system, the migration of cpuset's memory
@@ -77,7 +77,7 @@
write_cpuset_files(CPATH_NEW, "memory_migrate", "1");
tst_resm(TINFO, "OOM on CPUSET & MEMCG with "
"cpuset.memory_migrate=1");
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
}
if (access(MEMCG_SW_LIMIT, F_OK) == -1) {
@@ -93,12 +93,12 @@
tst_resm(TINFO, "OOM on CPUSET & MEMCG with "
"special memswap limitation:");
SAFE_FILE_PRINTF(cleanup, MEMCG_SW_LIMIT, "%ld", TESTMEM);
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
tst_resm(TINFO, "OOM on CPUSET & MEMCG with "
"disabled memswap limitation:");
SAFE_FILE_PRINTF(cleanup, MEMCG_SW_LIMIT, "-1");
- testoom(0, 0);
+ testoom(0, 0, ENOMEM, 1);
}
}