1 /* 2 * Copyright 2014, Michael Ellerman, IBM Corp. 3 * Licensed under GPLv2. 4 */ 5 6 #define _GNU_SOURCE /* For CPU_ZERO etc. */ 7 8 #include <elf.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <link.h> 12 #include <sched.h> 13 #include <setjmp.h> 14 #include <stdlib.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 19 #include "utils.h" 20 #include "lib.h" 21 22 23 int pick_online_cpu(void) 24 { 25 cpu_set_t mask; 26 int cpu; 27 28 CPU_ZERO(&mask); 29 30 if (sched_getaffinity(0, sizeof(mask), &mask)) { 31 perror("sched_getaffinity"); 32 return -1; 33 } 34 35 /* We prefer a primary thread, but skip 0 */ 36 for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8) 37 if (CPU_ISSET(cpu, &mask)) 38 return cpu; 39 40 /* Search for anything, but in reverse */ 41 for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--) 42 if (CPU_ISSET(cpu, &mask)) 43 return cpu; 44 45 printf("No cpus in affinity mask?!\n"); 46 return -1; 47 } 48 49 int bind_to_cpu(int cpu) 50 { 51 cpu_set_t mask; 52 53 printf("Binding to cpu %d\n", cpu); 54 55 CPU_ZERO(&mask); 56 CPU_SET(cpu, &mask); 57 58 return sched_setaffinity(0, sizeof(mask), &mask); 59 } 60 61 #define PARENT_TOKEN 0xAA 62 #define CHILD_TOKEN 0x55 63 64 int sync_with_child(union pipe read_pipe, union pipe write_pipe) 65 { 66 char c = PARENT_TOKEN; 67 68 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 69 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 70 if (c != CHILD_TOKEN) /* sometimes expected */ 71 return 1; 72 73 return 0; 74 } 75 76 int wait_for_parent(union pipe read_pipe) 77 { 78 char c; 79 80 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 81 FAIL_IF(c != PARENT_TOKEN); 82 83 return 0; 84 } 85 86 int notify_parent(union pipe write_pipe) 87 { 88 char c = CHILD_TOKEN; 89 90 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 91 92 return 0; 93 } 94 95 int notify_parent_of_error(union pipe write_pipe) 96 { 97 char c = ~CHILD_TOKEN; 98 99 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 100 101 return 0; 102 } 103 104 int wait_for_child(pid_t child_pid) 105 { 106 int rc; 107 108 if (waitpid(child_pid, &rc, 0) == -1) { 109 perror("waitpid"); 110 return 1; 111 } 112 113 if (WIFEXITED(rc)) 114 rc = WEXITSTATUS(rc); 115 else 116 rc = 1; /* Signal or other */ 117 118 return rc; 119 } 120 121 int kill_child_and_wait(pid_t child_pid) 122 { 123 kill(child_pid, SIGTERM); 124 125 return wait_for_child(child_pid); 126 } 127 128 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe) 129 { 130 volatile int i = 0; 131 132 /* 133 * We are just here to eat cpu and die. So make sure we can be killed, 134 * and also don't do any custom SIGTERM handling. 135 */ 136 signal(SIGTERM, SIG_DFL); 137 138 notify_parent(write_pipe); 139 wait_for_parent(read_pipe); 140 141 /* Soak up cpu forever */ 142 while (1) i++; 143 144 return 0; 145 } 146 147 pid_t eat_cpu(int (test_function)(void)) 148 { 149 union pipe read_pipe, write_pipe; 150 int cpu, rc; 151 pid_t pid; 152 153 cpu = pick_online_cpu(); 154 FAIL_IF(cpu < 0); 155 FAIL_IF(bind_to_cpu(cpu)); 156 157 if (pipe(read_pipe.fds) == -1) 158 return -1; 159 160 if (pipe(write_pipe.fds) == -1) 161 return -1; 162 163 pid = fork(); 164 if (pid == 0) 165 exit(eat_cpu_child(write_pipe, read_pipe)); 166 167 if (sync_with_child(read_pipe, write_pipe)) { 168 rc = -1; 169 goto out; 170 } 171 172 printf("main test running as pid %d\n", getpid()); 173 174 rc = test_function(); 175 out: 176 kill(pid, SIGKILL); 177 178 return rc; 179 } 180 181 struct addr_range libc, vdso; 182 183 int parse_proc_maps(void) 184 { 185 unsigned long start, end; 186 char execute, name[128]; 187 FILE *f; 188 int rc; 189 190 f = fopen("/proc/self/maps", "r"); 191 if (!f) { 192 perror("fopen"); 193 return -1; 194 } 195 196 do { 197 /* This skips line with no executable which is what we want */ 198 rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n", 199 &start, &end, &execute, name); 200 if (rc <= 0) 201 break; 202 203 if (execute != 'x') 204 continue; 205 206 if (strstr(name, "libc")) { 207 libc.first = start; 208 libc.last = end - 1; 209 } else if (strstr(name, "[vdso]")) { 210 vdso.first = start; 211 vdso.last = end - 1; 212 } 213 } while(1); 214 215 fclose(f); 216 217 return 0; 218 } 219 220 #define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid" 221 222 bool require_paranoia_below(int level) 223 { 224 unsigned long current; 225 char *end, buf[16]; 226 FILE *f; 227 int rc; 228 229 rc = -1; 230 231 f = fopen(PARANOID_PATH, "r"); 232 if (!f) { 233 perror("fopen"); 234 goto out; 235 } 236 237 if (!fgets(buf, sizeof(buf), f)) { 238 printf("Couldn't read " PARANOID_PATH "?\n"); 239 goto out_close; 240 } 241 242 current = strtoul(buf, &end, 10); 243 244 if (end == buf) { 245 printf("Couldn't parse " PARANOID_PATH "?\n"); 246 goto out_close; 247 } 248 249 if (current >= level) 250 goto out; 251 252 rc = 0; 253 out_close: 254 fclose(f); 255 out: 256 return rc; 257 } 258 259 static char auxv[4096]; 260 261 void *get_auxv_entry(int type) 262 { 263 ElfW(auxv_t) *p; 264 void *result; 265 ssize_t num; 266 int fd; 267 268 fd = open("/proc/self/auxv", O_RDONLY); 269 if (fd == -1) { 270 perror("open"); 271 return NULL; 272 } 273 274 result = NULL; 275 276 num = read(fd, auxv, sizeof(auxv)); 277 if (num < 0) { 278 perror("read"); 279 goto out; 280 } 281 282 if (num > sizeof(auxv)) { 283 printf("Overflowed auxv buffer\n"); 284 goto out; 285 } 286 287 p = (ElfW(auxv_t) *)auxv; 288 289 while (p->a_type != AT_NULL) { 290 if (p->a_type == type) { 291 result = (void *)p->a_un.a_val; 292 break; 293 } 294 295 p++; 296 } 297 out: 298 close(fd); 299 return result; 300 } 301