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 #define PARENT_TOKEN 0xAA
18 #define CHILD_TOKEN 0x55
19
sync_with_child(union pipe read_pipe,union pipe write_pipe)20 int sync_with_child(union pipe read_pipe, union pipe write_pipe)
21 {
22 char c = PARENT_TOKEN;
23
24 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
25 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
26 if (c != CHILD_TOKEN) /* sometimes expected */
27 return 1;
28
29 return 0;
30 }
31
wait_for_parent(union pipe read_pipe)32 int wait_for_parent(union pipe read_pipe)
33 {
34 char c;
35
36 FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
37 FAIL_IF(c != PARENT_TOKEN);
38
39 return 0;
40 }
41
notify_parent(union pipe write_pipe)42 int notify_parent(union pipe write_pipe)
43 {
44 char c = CHILD_TOKEN;
45
46 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
47
48 return 0;
49 }
50
notify_parent_of_error(union pipe write_pipe)51 int notify_parent_of_error(union pipe write_pipe)
52 {
53 char c = ~CHILD_TOKEN;
54
55 FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
56
57 return 0;
58 }
59
wait_for_child(pid_t child_pid)60 int wait_for_child(pid_t child_pid)
61 {
62 int rc;
63
64 if (waitpid(child_pid, &rc, 0) == -1) {
65 perror("waitpid");
66 return 1;
67 }
68
69 if (WIFEXITED(rc))
70 rc = WEXITSTATUS(rc);
71 else
72 rc = 1; /* Signal or other */
73
74 return rc;
75 }
76
kill_child_and_wait(pid_t child_pid)77 int kill_child_and_wait(pid_t child_pid)
78 {
79 kill(child_pid, SIGTERM);
80
81 return wait_for_child(child_pid);
82 }
83
eat_cpu_child(union pipe read_pipe,union pipe write_pipe)84 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
85 {
86 volatile int i = 0;
87
88 /*
89 * We are just here to eat cpu and die. So make sure we can be killed,
90 * and also don't do any custom SIGTERM handling.
91 */
92 signal(SIGTERM, SIG_DFL);
93
94 notify_parent(write_pipe);
95 wait_for_parent(read_pipe);
96
97 /* Soak up cpu forever */
98 while (1) i++;
99
100 return 0;
101 }
102
eat_cpu(int (test_function)(void))103 pid_t eat_cpu(int (test_function)(void))
104 {
105 union pipe read_pipe, write_pipe;
106 int rc;
107 pid_t pid;
108
109 FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
110
111 if (pipe(read_pipe.fds) == -1)
112 return -1;
113
114 if (pipe(write_pipe.fds) == -1)
115 return -1;
116
117 pid = fork();
118 if (pid == 0)
119 exit(eat_cpu_child(write_pipe, read_pipe));
120
121 if (sync_with_child(read_pipe, write_pipe)) {
122 rc = -1;
123 goto out;
124 }
125
126 printf("main test running as pid %d\n", getpid());
127
128 rc = test_function();
129 out:
130 kill(pid, SIGKILL);
131
132 return rc;
133 }
134
135 struct addr_range libc, vdso;
136
parse_proc_maps(void)137 int parse_proc_maps(void)
138 {
139 unsigned long start, end;
140 char execute, name[128];
141 FILE *f;
142 int rc;
143
144 f = fopen("/proc/self/maps", "r");
145 if (!f) {
146 perror("fopen");
147 return -1;
148 }
149
150 do {
151 /* This skips line with no executable which is what we want */
152 rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
153 &start, &end, &execute, name);
154 if (rc <= 0)
155 break;
156
157 if (execute != 'x')
158 continue;
159
160 if (strstr(name, "libc")) {
161 libc.first = start;
162 libc.last = end - 1;
163 } else if (strstr(name, "[vdso]")) {
164 vdso.first = start;
165 vdso.last = end - 1;
166 }
167 } while(1);
168
169 fclose(f);
170
171 return 0;
172 }
173
174 #define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid"
175
require_paranoia_below(int level)176 bool require_paranoia_below(int level)
177 {
178 int err;
179 long current;
180
181 err = read_long(PARANOID_PATH, ¤t, 10);
182 if (err) {
183 printf("Couldn't parse " PARANOID_PATH "?\n");
184 return false;
185 }
186
187 return current < level;
188 }
189