1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2016 Google, Inc. |
4 | */ |
5 | |
6 | #define _GNU_SOURCE |
7 | |
8 | #include <errno.h> |
9 | #include <fcntl.h> |
10 | #include <sched.h> |
11 | #include <signal.h> |
12 | #include <stdbool.h> |
13 | #include <stdio.h> |
14 | #include <string.h> |
15 | #include <unistd.h> |
16 | #include <sys/ptrace.h> |
17 | #include <sys/stat.h> |
18 | #include <sys/timerfd.h> |
19 | #include <sys/types.h> |
20 | #include <sys/wait.h> |
21 | |
22 | #include "../kselftest.h" |
23 | |
24 | void child(int cpu) |
25 | { |
26 | cpu_set_t set; |
27 | |
28 | CPU_ZERO(&set); |
29 | CPU_SET(cpu, &set); |
30 | if (sched_setaffinity(0, sizeof(set), &set) != 0) { |
31 | ksft_print_msg(msg: "sched_setaffinity() failed: %s\n" , |
32 | strerror(errno)); |
33 | _exit(1); |
34 | } |
35 | |
36 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { |
37 | ksft_print_msg(msg: "ptrace(PTRACE_TRACEME) failed: %s\n" , |
38 | strerror(errno)); |
39 | _exit(1); |
40 | } |
41 | |
42 | if (raise(SIGSTOP) != 0) { |
43 | ksft_print_msg(msg: "raise(SIGSTOP) failed: %s\n" , strerror(errno)); |
44 | _exit(1); |
45 | } |
46 | |
47 | _exit(0); |
48 | } |
49 | |
50 | int run_test(int cpu) |
51 | { |
52 | int status; |
53 | pid_t pid = fork(); |
54 | pid_t wpid; |
55 | |
56 | if (pid < 0) { |
57 | ksft_print_msg(msg: "fork() failed: %s\n" , strerror(errno)); |
58 | return KSFT_FAIL; |
59 | } |
60 | if (pid == 0) |
61 | child(cpu); |
62 | |
63 | wpid = waitpid(pid, &status, __WALL); |
64 | if (wpid != pid) { |
65 | ksft_print_msg(msg: "waitpid() failed: %s\n" , strerror(errno)); |
66 | return KSFT_FAIL; |
67 | } |
68 | if (!WIFSTOPPED(status)) { |
69 | ksft_print_msg(msg: "child did not stop: %s\n" , strerror(errno)); |
70 | return KSFT_FAIL; |
71 | } |
72 | if (WSTOPSIG(status) != SIGSTOP) { |
73 | ksft_print_msg(msg: "child did not stop with SIGSTOP: %s\n" , |
74 | strerror(errno)); |
75 | return KSFT_FAIL; |
76 | } |
77 | |
78 | if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) { |
79 | if (errno == EIO) { |
80 | ksft_print_msg( |
81 | msg: "ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s\n" , |
82 | strerror(errno)); |
83 | return KSFT_SKIP; |
84 | } |
85 | ksft_print_msg(msg: "ptrace(PTRACE_SINGLESTEP) failed: %s\n" , |
86 | strerror(errno)); |
87 | return KSFT_FAIL; |
88 | } |
89 | |
90 | wpid = waitpid(pid, &status, __WALL); |
91 | if (wpid != pid) { |
92 | ksft_print_msg(msg: "waitpid() failed: %s\n" , strerror(errno)); |
93 | return KSFT_FAIL; |
94 | } |
95 | if (WIFEXITED(status)) { |
96 | ksft_print_msg(msg: "child did not single-step: %s\n" , |
97 | strerror(errno)); |
98 | return KSFT_FAIL; |
99 | } |
100 | if (!WIFSTOPPED(status)) { |
101 | ksft_print_msg("child did not stop: %s\n" , strerror(errno)); |
102 | return KSFT_FAIL; |
103 | } |
104 | if (WSTOPSIG(status) != SIGTRAP) { |
105 | ksft_print_msg("child did not stop with SIGTRAP: %s\n" , |
106 | strerror(errno)); |
107 | return KSFT_FAIL; |
108 | } |
109 | |
110 | if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { |
111 | ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n" , |
112 | strerror(errno)); |
113 | return KSFT_FAIL; |
114 | } |
115 | |
116 | wpid = waitpid(pid, &status, __WALL); |
117 | if (wpid != pid) { |
118 | ksft_print_msg("waitpid() failed: %s\n" , strerror(errno)); |
119 | return KSFT_FAIL; |
120 | } |
121 | if (!WIFEXITED(status)) { |
122 | ksft_print_msg("child did not exit after PTRACE_CONT: %s\n" , |
123 | strerror(errno)); |
124 | return KSFT_FAIL; |
125 | } |
126 | |
127 | return KSFT_PASS; |
128 | } |
129 | |
130 | void suspend(void) |
131 | { |
132 | int power_state_fd; |
133 | struct sigevent event = {}; |
134 | int timerfd; |
135 | int err; |
136 | struct itimerspec spec = {}; |
137 | |
138 | if (getuid() != 0) |
139 | ksft_exit_skip(msg: "Please run the test as root - Exiting.\n" ); |
140 | |
141 | power_state_fd = open("/sys/power/state" , O_RDWR); |
142 | if (power_state_fd < 0) |
143 | ksft_exit_fail_msg( |
144 | "open(\"/sys/power/state\") failed %s)\n" , |
145 | strerror(errno)); |
146 | |
147 | timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); |
148 | if (timerfd < 0) |
149 | ksft_exit_fail_msg(msg: "timerfd_create() failed\n" ); |
150 | |
151 | spec.it_value.tv_sec = 5; |
152 | err = timerfd_settime(timerfd, 0, &spec, NULL); |
153 | if (err < 0) |
154 | ksft_exit_fail_msg(msg: "timerfd_settime() failed\n" ); |
155 | |
156 | if (write(power_state_fd, "mem" , strlen("mem" )) != strlen("mem" )) |
157 | ksft_exit_fail_msg(msg: "Failed to enter Suspend state\n" ); |
158 | |
159 | close(timerfd); |
160 | close(power_state_fd); |
161 | } |
162 | |
163 | int main(int argc, char **argv) |
164 | { |
165 | int opt; |
166 | bool do_suspend = true; |
167 | bool succeeded = true; |
168 | unsigned int tests = 0; |
169 | cpu_set_t available_cpus; |
170 | int err; |
171 | int cpu; |
172 | |
173 | ksft_print_header(); |
174 | |
175 | while ((opt = getopt(argc, argv, "n" )) != -1) { |
176 | switch (opt) { |
177 | case 'n': |
178 | do_suspend = false; |
179 | break; |
180 | default: |
181 | printf("Usage: %s [-n]\n" , argv[0]); |
182 | printf(" -n: do not trigger a suspend/resume cycle before the test\n" ); |
183 | return -1; |
184 | } |
185 | } |
186 | |
187 | err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus); |
188 | if (err < 0) |
189 | ksft_exit_fail_msg(msg: "sched_getaffinity() failed\n" ); |
190 | |
191 | for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { |
192 | if (!CPU_ISSET(cpu, &available_cpus)) |
193 | continue; |
194 | tests++; |
195 | } |
196 | |
197 | if (do_suspend) |
198 | suspend(); |
199 | |
200 | ksft_set_plan(plan: tests); |
201 | for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { |
202 | int test_success; |
203 | |
204 | if (!CPU_ISSET(cpu, &available_cpus)) |
205 | continue; |
206 | |
207 | test_success = run_test(cpu); |
208 | switch (test_success) { |
209 | case KSFT_PASS: |
210 | ksft_test_result_pass("CPU %d\n" , cpu); |
211 | break; |
212 | case KSFT_SKIP: |
213 | ksft_test_result_skip("CPU %d\n" , cpu); |
214 | break; |
215 | case KSFT_FAIL: |
216 | ksft_test_result_fail("CPU %d\n" , cpu); |
217 | succeeded = false; |
218 | break; |
219 | } |
220 | } |
221 | |
222 | if (succeeded) |
223 | ksft_exit_pass(); |
224 | else |
225 | ksft_exit_fail(); |
226 | } |
227 | |