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