1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <errno.h> 5 #include <linux/limits.h> 6 #include <signal.h> 7 #include <string.h> 8 #include <sys/stat.h> 9 #include <sys/types.h> 10 #include <unistd.h> 11 12 #include "../kselftest.h" 13 #include "cgroup_util.h" 14 15 static int run_success(const char *cgroup, void *arg) 16 { 17 return 0; 18 } 19 20 static int run_pause(const char *cgroup, void *arg) 21 { 22 return pause(); 23 } 24 25 /* 26 * This test checks that pids.max prevents forking new children above the 27 * specified limit in the cgroup. 28 */ 29 static int test_pids_max(const char *root) 30 { 31 int ret = KSFT_FAIL; 32 char *cg_pids; 33 int pid; 34 35 cg_pids = cg_name(root, "pids_test"); 36 if (!cg_pids) 37 goto cleanup; 38 39 if (cg_create(cg_pids)) 40 goto cleanup; 41 42 if (cg_read_strcmp(cg_pids, "pids.max", "max\n")) 43 goto cleanup; 44 45 if (cg_write(cg_pids, "pids.max", "2")) 46 goto cleanup; 47 48 if (cg_enter_current(cg_pids)) 49 goto cleanup; 50 51 pid = cg_run_nowait(cg_pids, run_pause, NULL); 52 if (pid < 0) 53 goto cleanup; 54 55 if (cg_run_nowait(cg_pids, run_success, NULL) != -1 || errno != EAGAIN) 56 goto cleanup; 57 58 if (kill(pid, SIGINT)) 59 goto cleanup; 60 61 ret = KSFT_PASS; 62 63 cleanup: 64 cg_enter_current(root); 65 cg_destroy(cg_pids); 66 free(cg_pids); 67 68 return ret; 69 } 70 71 /* 72 * This test checks that pids.events are counted in cgroup associated with pids.max 73 */ 74 static int test_pids_events(const char *root) 75 { 76 int ret = KSFT_FAIL; 77 char *cg_parent = NULL, *cg_child = NULL; 78 int pid; 79 80 if (cgroup_feature("pids_localevents") <= 0) 81 return KSFT_SKIP; 82 83 cg_parent = cg_name(root, "pids_parent"); 84 cg_child = cg_name(cg_parent, "pids_child"); 85 if (!cg_parent || !cg_child) 86 goto cleanup; 87 88 if (cg_create(cg_parent)) 89 goto cleanup; 90 if (cg_write(cg_parent, "cgroup.subtree_control", "+pids")) 91 goto cleanup; 92 if (cg_create(cg_child)) 93 goto cleanup; 94 95 if (cg_write(cg_parent, "pids.max", "2")) 96 goto cleanup; 97 98 if (cg_read_strcmp(cg_child, "pids.max", "max\n")) 99 goto cleanup; 100 101 if (cg_enter_current(cg_child)) 102 goto cleanup; 103 104 pid = cg_run_nowait(cg_child, run_pause, NULL); 105 if (pid < 0) 106 goto cleanup; 107 108 if (cg_run_nowait(cg_child, run_success, NULL) != -1 || errno != EAGAIN) 109 goto cleanup; 110 111 if (kill(pid, SIGINT)) 112 goto cleanup; 113 114 if (cg_read_key_long(cg_child, "pids.events", "max ") != 0) 115 goto cleanup; 116 if (cg_read_key_long(cg_parent, "pids.events", "max ") != 1) 117 goto cleanup; 118 119 120 ret = KSFT_PASS; 121 122 cleanup: 123 cg_enter_current(root); 124 if (cg_child) 125 cg_destroy(cg_child); 126 if (cg_parent) 127 cg_destroy(cg_parent); 128 free(cg_child); 129 free(cg_parent); 130 131 return ret; 132 } 133 134 135 136 #define T(x) { x, #x } 137 struct pids_test { 138 int (*fn)(const char *root); 139 const char *name; 140 } tests[] = { 141 T(test_pids_max), 142 T(test_pids_events), 143 }; 144 #undef T 145 146 int main(int argc, char **argv) 147 { 148 char root[PATH_MAX]; 149 150 ksft_print_header(); 151 ksft_set_plan(ARRAY_SIZE(tests)); 152 if (cg_find_unified_root(root, sizeof(root), NULL)) 153 ksft_exit_skip("cgroup v2 isn't mounted\n"); 154 155 /* 156 * Check that pids controller is available: 157 * pids is listed in cgroup.controllers 158 */ 159 if (cg_read_strstr(root, "cgroup.controllers", "pids")) 160 ksft_exit_skip("pids controller isn't available\n"); 161 162 if (cg_read_strstr(root, "cgroup.subtree_control", "pids")) 163 if (cg_write(root, "cgroup.subtree_control", "+pids")) 164 ksft_exit_skip("Failed to set pids controller\n"); 165 166 for (int i = 0; i < ARRAY_SIZE(tests); i++) { 167 switch (tests[i].fn(root)) { 168 case KSFT_PASS: 169 ksft_test_result_pass("%s\n", tests[i].name); 170 break; 171 case KSFT_SKIP: 172 ksft_test_result_skip("%s\n", tests[i].name); 173 break; 174 default: 175 ksft_test_result_fail("%s\n", tests[i].name); 176 break; 177 } 178 } 179 180 ksft_finished(); 181 } 182