Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jay Kamat and committed by
Shuah Khan (Samsung OSG)
a987785d 48c2bb0b

+227
+21
tools/testing/selftests/cgroup/cgroup_util.c
··· 348 348 349 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 + }
+1
tools/testing/selftests/cgroup/cgroup_util.h
··· 40 40 extern int alloc_pagecache(int fd, size_t size); 41 41 extern int alloc_anon(const char *cgroup, void *arg); 42 42 extern int is_swap_enabled(void); 43 + extern int set_oom_adj_score(int pid, int score);
+205
tools/testing/selftests/cgroup/test_memcontrol.c
··· 2 2 #define _GNU_SOURCE 3 3 4 4 #include <linux/limits.h> 5 + #include <linux/oom.h> 5 6 #include <fcntl.h> 6 7 #include <stdio.h> 7 8 #include <stdlib.h> ··· 201 200 sleep(1); 202 201 203 202 return 0; 203 + } 204 + 205 + static int alloc_anon_noexit(const char *cgroup, void *arg) 206 + { 207 + int ppid = getppid(); 208 + 209 + if (alloc_anon(cgroup, arg)) 210 + return -1; 211 + 212 + while (getppid() == ppid) 213 + sleep(1); 214 + 215 + return 0; 216 + } 217 + 218 + /* 219 + * Wait until processes are killed asynchronously by the OOM killer 220 + * If we exceed a timeout, fail. 221 + */ 222 + static int cg_test_proc_killed(const char *cgroup) 223 + { 224 + int limit; 225 + 226 + for (limit = 10; limit > 0; limit--) { 227 + if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0) 228 + return 0; 229 + 230 + usleep(100000); 231 + } 232 + return -1; 204 233 } 205 234 206 235 /* ··· 995 964 return ret; 996 965 } 997 966 967 + /* 968 + * This test disables swapping and tries to allocate anonymous memory 969 + * up to OOM with memory.group.oom set. Then it checks that all 970 + * processes in the leaf (but not the parent) were killed. 971 + */ 972 + static int test_memcg_oom_group_leaf_events(const char *root) 973 + { 974 + int ret = KSFT_FAIL; 975 + char *parent, *child; 976 + 977 + parent = cg_name(root, "memcg_test_0"); 978 + child = cg_name(root, "memcg_test_0/memcg_test_1"); 979 + 980 + if (!parent || !child) 981 + goto cleanup; 982 + 983 + if (cg_create(parent)) 984 + goto cleanup; 985 + 986 + if (cg_create(child)) 987 + goto cleanup; 988 + 989 + if (cg_write(parent, "cgroup.subtree_control", "+memory")) 990 + goto cleanup; 991 + 992 + if (cg_write(child, "memory.max", "50M")) 993 + goto cleanup; 994 + 995 + if (cg_write(child, "memory.swap.max", "0")) 996 + goto cleanup; 997 + 998 + if (cg_write(child, "memory.oom.group", "1")) 999 + goto cleanup; 1000 + 1001 + cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1002 + cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1003 + cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1004 + if (!cg_run(child, alloc_anon, (void *)MB(100))) 1005 + goto cleanup; 1006 + 1007 + if (cg_test_proc_killed(child)) 1008 + goto cleanup; 1009 + 1010 + if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0) 1011 + goto cleanup; 1012 + 1013 + if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0) 1014 + goto cleanup; 1015 + 1016 + ret = KSFT_PASS; 1017 + 1018 + cleanup: 1019 + if (child) 1020 + cg_destroy(child); 1021 + if (parent) 1022 + cg_destroy(parent); 1023 + free(child); 1024 + free(parent); 1025 + 1026 + return ret; 1027 + } 1028 + 1029 + /* 1030 + * This test disables swapping and tries to allocate anonymous memory 1031 + * up to OOM with memory.group.oom set. Then it checks that all 1032 + * processes in the parent and leaf were killed. 1033 + */ 1034 + static int test_memcg_oom_group_parent_events(const char *root) 1035 + { 1036 + int ret = KSFT_FAIL; 1037 + char *parent, *child; 1038 + 1039 + parent = cg_name(root, "memcg_test_0"); 1040 + child = cg_name(root, "memcg_test_0/memcg_test_1"); 1041 + 1042 + if (!parent || !child) 1043 + goto cleanup; 1044 + 1045 + if (cg_create(parent)) 1046 + goto cleanup; 1047 + 1048 + if (cg_create(child)) 1049 + goto cleanup; 1050 + 1051 + if (cg_write(parent, "memory.max", "80M")) 1052 + goto cleanup; 1053 + 1054 + if (cg_write(parent, "memory.swap.max", "0")) 1055 + goto cleanup; 1056 + 1057 + if (cg_write(parent, "memory.oom.group", "1")) 1058 + goto cleanup; 1059 + 1060 + cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1061 + cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1062 + cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1063 + 1064 + if (!cg_run(child, alloc_anon, (void *)MB(100))) 1065 + goto cleanup; 1066 + 1067 + if (cg_test_proc_killed(child)) 1068 + goto cleanup; 1069 + if (cg_test_proc_killed(parent)) 1070 + goto cleanup; 1071 + 1072 + ret = KSFT_PASS; 1073 + 1074 + cleanup: 1075 + if (child) 1076 + cg_destroy(child); 1077 + if (parent) 1078 + cg_destroy(parent); 1079 + free(child); 1080 + free(parent); 1081 + 1082 + return ret; 1083 + } 1084 + 1085 + /* 1086 + * This test disables swapping and tries to allocate anonymous memory 1087 + * up to OOM with memory.group.oom set. Then it checks that all 1088 + * processes were killed except those set with OOM_SCORE_ADJ_MIN 1089 + */ 1090 + static int test_memcg_oom_group_score_events(const char *root) 1091 + { 1092 + int ret = KSFT_FAIL; 1093 + char *memcg; 1094 + int safe_pid; 1095 + 1096 + memcg = cg_name(root, "memcg_test_0"); 1097 + 1098 + if (!memcg) 1099 + goto cleanup; 1100 + 1101 + if (cg_create(memcg)) 1102 + goto cleanup; 1103 + 1104 + if (cg_write(memcg, "memory.max", "50M")) 1105 + goto cleanup; 1106 + 1107 + if (cg_write(memcg, "memory.swap.max", "0")) 1108 + goto cleanup; 1109 + 1110 + if (cg_write(memcg, "memory.oom.group", "1")) 1111 + goto cleanup; 1112 + 1113 + safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1114 + if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN)) 1115 + goto cleanup; 1116 + 1117 + cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1118 + if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 1119 + goto cleanup; 1120 + 1121 + if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3) 1122 + goto cleanup; 1123 + 1124 + if (kill(safe_pid, SIGKILL)) 1125 + goto cleanup; 1126 + 1127 + ret = KSFT_PASS; 1128 + 1129 + cleanup: 1130 + if (memcg) 1131 + cg_destroy(memcg); 1132 + free(memcg); 1133 + 1134 + return ret; 1135 + } 1136 + 1137 + 998 1138 #define T(x) { x, #x } 999 1139 struct memcg_test { 1000 1140 int (*fn)(const char *root); ··· 1180 978 T(test_memcg_oom_events), 1181 979 T(test_memcg_swap_max), 1182 980 T(test_memcg_sock), 981 + T(test_memcg_oom_group_leaf_events), 982 + T(test_memcg_oom_group_parent_events), 983 + T(test_memcg_oom_group_score_events), 1183 984 }; 1184 985 #undef T 1185 986