1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2014, Michael Ellerman, IBM Corp. 4 */ 5 6 #define _GNU_SOURCE /* For CPU_ZERO etc. */ 7 8 #include <errno.h> 9 #include <sched.h> 10 #include <setjmp.h> 11 #include <stdlib.h> 12 #include <sys/wait.h> 13 14 #include "utils.h" 15 #include "lib.h" 16 17 18 int bind_to_cpu(int cpu) 19 { 20 cpu_set_t mask; 21 22 printf("Binding to cpu %d\n", cpu); 23 24 CPU_ZERO(&mask); 25 CPU_SET(cpu, &mask); 26 27 return sched_setaffinity(0, sizeof(mask), &mask); 28 } 29 30 #define PARENT_TOKEN 0xAA 31 #define CHILD_TOKEN 0x55 32 33 int sync_with_child(union pipe read_pipe, union pipe write_pipe) 34 { 35 char c = PARENT_TOKEN; 36 37 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 38 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 39 if (c != CHILD_TOKEN) /* sometimes expected */ 40 return 1; 41 42 return 0; 43 } 44 45 int wait_for_parent(union pipe read_pipe) 46 { 47 char c; 48 49 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 50 FAIL_IF(c != PARENT_TOKEN); 51 52 return 0; 53 } 54 55 int notify_parent(union pipe write_pipe) 56 { 57 char c = CHILD_TOKEN; 58 59 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 60 61 return 0; 62 } 63 64 int notify_parent_of_error(union pipe write_pipe) 65 { 66 char c = ~CHILD_TOKEN; 67 68 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 69 70 return 0; 71 } 72 73 int wait_for_child(pid_t child_pid) 74 { 75 int rc; 76 77 if (waitpid(child_pid, &rc, 0) == -1) { 78 perror("waitpid"); 79 return 1; 80 } 81 82 if (WIFEXITED(rc)) 83 rc = WEXITSTATUS(rc); 84 else 85 rc = 1; /* Signal or other */ 86 87 return rc; 88 } 89 90 int kill_child_and_wait(pid_t child_pid) 91 { 92 kill(child_pid, SIGTERM); 93 94 return wait_for_child(child_pid); 95 } 96 97 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe) 98 { 99 volatile int i = 0; 100 101 /* 102 * We are just here to eat cpu and die. So make sure we can be killed, 103 * and also don't do any custom SIGTERM handling. 104 */ 105 signal(SIGTERM, SIG_DFL); 106 107 notify_parent(write_pipe); 108 wait_for_parent(read_pipe); 109 110 /* Soak up cpu forever */ 111 while (1) i++; 112 113 return 0; 114 } 115 116 pid_t eat_cpu(int (test_function)(void)) 117 { 118 union pipe read_pipe, write_pipe; 119 int cpu, rc; 120 pid_t pid; 121 122 cpu = pick_online_cpu(); 123 FAIL_IF(cpu < 0); 124 FAIL_IF(bind_to_cpu(cpu)); 125 126 if (pipe(read_pipe.fds) == -1) 127 return -1; 128 129 if (pipe(write_pipe.fds) == -1) 130 return -1; 131 132 pid = fork(); 133 if (pid == 0) 134 exit(eat_cpu_child(write_pipe, read_pipe)); 135 136 if (sync_with_child(read_pipe, write_pipe)) { 137 rc = -1; 138 goto out; 139 } 140 141 printf("main test running as pid %d\n", getpid()); 142 143 rc = test_function(); 144 out: 145 kill(pid, SIGKILL); 146 147 return rc; 148 } 149 150 struct addr_range libc, vdso; 151 152 int parse_proc_maps(void) 153 { 154 unsigned long start, end; 155 char execute, name[128]; 156 FILE *f; 157 int rc; 158 159 f = fopen("/proc/self/maps", "r"); 160 if (!f) { 161 perror("fopen"); 162 return -1; 163 } 164 165 do { 166 /* This skips line with no executable which is what we want */ 167 rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n", 168 &start, &end, &execute, name); 169 if (rc <= 0) 170 break; 171 172 if (execute != 'x') 173 continue; 174 175 if (strstr(name, "libc")) { 176 libc.first = start; 177 libc.last = end - 1; 178 } else if (strstr(name, "[vdso]")) { 179 vdso.first = start; 180 vdso.last = end - 1; 181 } 182 } while(1); 183 184 fclose(f); 185 186 return 0; 187 } 188 189 #define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid" 190 191 bool require_paranoia_below(int level) 192 { 193 int err; 194 long current; 195 char buf[16] = {0}; 196 197 err = read_file(PARANOID_PATH, buf, sizeof(buf) - 1, NULL); 198 if (err) { 199 printf("Couldn't read " PARANOID_PATH "?\n"); 200 return false; 201 } 202 203 err = parse_long(buf, sizeof(buf), ¤t, 10); 204 if (err) { 205 printf("Couldn't parse " PARANOID_PATH "?\n"); 206 return false; 207 } 208 209 return current < level; 210 } 211