From: Garrett C. <su...@li...> - 2011-01-18 09:56:41
|
The branch, master, has been updated via 6a4f5725bc3543d2f7c20d59f0adeac608e09ad1 (commit) via ddf33085b8e0f9ee62f39e5cf9c7eced27e4a7ad (commit) from 399e8999e08440623fd27f6d32c89717195607c1 (commit) - Log ----------------------------------------------------------------- commit 6a4f5725bc3543d2f7c20d59f0adeac608e09ad1 Author: Garrett Cooper <yan...@gm...> Date: Tue Jan 18 01:55:30 2011 -0800 Add testcase to test OOM for memcg. Signed-off-by: CAI Qian <ca...@re...> commit ddf33085b8e0f9ee62f39e5cf9c7eced27e4a7ad Author: Garrett Cooper <yan...@gm...> Date: Tue Jan 18 01:54:46 2011 -0800 Add testcase for OOM ala NUMA with memcg. Signed-off-by: CAI Qian <ca...@re...> ----------------------------------------------------------------------- Summary of changes: testcases/kernel/mem/oom/{oom01.c => oom03.c} | 51 ++-- testcases/kernel/mem/oom/oom04.c | 400 +++++++++++++++++++++++++ 2 files changed, 432 insertions(+), 19 deletions(-) copy testcases/kernel/mem/oom/{oom01.c => oom03.c} (71%) create mode 100644 testcases/kernel/mem/oom/oom04.c diff --git a/testcases/kernel/mem/oom/oom01.c b/testcases/kernel/mem/oom/oom03.c similarity index 71% copy from testcases/kernel/mem/oom/oom01.c copy to testcases/kernel/mem/oom/oom03.c index 8fb58c3..93c0f79 100644 --- a/testcases/kernel/mem/oom/oom01.c +++ b/testcases/kernel/mem/oom/oom03.c @@ -1,5 +1,5 @@ /* - * Out Of Memory (OOM) + * Out Of Memory (OOM) for Memory Resource Controller * * The program is designed to cope with unpredictable like amount and * system physical memory, swap size and other VMM technology like KSM, @@ -30,17 +30,14 @@ */ #include <sys/types.h> #include <sys/stat.h> -#include <asm/types.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> -#include <stdlib.h> -#include <unistd.h> #include "test.h" #include "usctest.h" #include "lib/oom.h" -char *TCID = "oom01"; +char *TCID = "oom03"; int TST_TOTAL = 1; extern int Tst_count; @@ -50,6 +47,7 @@ int main(int argc, char *argv[]) { char *msg; int lc, fd; + char buf[BUFSIZ], mem[BUFSIZ]; msg = parse_opts(argc, argv, NULL, NULL); if (msg != NULL) @@ -57,34 +55,45 @@ int main(int argc, char *argv[]) #ifdef __i386__ tst_brkm(TCONF, tst_exit, - "this test is not designed for 32-bit system."); + "test is not designed for 32-bit system."); #endif /* __i386__ */ setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { Tst_count = 0; - tst_resm(TINFO, "start testing overcommit_memory=2."); fd = open(SYSFS_OVER, O_WRONLY); if (fd == -1) tst_brkm(TBROK|TERRNO, cleanup, "open"); - if (write(fd, "2", 1) != 1) + if (write(fd, "1", 1) != 1) tst_brkm(TBROK|TERRNO, cleanup, "write"); - oom(OVERCOMMIT, 0, 0); + close(fd); - tst_resm(TINFO, "start testing overcommit_memory=0."); - if (lseek(fd, SEEK_SET, 0) == -1) - tst_brkm(TBROK|TERRNO, cleanup, "lseek"); - if (write(fd, "0", 1) != 1) - tst_brkm(TBROK|TERRNO, cleanup, "write"); - oom(OVERCOMMIT, 0, 0); + fd = open(MEMCG_PATH_NEW "/memory.limit_in_bytes", O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + sprintf(mem, "%ld", TESTMEM); + if (write(fd, mem, strlen(mem)) != strlen(mem)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); - if (lseek(fd, SEEK_SET, 0) == -1) - tst_brkm(TBROK|TERRNO, cleanup, "lseek"); - if (write(fd, "1", 1) != 1) - tst_brkm(TBROK|TERRNO, cleanup, "write"); + fd = open(MEMCG_PATH_NEW "/tasks", O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + snprintf(buf, BUFSIZ, "%d", getpid()); + if (write(fd, buf, strlen(buf)) != strlen(buf)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); close(fd); testoom(0, 0, 0); + + fd = open(MEMCG_PATH_NEW "/memory.memsw.limit_in_bytes", + O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + if (write(fd, mem, strlen(mem)) != strlen(mem)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + testoom(0, 1, 0); } cleanup(); } @@ -102,6 +111,8 @@ void setup(void) if (read(fd, &overcommit, 1) != 1) tst_brkm(TBROK|TERRNO, cleanup, "read"); close(fd); + + mount_mem("memcg", "cgroup", "memory", MEMCG_PATH, MEMCG_PATH_NEW); } void cleanup(void) @@ -115,6 +126,8 @@ void cleanup(void) tst_brkm(TBROK|TERRNO, cleanup, "write"); close(fd); + umount_mem(MEMCG_PATH, MEMCG_PATH_NEW); + TEST_CLEANUP; tst_exit(); } diff --git a/testcases/kernel/mem/oom/oom04.c b/testcases/kernel/mem/oom/oom04.c new file mode 100644 index 0000000..4fec3cf --- /dev/null +++ b/testcases/kernel/mem/oom/oom04.c @@ -0,0 +1,400 @@ +/* + * Out Of Memory (OOM) for Memory Resource Controller and NUMA + * + * The program is designed to cope with unpredictable like amount and + * system physical memory, swap size and other VMM technology like KSM, + * memcg, memory hotplug and so on which may affect the OOM + * behaviours. It simply increase the memory consumption 3G each time + * until all the available memory is consumed and OOM is triggered. + * + * Copyright (C) 2010 Red Hat, Inc. + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it + * is free of the rightful claim of any third person regarding + * infringement or the like. Any license provided herein, whether + * implied or otherwise, applies only to this software file. Patent + * licenses, if any, provided herein do not apply to combinations of + * this program with other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include "test.h" +#include "usctest.h" +#include "config.h" + +char *TCID = "oom04"; +int TST_TOTAL = 1; +extern int Tst_count; + +#if HAVE_NUMA_H && HAVE_LINUX_MEMPOLICY_H && HAVE_NUMAIF_H \ + && HAVE_MPOL_CONSTANTS +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/mount.h> +#include <numaif.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <stdarg.h> + +#define MAXNODES 512 +#define CPATH "/dev/cpuset" +#define CPATH_NEW CPATH "/1" +#define MEMCG_PATH "/dev/cgroup" +#define MEMCG_PATH_NEW MEMCG_PATH "/1" +#define _PATH_SYS_SYSTEM "/sys/devices/system" +#define LENGTH (3UL<<30) +#define TESTMEM (1UL<<30) +#define MB (1UL<<20) +#define NORMAL 1 +#define MLOCK 2 +#define KSM 3 +#define SYSFS_OVER "/proc/sys/vm/overcommit_memory" + +static char overcommit[BUFSIZ]; + +static void setup(void); +static void cleanup(void) LTP_ATTRIBUTE_NORETURN; +static void oom(int testcase, int mempolicy, int lite); +static long count_numa(void); +static int path_exist(const char *path, ...); +static void testoom(int mempolicy, int lite); +static void alloc_mem(long int length, int testcase); +static void test_alloc(int testcase, int lite); +static void gather_cpus(char *cpus); +static void umount_mem(char *path, char *path_new); +static void mount_mem(char *name, char *fs, char *options, char *path, + char *path_new); + +int main(int argc, char *argv[]) +{ + char *msg; + int lc, fd; + unsigned long nnodes = 1; + char buf[BUFSIZ], mem[BUFSIZ]; + + msg = parse_opts(argc, argv, NULL, NULL); + if (msg != (char *)NULL) + tst_brkm(TBROK, tst_exit, "OPTION PARSING ERROR - %s", msg); + +#ifdef __i386__ + tst_brkm(TCONF, tst_exit, + "test is not designed for 32-bit system."); +#endif /* __i386__ */ + + nnodes = count_numa(); + if (count_numa() == 1) + tst_brkm(TCONF, tst_exit, "required a NUMA system."); + + setup(); + + for (lc = 0; TEST_LOOPING(lc); lc++) { + Tst_count = 0; + fd = open(SYSFS_OVER, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open"); + if (write(fd, "1", 1) != 1) + tst_brkm(TBROK|TERRNO, cleanup, "write"); + close(fd); + + snprintf(buf, BUFSIZ, "%s/memory.limit_in_bytes", + MEMCG_PATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + sprintf(mem, "%ld", TESTMEM); + if (write(fd, mem, strlen(mem)) != strlen(mem)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + + snprintf(buf, BUFSIZ, "%s/tasks", MEMCG_PATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + snprintf(buf, BUFSIZ, "%d", getpid()); + if (write(fd, buf, strlen(buf)) != strlen(buf)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + + tst_resm(TINFO, "process mempolicy."); + testoom(1, 0, 1); + + snprintf(buf, BUFSIZ, "%s/memory.memsw.limit_in_bytes", + MEMCG_PATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + if (write(fd, mem, strlen(mem)) != strlen(mem)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + testoom(1, 1, 1); + + tst_resm(TINFO, "process cpuset."); + snprintf(buf, BUFSIZ, "%s/memory.memsw.limit_in_bytes", + MEMCG_PATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + sprintf(mem, "%ld", TESTMEM); + if (write(fd, "-1", 2) != 2) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + testoom(0, 0, 1); + + snprintf(buf, BUFSIZ, "%s/memory.memsw.limit_in_bytes", + MEMCG_PATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + if (write(fd, mem, strlen(mem)) != strlen(mem)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + testoom(0, 1, 1); + } + cleanup(); +} + +void testoom(int mempolicy, int lite) +{ + int fd; + char buf[BUFSIZ] = ""; + char cpus[BUFSIZ] = ""; + + if (!mempolicy) { + gather_cpus(cpus); + tst_resm(TINFO, "CPU list for 2nd node is %s.", cpus); + + snprintf(buf, BUFSIZ, "%s/cpuset.mems", CPATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + if (write(fd, "1", 1) != 1) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + + snprintf(buf, BUFSIZ, "%s/cpuset.cpus", CPATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + if (write(fd, cpus, strlen(cpus)) != strlen(cpus)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + + snprintf(buf, BUFSIZ, "%s/tasks", CPATH_NEW); + fd = open(buf, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open %s", buf); + snprintf(buf, BUFSIZ, "%d", getpid()); + if (write(fd, buf, strlen(buf)) != strlen(buf)) + tst_brkm(TBROK|TERRNO, cleanup, "write %s", buf); + close(fd); + } + tst_resm(TINFO, "start normal OOM testing."); + oom(NORMAL, mempolicy, lite); + + tst_resm(TINFO, "start OOM testing for mlocked pages."); + oom(MLOCK, mempolicy, lite); + + tst_resm(TINFO, "start OOM testing for KSM pages."); + oom(KSM, mempolicy, lite); +} + +void setup(void) +{ + int fd; + + tst_sig(FORK, DEF_HANDLER, cleanup); + TEST_PAUSE; + + fd = open(SYSFS_OVER, O_RDONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open"); + if (read(fd, &overcommit, 1) != 1) + tst_brkm(TBROK|TERRNO, cleanup, "read"); + close(fd); + + mount_mem("cpuset", "cpuset", NULL, CPATH, CPATH_NEW); + mount_mem("memcg", "cgroup", "memory", MEMCG_PATH, MEMCG_PATH_NEW); +} + +void umount_mem(char *path, char *path_new) +{ + FILE *fp; + int fd; + char s_new[BUFSIZ], s[BUFSIZ], value[BUFSIZ]; + + /* Move all processes in task to its parent node. */ + snprintf(s, BUFSIZ, "%s/tasks", path); + fd = open(s, O_WRONLY); + if (fd == -1) + tst_resm(TWARN|TERRNO, "open %s", s); + snprintf(s_new, BUFSIZ, "%s/tasks", path_new); + + fp = fopen(s_new, "r"); + if (fp == NULL) + tst_resm(TWARN|TERRNO, "fopen %s", s_new); + if ((fd != -1) && (fp != NULL)) { + while (fgets(value, BUFSIZ, fp) != NULL) + if (write(fd, value, strlen(value) - 1) + != strlen(value) - 1) + tst_resm(TWARN|TERRNO, "write %s", s); + } + if (fd != -1) + close(fd); + if (fp != NULL) + fclose(fp); + if (rmdir(path_new) == -1) + tst_resm(TWARN|TERRNO, "rmdir %s", path_new); + if (umount(path) == -1) + tst_resm(TWARN|TERRNO, "umount %s", path); + if (rmdir(path) == -1) + tst_resm(TWARN|TERRNO, "rmdir %s", path); +} + +void mount_mem(char *name, char *fs, char *options, char *path, char *path_new) +{ + if (mkdir(path, 0777) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "mkdir %s", path); + if (mount(name, path, fs, 0, options) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "mount %s", path); + if (mkdir(path_new, 0777) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "mkdir %s", path_new); +} + +void cleanup(void) +{ + int fd; + + fd = open(SYSFS_OVER, O_WRONLY); + if (fd == -1) + tst_brkm(TBROK|TERRNO, cleanup, "open"); + if (write(fd, &overcommit, 1) != 1) + tst_brkm(TBROK|TERRNO, cleanup, "write"); + close(fd); + + umount_mem(CPATH, CPATH_NEW); + umount_mem(MEMCG_PATH, MEMCG_PATH_NEW); + + TEST_CLEANUP; + tst_exit(); +} + +void oom(int testcase, int mempolicy, int lite) +{ + pid_t pid; + int status; + unsigned long nmask = 2; + + switch(pid = fork()) { + case -1: + tst_brkm(TBROK|TERRNO, cleanup, "fork"); + case 0: + if (mempolicy) + if (set_mempolicy(MPOL_BIND, &nmask, MAXNODES) == -1) + tst_brkm(TBROK|TERRNO, cleanup, + "set_mempolicy"); + + test_alloc(testcase, lite); + exit(0); + default: + break; + } + tst_resm(TINFO, "expected victim is %d.", pid); + if (waitpid(-1, &status, 0) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "waitpid"); + + if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) + tst_resm(TFAIL, "the victim unexpectedly failed: %d", status); +} + +long count_numa(void) +{ + int nnodes = 0; + + while(path_exist(_PATH_SYS_SYSTEM "/node/node%d", nnodes)) + nnodes++; + + return nnodes; +} + +int path_exist(const char *path, ...) +{ + va_list ap; + char pathbuf[PATH_MAX]; + + va_start(ap, path); + vsnprintf(pathbuf, sizeof(pathbuf), path, ap); + va_end(ap); + + return access(pathbuf, F_OK) == 0; +} + +void gather_cpus(char *cpus) +{ + int ncpus = 0; + int i; + char buf[BUFSIZ]; + + while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus)) + ncpus++; + + for (i = 0; i < ncpus; i++) + if (path_exist(_PATH_SYS_SYSTEM "/node/node1/cpu%d", i)) { + sprintf(buf, "%d,", i); + strcat(cpus, buf); + } + /* Remove the trailing comma. */ + cpus[strlen(cpus) - 1] = '\0'; +} + +void alloc_mem(long int length, int testcase) +{ + 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) { + tst_brkm(TBROK|TERRNO, cleanup, "mmap"); + } + if (testcase == MLOCK && mlock(s, length) == -1) + tst_brkm(TINFO|TERRNO, cleanup, "mlock"); + if (testcase == KSM + && madvise(s, length, MADV_MERGEABLE) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "madvise"); + memset(s, '\a', length); +} + +void test_alloc(int testcase, int lite) +{ + if (lite) + alloc_mem(TESTMEM + MB, testcase); + else + while(1) + alloc_mem(LENGTH, testcase); +} + +#else /* no NUMA */ +int main(void) { + tst_resm(TCONF, "no NUMA development packages installed."); + tst_exit(); +} +#endif hooks/post-receive -- ltp |