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 <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 18int pick_online_cpu(void) 19{ 20 cpu_set_t mask; 21 int cpu; 22 23 CPU_ZERO(&mask); 24 25 if (sched_getaffinity(0, sizeof(mask), &mask)) { 26 perror("sched_getaffinity"); 27 return -1; 28 } 29 30 /* We prefer a primary thread, but skip 0 */ 31 for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8) 32 if (CPU_ISSET(cpu, &mask)) 33 return cpu; 34 35 /* Search for anything, but in reverse */ 36 for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--) 37 if (CPU_ISSET(cpu, &mask)) 38 return cpu; 39 40 printf("No cpus in affinity mask?!\n"); 41 return -1; 42} 43 44int bind_to_cpu(int cpu) 45{ 46 cpu_set_t mask; 47 48 printf("Binding to cpu %d\n", cpu); 49 50 CPU_ZERO(&mask); 51 CPU_SET(cpu, &mask); 52 53 return sched_setaffinity(0, sizeof(mask), &mask); 54} 55 56#define PARENT_TOKEN 0xAA 57#define CHILD_TOKEN 0x55 58 59int sync_with_child(union pipe read_pipe, union pipe write_pipe) 60{ 61 char c = PARENT_TOKEN; 62 63 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 64 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 65 if (c != CHILD_TOKEN) /* sometimes expected */ 66 return 1; 67 68 return 0; 69} 70 71int wait_for_parent(union pipe read_pipe) 72{ 73 char c; 74 75 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); 76 FAIL_IF(c != PARENT_TOKEN); 77 78 return 0; 79} 80 81int notify_parent(union pipe write_pipe) 82{ 83 char c = CHILD_TOKEN; 84 85 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 86 87 return 0; 88} 89 90int notify_parent_of_error(union pipe write_pipe) 91{ 92 char c = ~CHILD_TOKEN; 93 94 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); 95 96 return 0; 97} 98 99int wait_for_child(pid_t child_pid) 100{ 101 int rc; 102 103 if (waitpid(child_pid, &rc, 0) == -1) { 104 perror("waitpid"); 105 return 1; 106 } 107 108 if (WIFEXITED(rc)) 109 rc = WEXITSTATUS(rc); 110 else 111 rc = 1; /* Signal or other */ 112 113 return rc; 114} 115 116int kill_child_and_wait(pid_t child_pid) 117{ 118 kill(child_pid, SIGTERM); 119 120 return wait_for_child(child_pid); 121} 122 123static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe) 124{ 125 volatile int i = 0; 126 127 /* 128 * We are just here to eat cpu and die. So make sure we can be killed, 129 * and also don't do any custom SIGTERM handling. 130 */ 131 signal(SIGTERM, SIG_DFL); 132 133 notify_parent(write_pipe); 134 wait_for_parent(read_pipe); 135 136 /* Soak up cpu forever */ 137 while (1) i++; 138 139 return 0; 140} 141 142pid_t eat_cpu(int (test_function)(void)) 143{ 144 union pipe read_pipe, write_pipe; 145 int cpu, rc; 146 pid_t pid; 147 148 cpu = pick_online_cpu(); 149 FAIL_IF(cpu < 0); 150 FAIL_IF(bind_to_cpu(cpu)); 151 152 if (pipe(read_pipe.fds) == -1) 153 return -1; 154 155 if (pipe(write_pipe.fds) == -1) 156 return -1; 157 158 pid = fork(); 159 if (pid == 0) 160 exit(eat_cpu_child(write_pipe, read_pipe)); 161 162 if (sync_with_child(read_pipe, write_pipe)) { 163 rc = -1; 164 goto out; 165 } 166 167 printf("main test running as pid %d\n", getpid()); 168 169 rc = test_function(); 170out: 171 kill(pid, SIGKILL); 172 173 return rc; 174} 175 176struct addr_range libc, vdso; 177 178int parse_proc_maps(void) 179{ 180 unsigned long start, end; 181 char execute, name[128]; 182 FILE *f; 183 int rc; 184 185 f = fopen("/proc/self/maps", "r"); 186 if (!f) { 187 perror("fopen"); 188 return -1; 189 } 190 191 do { 192 /* This skips line with no executable which is what we want */ 193 rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n", 194 &start, &end, &execute, name); 195 if (rc <= 0) 196 break; 197 198 if (execute != 'x') 199 continue; 200 201 if (strstr(name, "libc")) { 202 libc.first = start; 203 libc.last = end - 1; 204 } else if (strstr(name, "[vdso]")) { 205 vdso.first = start; 206 vdso.last = end - 1; 207 } 208 } while(1); 209 210 fclose(f); 211 212 return 0; 213} 214 215#define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid" 216 217bool require_paranoia_below(int level) 218{ 219 unsigned long current; 220 char *end, buf[16]; 221 FILE *f; 222 int rc; 223 224 rc = -1; 225 226 f = fopen(PARANOID_PATH, "r"); 227 if (!f) { 228 perror("fopen"); 229 goto out; 230 } 231 232 if (!fgets(buf, sizeof(buf), f)) { 233 printf("Couldn't read " PARANOID_PATH "?\n"); 234 goto out_close; 235 } 236 237 current = strtoul(buf, &end, 10); 238 239 if (end == buf) { 240 printf("Couldn't parse " PARANOID_PATH "?\n"); 241 goto out_close; 242 } 243 244 if (current >= level) 245 goto out; 246 247 rc = 0; 248out_close: 249 fclose(f); 250out: 251 return rc; 252} 253 254