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