2019-05-29 07:17:56 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2016-02-22 10:10:57 -08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2016 Google, Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sched.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/timerfd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include "../kselftest.h"
|
|
|
|
|
|
|
|
void child(int cpu)
|
|
|
|
{
|
|
|
|
cpu_set_t set;
|
|
|
|
|
|
|
|
CPU_ZERO(&set);
|
|
|
|
CPU_SET(cpu, &set);
|
|
|
|
if (sched_setaffinity(0, sizeof(set), &set) != 0) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("sched_setaffinity() failed: %s\n",
|
|
|
|
strerror(errno));
|
2016-02-22 10:10:57 -08:00
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("ptrace(PTRACE_TRACEME) failed: %s\n",
|
|
|
|
strerror(errno));
|
2016-02-22 10:10:57 -08:00
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (raise(SIGSTOP) != 0) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("raise(SIGSTOP) failed: %s\n", strerror(errno));
|
2016-02-22 10:10:57 -08:00
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:15:44 -04:00
|
|
|
int run_test(int cpu)
|
2016-02-22 10:10:57 -08:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
pid_t pid = fork();
|
|
|
|
pid_t wpid;
|
|
|
|
|
|
|
|
if (pid < 0) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("fork() failed: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (pid == 0)
|
|
|
|
child(cpu);
|
|
|
|
|
|
|
|
wpid = waitpid(pid, &status, __WALL);
|
|
|
|
if (wpid != pid) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("waitpid() failed: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (!WIFSTOPPED(status)) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not stop: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (WSTOPSIG(status) != SIGSTOP) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not stop with SIGSTOP: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) {
|
|
|
|
if (errno == EIO) {
|
2020-06-22 20:15:44 -04:00
|
|
|
ksft_print_msg(
|
2017-06-29 15:37:05 -06:00
|
|
|
"ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_SKIP;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("ptrace(PTRACE_SINGLESTEP) failed: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
wpid = waitpid(pid, &status, __WALL);
|
|
|
|
if (wpid != pid) {
|
2023-11-28 15:48:54 -06:00
|
|
|
ksft_print_msg("waitpid() failed: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (WIFEXITED(status)) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not single-step: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (!WIFSTOPPED(status)) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not stop: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (WSTOPSIG(status) != SIGTRAP) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not stop with SIGTRAP: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
wpid = waitpid(pid, &status, __WALL);
|
|
|
|
if (wpid != pid) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("waitpid() failed: %s\n", strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
if (!WIFEXITED(status)) {
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_print_msg("child did not exit after PTRACE_CONT: %s\n",
|
|
|
|
strerror(errno));
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_FAIL;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
2020-06-22 20:15:44 -04:00
|
|
|
return KSFT_PASS;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
|
2025-06-26 12:16:26 -07:00
|
|
|
/*
|
|
|
|
* Reads the suspend success count from sysfs.
|
|
|
|
* Returns the count on success or exits on failure.
|
|
|
|
*/
|
|
|
|
static int get_suspend_success_count_or_fail(void)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
int val;
|
|
|
|
|
|
|
|
fp = fopen("/sys/power/suspend_stats/success", "r");
|
|
|
|
if (!fp)
|
|
|
|
ksft_exit_fail_msg(
|
|
|
|
"Failed to open suspend_stats/success: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
|
|
|
|
if (fscanf(fp, "%d", &val) != 1) {
|
|
|
|
fclose(fp);
|
|
|
|
ksft_exit_fail_msg(
|
|
|
|
"Failed to read suspend success count\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2016-02-22 10:10:57 -08:00
|
|
|
void suspend(void)
|
|
|
|
{
|
|
|
|
int timerfd;
|
|
|
|
int err;
|
2025-06-26 12:16:26 -07:00
|
|
|
int count_before;
|
|
|
|
int count_after;
|
2016-02-22 10:10:57 -08:00
|
|
|
struct itimerspec spec = {};
|
|
|
|
|
2018-05-02 16:37:00 -06:00
|
|
|
if (getuid() != 0)
|
|
|
|
ksft_exit_skip("Please run the test as root - Exiting.\n");
|
|
|
|
|
2016-02-22 10:10:57 -08:00
|
|
|
timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
|
2017-06-12 08:56:50 +02:00
|
|
|
if (timerfd < 0)
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_exit_fail_msg("timerfd_create() failed\n");
|
2016-02-22 10:10:57 -08:00
|
|
|
|
|
|
|
spec.it_value.tv_sec = 5;
|
|
|
|
err = timerfd_settime(timerfd, 0, &spec, NULL);
|
2017-06-12 08:56:50 +02:00
|
|
|
if (err < 0)
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_exit_fail_msg("timerfd_settime() failed\n");
|
2016-02-22 10:10:57 -08:00
|
|
|
|
2025-06-26 12:16:26 -07:00
|
|
|
count_before = get_suspend_success_count_or_fail();
|
|
|
|
|
selftests: breakpoints: use remaining time to check if suspend succeed
step_after_suspend_test fails with device busy error while
writing to /sys/power/state to start suspend. The test believes
it failed to enter suspend state with
$ sudo ./step_after_suspend_test
TAP version 13
Bail out! Failed to enter Suspend state
However, in the kernel message, I indeed see the system get
suspended and then wake up later.
[611172.033108] PM: suspend entry (s2idle)
[611172.044940] Filesystems sync: 0.006 seconds
[611172.052254] Freezing user space processes
[611172.059319] Freezing user space processes completed (elapsed 0.001 seconds)
[611172.067920] OOM killer disabled.
[611172.072465] Freezing remaining freezable tasks
[611172.080332] Freezing remaining freezable tasks completed (elapsed 0.001 seconds)
[611172.089724] printk: Suspending console(s) (use no_console_suspend to debug)
[611172.117126] serial 00:03: disabled
some other hardware get reconnected
[611203.136277] OOM killer enabled.
[611203.140637] Restarting tasks ...
[611203.141135] usb 1-8.1: USB disconnect, device number 7
[611203.141755] done.
[611203.155268] random: crng reseeded on system resumption
[611203.162059] PM: suspend exit
After investigation, I noticed that for the code block
if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem"))
ksft_exit_fail_msg("Failed to enter Suspend state\n");
The write will return -1 and errno is set to 16 (device busy).
It should be caused by the write function is not successfully returned
before the system suspend and the return value get messed when waking up.
As a result, It may be better to check the time passed of those few
instructions to determine whether the suspend is executed correctly for
it is pretty hard to execute those few lines for 5 seconds.
The timer to wake up the system is set to expire after 5 seconds and
no re-arm. If the timer remaining time is 0 second and 0 nano secomd,
it means the timer expired and wake the system up. Otherwise, the system
could be considered to enter the suspend state failed if there is any
remaining time.
After appling this patch, the test would not fail for it believes the
system does not go to suspend by mistake. It now could continue to the
rest part of the test after suspend.
Fixes: bfd092b8c272 ("selftests: breakpoint: add step_after_suspend_test")
Reported-by: Sinadin Shan <sinadin.shan@oracle.com>
Signed-off-by: Yifei Liu <yifei.l.liu@oracle.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2024-09-30 15:40:25 -07:00
|
|
|
system("(echo mem > /sys/power/state) 2> /dev/null");
|
|
|
|
|
2025-06-26 12:16:26 -07:00
|
|
|
count_after = get_suspend_success_count_or_fail();
|
|
|
|
if (count_after <= count_before)
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_exit_fail_msg("Failed to enter Suspend state\n");
|
2016-02-22 10:10:57 -08:00
|
|
|
|
|
|
|
close(timerfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int opt;
|
|
|
|
bool do_suspend = true;
|
|
|
|
bool succeeded = true;
|
2019-04-24 16:12:37 -07:00
|
|
|
unsigned int tests = 0;
|
2016-02-22 10:10:57 -08:00
|
|
|
cpu_set_t available_cpus;
|
|
|
|
int err;
|
|
|
|
int cpu;
|
2017-06-12 08:56:50 +02:00
|
|
|
|
|
|
|
ksft_print_header();
|
2016-02-22 10:10:57 -08:00
|
|
|
|
|
|
|
while ((opt = getopt(argc, argv, "n")) != -1) {
|
|
|
|
switch (opt) {
|
|
|
|
case 'n':
|
|
|
|
do_suspend = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("Usage: %s [-n]\n", argv[0]);
|
|
|
|
printf(" -n: do not trigger a suspend/resume cycle before the test\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:15:43 -04:00
|
|
|
err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus);
|
|
|
|
if (err < 0)
|
|
|
|
ksft_exit_fail_msg("sched_getaffinity() failed\n");
|
|
|
|
|
2019-04-24 16:12:37 -07:00
|
|
|
for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
|
|
|
|
if (!CPU_ISSET(cpu, &available_cpus))
|
|
|
|
continue;
|
|
|
|
tests++;
|
|
|
|
}
|
|
|
|
|
2016-02-22 10:10:57 -08:00
|
|
|
if (do_suspend)
|
|
|
|
suspend();
|
|
|
|
|
2020-06-22 20:15:44 -04:00
|
|
|
ksft_set_plan(tests);
|
2016-02-22 10:10:57 -08:00
|
|
|
for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
|
2020-06-22 20:15:44 -04:00
|
|
|
int test_success;
|
2016-02-22 10:10:57 -08:00
|
|
|
|
|
|
|
if (!CPU_ISSET(cpu, &available_cpus))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
test_success = run_test(cpu);
|
2020-06-22 20:15:44 -04:00
|
|
|
switch (test_success) {
|
|
|
|
case KSFT_PASS:
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_test_result_pass("CPU %d\n", cpu);
|
2020-06-22 20:15:44 -04:00
|
|
|
break;
|
|
|
|
case KSFT_SKIP:
|
|
|
|
ksft_test_result_skip("CPU %d\n", cpu);
|
|
|
|
break;
|
|
|
|
case KSFT_FAIL:
|
2017-06-29 15:37:05 -06:00
|
|
|
ksft_test_result_fail("CPU %d\n", cpu);
|
2016-02-22 10:10:57 -08:00
|
|
|
succeeded = false;
|
2020-06-22 20:15:44 -04:00
|
|
|
break;
|
2016-02-22 10:10:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (succeeded)
|
|
|
|
ksft_exit_pass();
|
|
|
|
else
|
|
|
|
ksft_exit_fail();
|
|
|
|
}
|