2019-05-28 09:57:18 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-11-24 13:05:38 +11:00
/*
* Copyright 2013 - 2015 , Michael Ellerman , IBM Corp .
*/
2015-12-16 18:59:31 +11:00
# define _GNU_SOURCE /* For CPU_ZERO etc. */
2015-11-24 13:05:38 +11:00
# include <elf.h>
# include <errno.h>
# include <fcntl.h>
2023-02-03 11:39:45 +11:00
# include <inttypes.h>
# include <limits.h>
2015-11-24 13:05:38 +11:00
# include <link.h>
2015-12-16 18:59:31 +11:00
# include <sched.h>
2015-11-24 13:05:38 +11:00
# include <stdio.h>
2018-05-21 20:43:57 +05:30
# include <stdlib.h>
2018-07-26 22:24:57 +10:00
# include <string.h>
2018-05-21 20:43:57 +05:30
# include <sys/ioctl.h>
2015-11-24 13:05:38 +11:00
# include <sys/stat.h>
2020-07-30 10:38:46 +05:30
# include <sys/sysinfo.h>
2015-11-24 13:05:38 +11:00
# include <sys/types.h>
2018-07-26 22:24:57 +10:00
# include <sys/utsname.h>
2015-11-24 13:05:38 +11:00
# include <unistd.h>
2018-05-21 20:43:57 +05:30
# include <asm/unistd.h>
# include <linux/limits.h>
2015-11-24 13:05:38 +11:00
# include "utils.h"
static char auxv [ 4096 ] ;
2023-02-03 11:39:43 +11:00
int read_file ( const char * path , char * buf , size_t count , size_t * len )
2015-11-24 13:05:38 +11:00
{
2023-02-03 11:39:43 +11:00
ssize_t rc ;
int fd ;
int err ;
char eof ;
2015-11-24 13:05:38 +11:00
2023-02-03 11:39:43 +11:00
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
2017-02-06 21:13:27 +11:00
return - errno ;
2023-02-03 11:39:43 +11:00
rc = read ( fd , buf , count ) ;
if ( rc < 0 ) {
err = - errno ;
goto out ;
2015-11-24 13:05:38 +11:00
}
2023-02-03 11:39:43 +11:00
if ( len )
* len = rc ;
/* Overflow if there are still more bytes after filling the buffer */
if ( rc = = count ) {
rc = read ( fd , & eof , 1 ) ;
if ( rc ! = 0 ) {
err = - EOVERFLOW ;
goto out ;
}
}
err = 0 ;
out :
close ( fd ) ;
errno = - err ;
return err ;
}
2023-02-03 11:39:47 +11:00
int read_file_alloc ( const char * path , char * * buf , size_t * len )
{
size_t read_offset = 0 ;
size_t buffer_len = 0 ;
char * buffer = NULL ;
int err ;
int fd ;
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
return - errno ;
/*
* We don ' t use stat & preallocate st_size because some non - files
* report 0 file size . Instead just dynamically grow the buffer
* as needed .
*/
while ( 1 ) {
ssize_t rc ;
if ( read_offset > = buffer_len / 2 ) {
char * next_buffer ;
buffer_len = buffer_len ? buffer_len * 2 : 4096 ;
next_buffer = realloc ( buffer , buffer_len ) ;
if ( ! next_buffer ) {
err = - errno ;
goto out ;
}
buffer = next_buffer ;
}
rc = read ( fd , buffer + read_offset , buffer_len - read_offset ) ;
if ( rc < 0 ) {
err = - errno ;
goto out ;
}
if ( rc = = 0 )
break ;
read_offset + = rc ;
}
* buf = buffer ;
if ( len )
* len = read_offset ;
err = 0 ;
out :
close ( fd ) ;
if ( err )
free ( buffer ) ;
errno = - err ;
return err ;
}
2023-02-03 11:39:43 +11:00
int write_file ( const char * path , const char * buf , size_t count )
{
int fd ;
int err ;
ssize_t rc ;
fd = open ( path , O_WRONLY | O_CREAT | O_TRUNC , 0644 ) ;
if ( fd < 0 )
return - errno ;
rc = write ( fd , buf , count ) ;
if ( rc < 0 ) {
err = - errno ;
2015-11-24 13:05:38 +11:00
goto out ;
}
2023-02-03 11:39:43 +11:00
if ( rc ! = count ) {
err = - EOVERFLOW ;
2015-11-24 13:05:38 +11:00
goto out ;
}
2023-02-03 11:39:43 +11:00
err = 0 ;
2017-02-06 21:13:27 +11:00
out :
close ( fd ) ;
2023-02-03 11:39:43 +11:00
errno = - err ;
return err ;
}
int read_auxv ( char * buf , ssize_t buf_size )
{
int err ;
err = read_file ( " /proc/self/auxv " , buf , buf_size , NULL ) ;
if ( err ) {
perror ( " Error reading /proc/self/auxv " ) ;
return err ;
}
return 0 ;
2017-02-06 21:13:27 +11:00
}
2023-02-03 11:39:44 +11:00
int read_debugfs_file ( const char * subpath , char * buf , size_t count )
{
char path [ PATH_MAX ] = " /sys/kernel/debug/ " ;
strncat ( path , subpath , sizeof ( path ) - strlen ( path ) - 1 ) ;
return read_file ( path , buf , count , NULL ) ;
}
int write_debugfs_file ( const char * subpath , const char * buf , size_t count )
{
char path [ PATH_MAX ] = " /sys/kernel/debug/ " ;
strncat ( path , subpath , sizeof ( path ) - strlen ( path ) - 1 ) ;
return write_file ( path , buf , count ) ;
}
2023-02-03 11:39:45 +11:00
static int validate_int_parse ( const char * buffer , size_t count , char * end )
{
int err = 0 ;
/* Require at least one digit */
if ( end = = buffer ) {
err = - EINVAL ;
goto out ;
}
/* Require all remaining characters be whitespace-ish */
for ( ; end < buffer + count ; end + + ) {
if ( * end = = ' \0 ' )
break ;
if ( * end ! = ' ' & & * end ! = ' \n ' ) {
err = - EINVAL ;
goto out ;
}
}
out :
errno = - err ;
return err ;
}
static int parse_bounded_int ( const char * buffer , size_t count , intmax_t * result ,
int base , intmax_t min , intmax_t max )
{
int err ;
char * end ;
errno = 0 ;
* result = strtoimax ( buffer , & end , base ) ;
if ( errno )
return - errno ;
err = validate_int_parse ( buffer , count , end ) ;
if ( err )
goto out ;
if ( * result < min | | * result > max )
err = - EOVERFLOW ;
out :
errno = - err ;
return err ;
}
static int parse_bounded_uint ( const char * buffer , size_t count , uintmax_t * result ,
int base , uintmax_t max )
{
int err = 0 ;
char * end ;
errno = 0 ;
* result = strtoumax ( buffer , & end , base ) ;
if ( errno )
return - errno ;
err = validate_int_parse ( buffer , count , end ) ;
if ( err )
goto out ;
if ( * result > max )
err = - EOVERFLOW ;
out :
errno = - err ;
return err ;
}
int parse_intmax ( const char * buffer , size_t count , intmax_t * result , int base )
{
return parse_bounded_int ( buffer , count , result , base , INTMAX_MIN , INTMAX_MAX ) ;
}
int parse_uintmax ( const char * buffer , size_t count , uintmax_t * result , int base )
{
return parse_bounded_uint ( buffer , count , result , base , UINTMAX_MAX ) ;
}
int parse_int ( const char * buffer , size_t count , int * result , int base )
{
intmax_t parsed ;
int err = parse_bounded_int ( buffer , count , & parsed , base , INT_MIN , INT_MAX ) ;
* result = parsed ;
return err ;
}
int parse_uint ( const char * buffer , size_t count , unsigned int * result , int base )
{
uintmax_t parsed ;
int err = parse_bounded_uint ( buffer , count , & parsed , base , UINT_MAX ) ;
* result = parsed ;
return err ;
}
int parse_long ( const char * buffer , size_t count , long * result , int base )
{
intmax_t parsed ;
int err = parse_bounded_int ( buffer , count , & parsed , base , LONG_MIN , LONG_MAX ) ;
* result = parsed ;
return err ;
}
int parse_ulong ( const char * buffer , size_t count , unsigned long * result , int base )
{
uintmax_t parsed ;
int err = parse_bounded_uint ( buffer , count , & parsed , base , ULONG_MAX ) ;
* result = parsed ;
return err ;
}
2023-02-03 11:39:46 +11:00
int read_long ( const char * path , long * result , int base )
{
int err ;
char buffer [ 32 ] = { 0 } ;
err = read_file ( path , buffer , sizeof ( buffer ) - 1 , NULL ) ;
if ( err )
return err ;
return parse_long ( buffer , sizeof ( buffer ) , result , base ) ;
}
int read_ulong ( const char * path , unsigned long * result , int base )
{
int err ;
char buffer [ 32 ] = { 0 } ;
err = read_file ( path , buffer , sizeof ( buffer ) - 1 , NULL ) ;
if ( err )
return err ;
return parse_ulong ( buffer , sizeof ( buffer ) , result , base ) ;
}
int write_long ( const char * path , long result , int base )
{
int err ;
int len ;
char buffer [ 32 ] ;
/* Decimal only for now: no format specifier for signed hex values */
if ( base ! = 10 ) {
err = - EINVAL ;
goto out ;
}
len = snprintf ( buffer , sizeof ( buffer ) , " %ld " , result ) ;
if ( len < 0 | | len > = sizeof ( buffer ) ) {
err = - EOVERFLOW ;
goto out ;
}
err = write_file ( path , buffer , len ) ;
out :
errno = - err ;
return err ;
}
int write_ulong ( const char * path , unsigned long result , int base )
{
int err ;
int len ;
char buffer [ 32 ] ;
char * fmt ;
switch ( base ) {
case 10 :
fmt = " %lu " ;
break ;
case 16 :
fmt = " %lx " ;
break ;
default :
err = - EINVAL ;
goto out ;
}
len = snprintf ( buffer , sizeof ( buffer ) , fmt , result ) ;
if ( len < 0 | | len > = sizeof ( buffer ) ) {
err = - errno ;
goto out ;
}
err = write_file ( path , buffer , len ) ;
out :
errno = - err ;
return err ;
}
2017-02-06 21:13:27 +11:00
void * find_auxv_entry ( int type , char * auxv )
{
ElfW ( auxv_t ) * p ;
2015-11-24 13:05:38 +11:00
p = ( ElfW ( auxv_t ) * ) auxv ;
while ( p - > a_type ! = AT_NULL ) {
2017-02-06 21:13:27 +11:00
if ( p - > a_type = = type )
return p ;
2015-11-24 13:05:38 +11:00
p + + ;
}
2017-02-06 21:13:27 +11:00
return NULL ;
}
void * get_auxv_entry ( int type )
{
ElfW ( auxv_t ) * p ;
if ( read_auxv ( auxv , sizeof ( auxv ) ) )
return NULL ;
p = find_auxv_entry ( type , auxv ) ;
if ( p )
return ( void * ) p - > a_un . a_val ;
return NULL ;
2015-11-24 13:05:38 +11:00
}
2015-12-16 18:59:31 +11:00
int pick_online_cpu ( void )
{
2020-07-30 10:38:46 +05:30
int ncpus , cpu = - 1 ;
cpu_set_t * mask ;
size_t size ;
ncpus = get_nprocs_conf ( ) ;
size = CPU_ALLOC_SIZE ( ncpus ) ;
mask = CPU_ALLOC ( ncpus ) ;
if ( ! mask ) {
perror ( " malloc " ) ;
return - 1 ;
}
2015-12-16 18:59:31 +11:00
2020-07-30 10:38:46 +05:30
CPU_ZERO_S ( size , mask ) ;
2015-12-16 18:59:31 +11:00
2020-07-30 10:38:46 +05:30
if ( sched_getaffinity ( 0 , size , mask ) ) {
2015-12-16 18:59:31 +11:00
perror ( " sched_getaffinity " ) ;
2020-07-30 10:38:46 +05:30
goto done ;
2015-12-16 18:59:31 +11:00
}
/* We prefer a primary thread, but skip 0 */
2020-07-30 10:38:46 +05:30
for ( cpu = 8 ; cpu < ncpus ; cpu + = 8 )
if ( CPU_ISSET_S ( cpu , size , mask ) )
goto done ;
2015-12-16 18:59:31 +11:00
/* Search for anything, but in reverse */
2020-07-30 10:38:46 +05:30
for ( cpu = ncpus - 1 ; cpu > = 0 ; cpu - - )
if ( CPU_ISSET_S ( cpu , size , mask ) )
goto done ;
2015-12-16 18:59:31 +11:00
printf ( " No cpus in affinity mask?! \n " ) ;
2020-07-30 10:38:46 +05:30
done :
CPU_FREE ( mask ) ;
return cpu ;
2015-12-16 18:59:31 +11:00
}
2018-07-26 22:24:57 +10:00
2023-04-06 14:33:15 +10:00
int bind_to_cpu ( int cpu )
{
cpu_set_t mask ;
2023-04-06 14:33:16 +10:00
int err ;
if ( cpu = = BIND_CPU_ANY ) {
cpu = pick_online_cpu ( ) ;
if ( cpu < 0 )
return cpu ;
}
2023-04-06 14:33:15 +10:00
printf ( " Binding to cpu %d \n " , cpu ) ;
CPU_ZERO ( & mask ) ;
CPU_SET ( cpu , & mask ) ;
2023-04-06 14:33:16 +10:00
err = sched_setaffinity ( 0 , sizeof ( mask ) , & mask ) ;
if ( err )
return err ;
return cpu ;
2023-04-06 14:33:15 +10:00
}
2018-07-26 22:24:57 +10:00
bool is_ppc64le ( void )
{
struct utsname uts ;
int rc ;
errno = 0 ;
rc = uname ( & uts ) ;
if ( rc ) {
perror ( " uname " ) ;
return false ;
}
return strcmp ( uts . machine , " ppc64le " ) = = 0 ;
}
2018-05-21 20:43:57 +05:30
2019-05-20 20:55:20 +10:00
int read_sysfs_file ( char * fpath , char * result , size_t result_size )
{
char path [ PATH_MAX ] = " /sys/ " ;
strncat ( path , fpath , PATH_MAX - strlen ( path ) - 1 ) ;
2023-02-03 11:39:43 +11:00
return read_file ( path , result , result_size , NULL ) ;
2019-05-20 20:55:20 +10:00
}
2023-02-03 11:39:44 +11:00
int read_debugfs_int ( const char * debugfs_file , int * result )
2018-05-21 20:43:57 +05:30
{
2023-02-03 11:39:43 +11:00
int err ;
char value [ 16 ] = { 0 } ;
2018-05-21 20:43:57 +05:30
2023-02-03 11:39:44 +11:00
err = read_debugfs_file ( debugfs_file , value , sizeof ( value ) - 1 ) ;
2023-02-03 11:39:43 +11:00
if ( err )
return err ;
2018-05-21 20:43:57 +05:30
2023-02-03 11:39:45 +11:00
return parse_int ( value , sizeof ( value ) , result , 10 ) ;
2018-05-21 20:43:57 +05:30
}
2023-02-03 11:39:44 +11:00
int write_debugfs_int ( const char * debugfs_file , int result )
2018-05-21 20:43:57 +05:30
{
char value [ 16 ] ;
snprintf ( value , 16 , " %d " , result ) ;
2023-02-03 11:39:44 +11:00
return write_debugfs_file ( debugfs_file , value , strlen ( value ) ) ;
2018-05-21 20:43:57 +05:30
}
static long perf_event_open ( struct perf_event_attr * hw_event , pid_t pid ,
int cpu , int group_fd , unsigned long flags )
{
return syscall ( __NR_perf_event_open , hw_event , pid , cpu ,
group_fd , flags ) ;
}
static void perf_event_attr_init ( struct perf_event_attr * event_attr ,
unsigned int type ,
unsigned long config )
{
memset ( event_attr , 0 , sizeof ( * event_attr ) ) ;
event_attr - > type = type ;
event_attr - > size = sizeof ( struct perf_event_attr ) ;
event_attr - > config = config ;
event_attr - > read_format = PERF_FORMAT_GROUP ;
event_attr - > disabled = 1 ;
event_attr - > exclude_kernel = 1 ;
event_attr - > exclude_hv = 1 ;
event_attr - > exclude_guest = 1 ;
}
int perf_event_open_counter ( unsigned int type ,
unsigned long config , int group_fd )
{
int fd ;
struct perf_event_attr event_attr ;
perf_event_attr_init ( & event_attr , type , config ) ;
fd = perf_event_open ( & event_attr , 0 , - 1 , group_fd , 0 ) ;
if ( fd < 0 )
perror ( " perf_event_open() failed " ) ;
return fd ;
}
int perf_event_enable ( int fd )
{
if ( ioctl ( fd , PERF_EVENT_IOC_ENABLE , PERF_IOC_FLAG_GROUP ) = = - 1 ) {
perror ( " error while enabling perf events " ) ;
return - 1 ;
}
return 0 ;
}
int perf_event_disable ( int fd )
{
if ( ioctl ( fd , PERF_EVENT_IOC_DISABLE , PERF_IOC_FLAG_GROUP ) = = - 1 ) {
perror ( " error disabling perf events " ) ;
return - 1 ;
}
return 0 ;
}
int perf_event_reset ( int fd )
{
if ( ioctl ( fd , PERF_EVENT_IOC_RESET , PERF_IOC_FLAG_GROUP ) = = - 1 ) {
perror ( " error resetting perf events " ) ;
return - 1 ;
}
return 0 ;
}
2020-06-04 18:26:09 +05:30
int using_hash_mmu ( bool * using_hash )
{
char line [ 128 ] ;
FILE * f ;
int rc ;
f = fopen ( " /proc/cpuinfo " , " r " ) ;
FAIL_IF ( ! f ) ;
rc = 0 ;
while ( fgets ( line , sizeof ( line ) , f ) ! = NULL ) {
2020-08-19 11:57:19 +10:00
if ( ! strcmp ( line , " MMU : Hash \n " ) | |
! strcmp ( line , " platform : Cell \n " ) | |
! strcmp ( line , " platform : PowerMac \n " ) ) {
2020-06-04 18:26:09 +05:30
* using_hash = true ;
goto out ;
}
if ( strcmp ( line , " MMU : Radix \n " ) = = 0 ) {
* using_hash = false ;
goto out ;
}
}
rc = - 1 ;
out :
fclose ( f ) ;
return rc ;
}
2023-06-19 17:36:27 +10:00
struct sigaction push_signal_handler ( int sig , void ( * fn ) ( int , siginfo_t * , void * ) )
{
struct sigaction sa ;
struct sigaction old_handler ;
sa . sa_sigaction = fn ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_flags = SA_SIGINFO ;
FAIL_IF_EXIT_MSG ( sigaction ( sig , & sa , & old_handler ) ,
" failed to push signal handler " ) ;
return old_handler ;
}
struct sigaction pop_signal_handler ( int sig , struct sigaction old_handler )
{
struct sigaction popped ;
FAIL_IF_EXIT_MSG ( sigaction ( sig , & old_handler , & popped ) ,
" failed to pop signal handler " ) ;
return popped ;
}