Commit | Line | Data |
---|---|---|
3752e453 ME |
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 | ||
3752e453 ME |
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 | { | |
6861b44a | 154 | unsigned long start, end; |
3752e453 | 155 | char execute, name[128]; |
3752e453 ME |
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 | { | |
d4ecdff2 | 193 | long current; |
3752e453 ME |
194 | char *end, buf[16]; |
195 | FILE *f; | |
196 | int rc; | |
197 | ||
198 | rc = -1; | |
199 | ||
200 | f = fopen(PARANOID_PATH, "r"); | |
201 | if (!f) { | |
202 | perror("fopen"); | |
203 | goto out; | |
204 | } | |
205 | ||
206 | if (!fgets(buf, sizeof(buf), f)) { | |
207 | printf("Couldn't read " PARANOID_PATH "?\n"); | |
208 | goto out_close; | |
209 | } | |
210 | ||
d4ecdff2 | 211 | current = strtol(buf, &end, 10); |
3752e453 ME |
212 | |
213 | if (end == buf) { | |
214 | printf("Couldn't parse " PARANOID_PATH "?\n"); | |
215 | goto out_close; | |
216 | } | |
217 | ||
218 | if (current >= level) | |
d4ecdff2 | 219 | goto out_close; |
3752e453 ME |
220 | |
221 | rc = 0; | |
222 | out_close: | |
223 | fclose(f); | |
224 | out: | |
225 | return rc; | |
226 | } | |
bd8bbd87 | 227 |