diff options
author | Jay Kamat <jgkamat@fb.com> | 2018-09-07 17:34:05 -0400 |
---|---|---|
committer | Shuah Khan (Samsung OSG) <shuah@kernel.org> | 2018-09-07 18:36:01 -0400 |
commit | a987785dcd6c8ae2915460582aebd6481c81eb67 (patch) | |
tree | c451b56d196e449bade01055bc30dd43eaad8d4d | |
parent | 48c2bb0b9cf863e0ed78e269f188ce65b73e0fd1 (diff) |
Add tests for memory.oom.group
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 <jgkamat@fb.com>
Acked-by: Roman Gushchin <guro@fb.com>
Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org>
-rw-r--r-- | tools/testing/selftests/cgroup/cgroup_util.c | 21 | ||||
-rw-r--r-- | tools/testing/selftests/cgroup/cgroup_util.h | 1 | ||||
-rw-r--r-- | tools/testing/selftests/cgroup/test_memcontrol.c | 205 |
3 files changed, 227 insertions, 0 deletions
diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c index f857def9a9e6..14c9fe284806 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.c +++ b/tools/testing/selftests/cgroup/cgroup_util.c | |||
@@ -348,3 +348,24 @@ int is_swap_enabled(void) | |||
348 | 348 | ||
349 | return cnt > 1; | 349 | return cnt > 1; |
350 | } | 350 | } |
351 | |||
352 | int set_oom_adj_score(int pid, int score) | ||
353 | { | ||
354 | char path[PATH_MAX]; | ||
355 | int fd, len; | ||
356 | |||
357 | sprintf(path, "/proc/%d/oom_score_adj", pid); | ||
358 | |||
359 | fd = open(path, O_WRONLY | O_APPEND); | ||
360 | if (fd < 0) | ||
361 | return fd; | ||
362 | |||
363 | len = dprintf(fd, "%d", score); | ||
364 | if (len < 0) { | ||
365 | close(fd); | ||
366 | return len; | ||
367 | } | ||
368 | |||
369 | close(fd); | ||
370 | return 0; | ||
371 | } | ||
diff --git a/tools/testing/selftests/cgroup/cgroup_util.h b/tools/testing/selftests/cgroup/cgroup_util.h index 1ff6f9f1abdc..9ac8b7958f83 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.h +++ b/tools/testing/selftests/cgroup/cgroup_util.h | |||
@@ -40,3 +40,4 @@ extern int get_temp_fd(void); | |||
40 | extern int alloc_pagecache(int fd, size_t size); | 40 | extern int alloc_pagecache(int fd, size_t size); |
41 | extern int alloc_anon(const char *cgroup, void *arg); | 41 | extern int alloc_anon(const char *cgroup, void *arg); |
42 | extern int is_swap_enabled(void); | 42 | extern int is_swap_enabled(void); |
43 | 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 @@ | |||
2 | #define _GNU_SOURCE | 2 | #define _GNU_SOURCE |
3 | 3 | ||
4 | #include <linux/limits.h> | 4 | #include <linux/limits.h> |
5 | #include <linux/oom.h> | ||
5 | #include <fcntl.h> | 6 | #include <fcntl.h> |
6 | #include <stdio.h> | 7 | #include <stdio.h> |
7 | #include <stdlib.h> | 8 | #include <stdlib.h> |
@@ -202,6 +203,36 @@ static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg) | |||
202 | return 0; | 203 | return 0; |
203 | } | 204 | } |
204 | 205 | ||
206 | static int alloc_anon_noexit(const char *cgroup, void *arg) | ||
207 | { | ||
208 | int ppid = getppid(); | ||
209 | |||
210 | if (alloc_anon(cgroup, arg)) | ||
211 | return -1; | ||
212 | |||
213 | while (getppid() == ppid) | ||
214 | sleep(1); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Wait until processes are killed asynchronously by the OOM killer | ||
221 | * If we exceed a timeout, fail. | ||
222 | */ | ||
223 | static int cg_test_proc_killed(const char *cgroup) | ||
224 | { | ||
225 | int limit; | ||
226 | |||
227 | for (limit = 10; limit > 0; limit--) { | ||
228 | if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0) | ||
229 | return 0; | ||
230 | |||
231 | usleep(100000); | ||
232 | } | ||
233 | return -1; | ||
234 | } | ||
235 | |||
205 | /* | 236 | /* |
206 | * First, this test creates the following hierarchy: | 237 | * First, this test creates the following hierarchy: |
207 | * A memory.min = 50M, memory.max = 200M | 238 | * A memory.min = 50M, memory.max = 200M |
@@ -964,6 +995,177 @@ cleanup: | |||
964 | return ret; | 995 | return ret; |
965 | } | 996 | } |
966 | 997 | ||
998 | /* | ||
999 | * This test disables swapping and tries to allocate anonymous memory | ||
1000 | * up to OOM with memory.group.oom set. Then it checks that all | ||
1001 | * processes in the leaf (but not the parent) were killed. | ||
1002 | */ | ||
1003 | static int test_memcg_oom_group_leaf_events(const char *root) | ||
1004 | { | ||
1005 | int ret = KSFT_FAIL; | ||
1006 | char *parent, *child; | ||
1007 | |||
1008 | parent = cg_name(root, "memcg_test_0"); | ||
1009 | child = cg_name(root, "memcg_test_0/memcg_test_1"); | ||
1010 | |||
1011 | if (!parent || !child) | ||
1012 | goto cleanup; | ||
1013 | |||
1014 | if (cg_create(parent)) | ||
1015 | goto cleanup; | ||
1016 | |||
1017 | if (cg_create(child)) | ||
1018 | goto cleanup; | ||
1019 | |||
1020 | if (cg_write(parent, "cgroup.subtree_control", "+memory")) | ||
1021 | goto cleanup; | ||
1022 | |||
1023 | if (cg_write(child, "memory.max", "50M")) | ||
1024 | goto cleanup; | ||
1025 | |||
1026 | if (cg_write(child, "memory.swap.max", "0")) | ||
1027 | goto cleanup; | ||
1028 | |||
1029 | if (cg_write(child, "memory.oom.group", "1")) | ||
1030 | goto cleanup; | ||
1031 | |||
1032 | cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); | ||
1033 | cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); | ||
1034 | cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); | ||
1035 | if (!cg_run(child, alloc_anon, (void *)MB(100))) | ||
1036 | goto cleanup; | ||
1037 | |||
1038 | if (cg_test_proc_killed(child)) | ||
1039 | goto cleanup; | ||
1040 | |||
1041 | if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0) | ||
1042 | goto cleanup; | ||
1043 | |||
1044 | if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0) | ||
1045 | goto cleanup; | ||
1046 | |||
1047 | ret = KSFT_PASS; | ||
1048 | |||
1049 | cleanup: | ||
1050 | if (child) | ||
1051 | cg_destroy(child); | ||
1052 | if (parent) | ||
1053 | cg_destroy(parent); | ||
1054 | free(child); | ||
1055 | free(parent); | ||
1056 | |||
1057 | return ret; | ||
1058 | } | ||
1059 | |||
1060 | /* | ||
1061 | * This test disables swapping and tries to allocate anonymous memory | ||
1062 | * up to OOM with memory.group.oom set. Then it checks that all | ||
1063 | * processes in the parent and leaf were killed. | ||
1064 | */ | ||
1065 | static int test_memcg_oom_group_parent_events(const char *root) | ||
1066 | { | ||
1067 | int ret = KSFT_FAIL; | ||
1068 | char *parent, *child; | ||
1069 | |||
1070 | parent = cg_name(root, "memcg_test_0"); | ||
1071 | child = cg_name(root, "memcg_test_0/memcg_test_1"); | ||
1072 | |||
1073 | if (!parent || !child) | ||
1074 | goto cleanup; | ||
1075 | |||
1076 | if (cg_create(parent)) | ||
1077 | goto cleanup; | ||
1078 | |||
1079 | if (cg_create(child)) | ||
1080 | goto cleanup; | ||
1081 | |||
1082 | if (cg_write(parent, "memory.max", "80M")) | ||
1083 | goto cleanup; | ||
1084 | |||
1085 | if (cg_write(parent, "memory.swap.max", "0")) | ||
1086 | goto cleanup; | ||
1087 | |||
1088 | if (cg_write(parent, "memory.oom.group", "1")) | ||
1089 | goto cleanup; | ||
1090 | |||
1091 | cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); | ||
1092 | cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); | ||
1093 | cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); | ||
1094 | |||
1095 | if (!cg_run(child, alloc_anon, (void *)MB(100))) | ||
1096 | goto cleanup; | ||
1097 | |||
1098 | if (cg_test_proc_killed(child)) | ||
1099 | goto cleanup; | ||
1100 | if (cg_test_proc_killed(parent)) | ||
1101 | goto cleanup; | ||
1102 | |||
1103 | ret = KSFT_PASS; | ||
1104 | |||
1105 | cleanup: | ||
1106 | if (child) | ||
1107 | cg_destroy(child); | ||
1108 | if (parent) | ||
1109 | cg_destroy(parent); | ||
1110 | free(child); | ||
1111 | free(parent); | ||
1112 | |||
1113 | return ret; | ||
1114 | } | ||
1115 | |||
1116 | /* | ||
1117 | * This test disables swapping and tries to allocate anonymous memory | ||
1118 | * up to OOM with memory.group.oom set. Then it checks that all | ||
1119 | * processes were killed except those set with OOM_SCORE_ADJ_MIN | ||
1120 | */ | ||
1121 | static int test_memcg_oom_group_score_events(const char *root) | ||
1122 | { | ||
1123 | int ret = KSFT_FAIL; | ||
1124 | char *memcg; | ||
1125 | int safe_pid; | ||
1126 | |||
1127 | memcg = cg_name(root, "memcg_test_0"); | ||
1128 | |||
1129 | if (!memcg) | ||
1130 | goto cleanup; | ||
1131 | |||
1132 | if (cg_create(memcg)) | ||
1133 | goto cleanup; | ||
1134 | |||
1135 | if (cg_write(memcg, "memory.max", "50M")) | ||
1136 | goto cleanup; | ||
1137 | |||
1138 | if (cg_write(memcg, "memory.swap.max", "0")) | ||
1139 | goto cleanup; | ||
1140 | |||
1141 | if (cg_write(memcg, "memory.oom.group", "1")) | ||
1142 | goto cleanup; | ||
1143 | |||
1144 | safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); | ||
1145 | if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN)) | ||
1146 | goto cleanup; | ||
1147 | |||
1148 | cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); | ||
1149 | if (!cg_run(memcg, alloc_anon, (void *)MB(100))) | ||
1150 | goto cleanup; | ||
1151 | |||
1152 | if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3) | ||
1153 | goto cleanup; | ||
1154 | |||
1155 | if (kill(safe_pid, SIGKILL)) | ||
1156 | goto cleanup; | ||
1157 | |||
1158 | ret = KSFT_PASS; | ||
1159 | |||
1160 | cleanup: | ||
1161 | if (memcg) | ||
1162 | cg_destroy(memcg); | ||
1163 | free(memcg); | ||
1164 | |||
1165 | return ret; | ||
1166 | } | ||
1167 | |||
1168 | |||
967 | #define T(x) { x, #x } | 1169 | #define T(x) { x, #x } |
968 | struct memcg_test { | 1170 | struct memcg_test { |
969 | int (*fn)(const char *root); | 1171 | int (*fn)(const char *root); |
@@ -978,6 +1180,9 @@ struct memcg_test { | |||
978 | T(test_memcg_oom_events), | 1180 | T(test_memcg_oom_events), |
979 | T(test_memcg_swap_max), | 1181 | T(test_memcg_swap_max), |
980 | T(test_memcg_sock), | 1182 | T(test_memcg_sock), |
1183 | T(test_memcg_oom_group_leaf_events), | ||
1184 | T(test_memcg_oom_group_parent_events), | ||
1185 | T(test_memcg_oom_group_score_events), | ||
981 | }; | 1186 | }; |
982 | #undef T | 1187 | #undef T |
983 | 1188 | ||