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 ) ;
}
bool run_test ( int cpu )
{
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
if ( ! WIFSTOPPED ( status ) ) {
2017-06-29 15:37:05 -06:00
ksft_print_msg ( " child did not stop: %s \n " , strerror ( errno ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
if ( ptrace ( PTRACE_SINGLESTEP , pid , NULL , NULL ) < 0 ) {
if ( errno = = EIO ) {
2017-06-29 15:37:05 -06:00
ksft_exit_skip (
" ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s \n " ,
strerror ( errno ) ) ;
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
if ( WIFEXITED ( status ) ) {
2017-06-29 15:37:05 -06:00
ksft_print_msg ( " child did not single-step: %s \n " ,
strerror ( errno ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
if ( ! WIFSTOPPED ( status ) ) {
2017-06-29 15:37:05 -06:00
ksft_print_msg ( " child did not stop: %s \n " , strerror ( errno ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
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 ) ) ;
2016-02-22 10:10:57 -08:00
return false ;
}
return true ;
}
void suspend ( void )
{
int power_state_fd ;
struct sigevent event = { } ;
int timerfd ;
int err ;
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
power_state_fd = open ( " /sys/power/state " , O_RDWR ) ;
2017-06-12 08:56:50 +02:00
if ( power_state_fd < 0 )
ksft_exit_fail_msg (
2018-05-02 16:37:00 -06:00
" open( \" /sys/power/state \" ) failed %s) \n " ,
strerror ( errno ) ) ;
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
2017-06-12 08:56:50 +02:00
if ( write ( power_state_fd , " mem " , strlen ( " mem " ) ) ! = strlen ( " mem " ) )
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 ) ;
close ( power_state_fd ) ;
}
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 ;
}
}
2019-04-24 16:12:37 -07:00
for ( cpu = 0 ; cpu < CPU_SETSIZE ; cpu + + ) {
if ( ! CPU_ISSET ( cpu , & available_cpus ) )
continue ;
tests + + ;
}
ksft_set_plan ( tests ) ;
2016-02-22 10:10:57 -08:00
if ( do_suspend )
suspend ( ) ;
err = sched_getaffinity ( 0 , sizeof ( available_cpus ) , & available_cpus ) ;
2017-06-12 08:56:50 +02:00
if ( err < 0 )
2017-06-29 15:37:05 -06:00
ksft_exit_fail_msg ( " sched_getaffinity() failed \n " ) ;
2016-02-22 10:10:57 -08:00
for ( cpu = 0 ; cpu < CPU_SETSIZE ; cpu + + ) {
bool test_success ;
if ( ! CPU_ISSET ( cpu , & available_cpus ) )
continue ;
test_success = run_test ( cpu ) ;
if ( test_success ) {
2017-06-29 15:37:05 -06:00
ksft_test_result_pass ( " CPU %d \n " , cpu ) ;
2016-02-22 10:10:57 -08:00
} else {
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 ;
}
}
if ( succeeded )
ksft_exit_pass ( ) ;
else
ksft_exit_fail ( ) ;
}