1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright 2014, Michael Ellerman, IBM Corp.
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 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
33int 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
45int 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
55int 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
64int 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
73int 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
90int kill_child_and_wait(pid_t child_pid)
91{
92	kill(child_pid, SIGTERM);
93
94	return wait_for_child(child_pid);
95}
96
97static 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
116pid_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();
144out:
145	kill(pid, SIGKILL);
146
147	return rc;
148}
149
150struct addr_range libc, vdso;
151
152int parse_proc_maps(void)
153{
154	unsigned long start, end;
155	char execute, name[128];
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
191bool require_paranoia_below(int level)
192{
193	long current;
194	char *end, buf[16];
195	FILE *f;
196	bool rc;
197
198	rc = false;
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
211	current = strtol(buf, &end, 10);
212
213	if (end == buf) {
214		printf("Couldn't parse " PARANOID_PATH "?\n");
215		goto out_close;
216	}
217
218	if (current >= level)
219		goto out_close;
220
221	rc = true;
222out_close:
223	fclose(f);
224out:
225	return rc;
226}
227
228