• 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 <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 
pick_online_cpu(void)18 int 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 
bind_to_cpu(int cpu)44 int 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 
sync_with_child(union pipe read_pipe,union pipe write_pipe)59 int 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 
wait_for_parent(union pipe read_pipe)71 int 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 
notify_parent(union pipe write_pipe)81 int 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 
notify_parent_of_error(union pipe write_pipe)90 int 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 
wait_for_child(pid_t child_pid)99 int 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 
kill_child_and_wait(pid_t child_pid)116 int kill_child_and_wait(pid_t child_pid)
117 {
118 	kill(child_pid, SIGTERM);
119 
120 	return wait_for_child(child_pid);
121 }
122 
eat_cpu_child(union pipe read_pipe,union pipe write_pipe)123 static 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 
eat_cpu(int (test_function)(void))142 pid_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();
170 out:
171 	kill(pid, SIGKILL);
172 
173 	return rc;
174 }
175 
176 struct addr_range libc, vdso;
177 
parse_proc_maps(void)178 int 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 
require_paranoia_below(int level)217 bool 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;
248 out_close:
249 	fclose(f);
250 out:
251 	return rc;
252 }
253 
254