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
pick_online_cpu(void)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
bind_to_cpu(int cpu)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
sync_with_child(union pipe read_pipe,union pipe write_pipe)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
wait_for_parent(union pipe read_pipe)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
notify_parent(union pipe write_pipe)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
notify_parent_of_error(union pipe write_pipe)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
wait_for_child(pid_t child_pid)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
kill_child_and_wait(pid_t child_pid)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
eat_cpu_child(union pipe read_pipe,union pipe write_pipe)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
eat_cpu(int (test_function)(void))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
parse_proc_maps(void)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
require_paranoia_below(int level)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
get_auxv_entry(int type)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