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