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 /tools | |
| 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>
Diffstat (limited to 'tools')
| -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 | ||
