• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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