kselftests for memory.oom.group

2018-09-07 Thread jgkamat
Here is the third version of the patchset.

Changes since the last patchset:
- Updated commit message of first patch to clarify fixes
- Add ack from Roman

There should be no code changes since the last patchset.

Let me know if any improvements can be made, and thanks for your time!
-Jay



kselftests for memory.oom.group

2018-09-07 Thread jgkamat
Here is the third version of the patchset.

Changes since the last patchset:
- Updated commit message of first patch to clarify fixes
- Add ack from Roman

There should be no code changes since the last patchset.

Let me know if any improvements can be made, and thanks for your time!
-Jay



[PATCH v3 1/2] Fix cg_read_strcmp()

2018-09-07 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings.
Previously, this function read to a size = 1 buffer when comparing
against empty strings, which would lead to cg_read_strcmp() comparing
two empty strings.
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
Acked-by: Roman Gushchin 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 17 ++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..8b644ea39725 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -89,17 +89,28 @@ int cg_read(const char *cgroup, const char *control, char 
*buf, size_t len)
 int cg_read_strcmp(const char *cgroup, const char *control,
   const char *expected)
 {
-   size_t size = strlen(expected) + 1;
+   size_t size;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (!expected)
+   size = 32;
+   else
+   size = strlen(expected) + 1;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1



[PATCH v3 2/2] Add tests for memory.oom.group

2018-09-07 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
Acked-by: Roman Gushchin 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 8b644ea39725..e0db048331cb 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -340,3 +340,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..28d321ba311b 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up 

[PATCH v3 1/2] Fix cg_read_strcmp()

2018-09-07 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings.
Previously, this function read to a size = 1 buffer when comparing
against empty strings, which would lead to cg_read_strcmp() comparing
two empty strings.
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
Acked-by: Roman Gushchin 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 17 ++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..8b644ea39725 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -89,17 +89,28 @@ int cg_read(const char *cgroup, const char *control, char 
*buf, size_t len)
 int cg_read_strcmp(const char *cgroup, const char *control,
   const char *expected)
 {
-   size_t size = strlen(expected) + 1;
+   size_t size;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (!expected)
+   size = 32;
+   else
+   size = strlen(expected) + 1;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1



[PATCH v3 2/2] Add tests for memory.oom.group

2018-09-07 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
Acked-by: Roman Gushchin 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 8b644ea39725..e0db048331cb 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -340,3 +340,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..28d321ba311b 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up 

kselftests for memory.oom.group

2018-09-07 Thread jgkamat
Here is an amended patch which fixes those issues.

Please let me know if you see anything else that could be improved.

Changes since the last patchset:

- Use if statement to check for empty string in cgroup_util.c
- Cleanup and return properly when failing to kill process
  in test_memcg_oom_group_score_events

Thanks,
-Jay



[PATCH 2/2] Add tests for memory.oom.group

2018-09-07 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 8b644ea39725..e0db048331cb 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -340,3 +340,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..28d321ba311b 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with 

kselftests for memory.oom.group

2018-09-07 Thread jgkamat
Here is an amended patch which fixes those issues.

Please let me know if you see anything else that could be improved.

Changes since the last patchset:

- Use if statement to check for empty string in cgroup_util.c
- Cleanup and return properly when failing to kill process
  in test_memcg_oom_group_score_events

Thanks,
-Jay



[PATCH 2/2] Add tests for memory.oom.group

2018-09-07 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 8b644ea39725..e0db048331cb 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -340,3 +340,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..28d321ba311b 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with 

[PATCH 1/2] Fix cg_read_strcmp()

2018-09-07 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 17 ++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..8b644ea39725 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -89,17 +89,28 @@ int cg_read(const char *cgroup, const char *control, char 
*buf, size_t len)
 int cg_read_strcmp(const char *cgroup, const char *control,
   const char *expected)
 {
-   size_t size = strlen(expected) + 1;
+   size_t size;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (!expected)
+   size = 32;
+   else
+   size = strlen(expected) + 1;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1



[PATCH 1/2] Fix cg_read_strcmp()

2018-09-07 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 17 ++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..8b644ea39725 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -89,17 +89,28 @@ int cg_read(const char *cgroup, const char *control, char 
*buf, size_t len)
 int cg_read_strcmp(const char *cgroup, const char *control,
   const char *expected)
 {
-   size_t size = strlen(expected) + 1;
+   size_t size;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (!expected)
+   size = 32;
+   else
+   size = strlen(expected) + 1;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1



kselftests for memory.oom.group

2018-09-04 Thread jgkamat
Hi Shuah,

I wrote some tests for the new memory.oom.group feature in cgroups 2.

In the process, I discovered a few small bugs in the cgroups tests, which
I have fixed as well in a separate commit.

This is my first ever patch to Linux, so let me know if you see any issues or
improvements that can be made.

Thanks for all your amazing work on Linux!
-Jay





kselftests for memory.oom.group

2018-09-04 Thread jgkamat
Hi Shuah,

I wrote some tests for the new memory.oom.group feature in cgroups 2.

In the process, I discovered a few small bugs in the cgroups tests, which
I have fixed as well in a separate commit.

This is my first ever patch to Linux, so let me know if you see any issues or
improvements that can be made.

Thanks for all your amazing work on Linux!
-Jay





[PATCH 2/2] Add tests for memory.oom.group

2018-09-04 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 4aadf38bcd5d..6799c69d7f03 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -338,3 +338,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..017c15a7a935 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with 

[PATCH 1/2] Fix cg_read_strcmp()

2018-09-04 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 13 +++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..4aadf38bcd5d 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -91,15 +91,24 @@ int cg_read_strcmp(const char *cgroup, const char *control,
 {
size_t size = strlen(expected) + 1;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (size == 1)
+   size = 32;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1



[PATCH 2/2] Add tests for memory.oom.group

2018-09-04 Thread jgkamat
From: Jay Kamat 

Add tests for memory.oom.group for the following cases:
- Killing all processes in a leaf cgroup, but leaving the
  parent untouched
- Killing all processes in a parent and leaf cgroup
- Keeping processes marked by OOM_SCORE_ADJ_MIN alive when considered
  for being killed by the group oom killer.

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c  |  21 ++
 tools/testing/selftests/cgroup/cgroup_util.h  |   1 +
 .../selftests/cgroup/test_memcontrol.c| 205 ++
 3 files changed, 227 insertions(+)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 4aadf38bcd5d..6799c69d7f03 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -338,3 +338,24 @@ int is_swap_enabled(void)
 
return cnt > 1;
 }
+
+int set_oom_adj_score(int pid, int score)
+{
+   char path[PATH_MAX];
+   int fd, len;
+
+   sprintf(path, "/proc/%d/oom_score_adj", pid);
+
+   fd = open(path, O_WRONLY | O_APPEND);
+   if (fd < 0)
+   return fd;
+
+   len = dprintf(fd, "%d", score);
+   if (len < 0) {
+   close(fd);
+   return len;
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h 
b/tools/testing/selftests/cgroup/cgroup_util.h
index fe82a297d4e0..cabd43fd137a 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/cgroup_util.h
@@ -39,3 +39,4 @@ extern int get_temp_fd(void);
 extern int alloc_pagecache(int fd, size_t size);
 extern int alloc_anon(const char *cgroup, void *arg);
 extern int is_swap_enabled(void);
+extern int set_oom_adj_score(int pid, int score);
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c 
b/tools/testing/selftests/cgroup/test_memcontrol.c
index cf0bddc9d271..017c15a7a935 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -2,6 +2,7 @@
 #define _GNU_SOURCE
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, 
void *arg)
return 0;
 }
 
+static int alloc_anon_noexit(const char *cgroup, void *arg)
+{
+   int ppid = getppid();
+
+   if (alloc_anon(cgroup, arg))
+   return -1;
+
+   while (getppid() == ppid)
+   sleep(1);
+
+   return 0;
+}
+
+/*
+ * Wait until processes are killed asynchronously by the OOM killer
+ * If we exceed a timeout, fail.
+ */
+static int cg_test_proc_killed(const char *cgroup)
+{
+   int limit;
+
+   for (limit = 10; limit > 0; limit--) {
+   if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
+   return 0;
+
+   usleep(10);
+   }
+   return -1;
+}
+
 /*
  * First, this test creates the following hierarchy:
  * A   memory.min = 50M,  memory.max = 200M
@@ -964,6 +995,177 @@ static int test_memcg_sock(const char *root)
return ret;
 }
 
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with memory.group.oom set. Then it checks that all
+ * processes in the leaf (but not the parent) were killed.
+ */
+static int test_memcg_oom_group_leaf_events(const char *root)
+{
+   int ret = KSFT_FAIL;
+   char *parent, *child;
+
+   parent = cg_name(root, "memcg_test_0");
+   child = cg_name(root, "memcg_test_0/memcg_test_1");
+
+   if (!parent || !child)
+   goto cleanup;
+
+   if (cg_create(parent))
+   goto cleanup;
+
+   if (cg_create(child))
+   goto cleanup;
+
+   if (cg_write(parent, "cgroup.subtree_control", "+memory"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.max", "50M"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.swap.max", "0"))
+   goto cleanup;
+
+   if (cg_write(child, "memory.oom.group", "1"))
+   goto cleanup;
+
+   cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
+   if (!cg_run(child, alloc_anon, (void *)MB(100)))
+   goto cleanup;
+
+   if (cg_test_proc_killed(child))
+   goto cleanup;
+
+   if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+   goto cleanup;
+
+   if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
+   goto cleanup;
+
+   ret = KSFT_PASS;
+
+cleanup:
+   if (child)
+   cg_destroy(child);
+   if (parent)
+   cg_destroy(parent);
+   free(child);
+   free(parent);
+
+   return ret;
+}
+
+/*
+ * This test disables swapping and tries to allocate anonymous memory
+ * up to OOM with 

[PATCH 1/2] Fix cg_read_strcmp()

2018-09-04 Thread jgkamat
From: Jay Kamat 

Fix a couple issues with cg_read_strcmp(), to improve correctness of
cgroup tests
- Fix cg_read_strcmp() always returning 0 for empty "needle" strings
- Fix a memory leak in cg_read_strcmp()

Fixes: 84092dbcf901 ("selftests: cgroup: add memory controller self-tests")

Signed-off-by: Jay Kamat 
---
 tools/testing/selftests/cgroup/cgroup_util.c | 13 +++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/cgroup/cgroup_util.c 
b/tools/testing/selftests/cgroup/cgroup_util.c
index 1e9e3c470561..4aadf38bcd5d 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -91,15 +91,24 @@ int cg_read_strcmp(const char *cgroup, const char *control,
 {
size_t size = strlen(expected) + 1;
char *buf;
+   int ret;
+
+   /* Handle the case of comparing against empty string */
+   if (size == 1)
+   size = 32;
 
buf = malloc(size);
if (!buf)
return -1;
 
-   if (cg_read(cgroup, control, buf, size))
+   if (cg_read(cgroup, control, buf, size)) {
+   free(buf);
return -1;
+   }
 
-   return strcmp(expected, buf);
+   ret = strcmp(expected, buf);
+   free(buf);
+   return ret;
 }
 
 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
-- 
2.17.1