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 cg_parent = cg_name(root, "pids_parent"); 81 cg_child = cg_name(cg_parent, "pids_child"); 82 if (!cg_parent || !cg_child) 83 goto cleanup; 84 85 if (cg_create(cg_parent)) 86 goto cleanup; 87 if (cg_write(cg_parent, "cgroup.subtree_control", "+pids")) 88 goto cleanup; 89 if (cg_create(cg_child)) 90 goto cleanup; 91 92 if (cg_write(cg_parent, "pids.max", "2")) 93 goto cleanup; 94 95 if (cg_read_strcmp(cg_child, "pids.max", "max\n")) 96 goto cleanup; 97 98 if (cg_enter_current(cg_child)) 99 goto cleanup; 100 101 pid = cg_run_nowait(cg_child, run_pause, NULL); 102 if (pid < 0) 103 goto cleanup; 104 105 if (cg_run_nowait(cg_child, run_success, NULL) != -1 || errno != EAGAIN) 106 goto cleanup; 107 108 if (kill(pid, SIGINT)) 109 goto cleanup; 110 111 if (cg_read_key_long(cg_child, "pids.events", "max ") != 0) 112 goto cleanup; 113 if (cg_read_key_long(cg_parent, "pids.events", "max ") != 1) 114 goto cleanup; 115 116 117 ret = KSFT_PASS; 118 119 cleanup: 120 cg_enter_current(root); 121 if (cg_child) 122 cg_destroy(cg_child); 123 if (cg_parent) 124 cg_destroy(cg_parent); 125 free(cg_child); 126 free(cg_parent); 127 128 return ret; 129 } 130 131 132 133 #define T(x) { x, #x } 134 struct pids_test { 135 int (*fn)(const char *root); 136 const char *name; 137 } tests[] = { 138 T(test_pids_max), 139 T(test_pids_events), 140 }; 141 #undef T 142 143 int main(int argc, char **argv) 144 { 145 char root[PATH_MAX]; 146 147 ksft_print_header(); 148 ksft_set_plan(ARRAY_SIZE(tests)); 149 if (cg_find_unified_root(root, sizeof(root), NULL)) 150 ksft_exit_skip("cgroup v2 isn't mounted\n"); 151 152 /* 153 * Check that pids controller is available: 154 * pids is listed in cgroup.controllers 155 */ 156 if (cg_read_strstr(root, "cgroup.controllers", "pids")) 157 ksft_exit_skip("pids controller isn't available\n"); 158 159 if (cg_read_strstr(root, "cgroup.subtree_control", "pids")) 160 if (cg_write(root, "cgroup.subtree_control", "+pids")) 161 ksft_exit_skip("Failed to set pids controller\n"); 162 163 for (int i = 0; i < ARRAY_SIZE(tests); i++) { 164 switch (tests[i].fn(root)) { 165 case KSFT_PASS: 166 ksft_test_result_pass("%s\n", tests[i].name); 167 break; 168 case KSFT_SKIP: 169 ksft_test_result_skip("%s\n", tests[i].name); 170 break; 171 default: 172 ksft_test_result_fail("%s\n", tests[i].name); 173 break; 174 } 175 } 176 177 ksft_finished(); 178 } 179