2019-02-19 10:58:13 +01:00
// SPDX-License-Identifier: GPL-2.0
# include <sys/param.h>
2019-06-04 15:50:41 -07:00
# include <sys/utsname.h>
2019-02-19 10:58:14 +01:00
# include <inttypes.h>
2019-07-04 11:21:24 -03:00
# include <stdlib.h>
2019-08-22 10:48:31 -03:00
# include <string.h>
2019-02-19 10:58:15 +01:00
# include <api/fs/fs.h>
2019-07-04 11:32:27 -03:00
# include <linux/zalloc.h>
2019-07-21 13:24:30 +02:00
# include <perf/cpumap.h>
2019-02-19 10:58:13 +01:00
# include "cputopo.h"
# include "cpumap.h"
2019-08-22 10:48:31 -03:00
# include "debug.h"
2019-02-19 10:58:14 +01:00
# include "env.h"
2021-05-14 20:29:47 +08:00
# include "pmu-hybrid.h"
2019-02-19 10:58:13 +01:00
# define CORE_SIB_FMT \
2019-02-19 10:58:15 +01:00
" %s/devices/system/cpu/cpu%d/topology/core_siblings_list "
2019-06-04 15:50:41 -07:00
# define DIE_SIB_FMT \
" %s/devices/system/cpu/cpu%d/topology/die_cpus_list "
2019-02-19 10:58:13 +01:00
# define THRD_SIB_FMT \
2019-02-19 10:58:15 +01:00
" %s/devices/system/cpu/cpu%d/topology/thread_siblings_list "
2019-06-04 15:50:44 -07:00
# define THRD_SIB_FMT_NEW \
" %s/devices/system/cpu/cpu%d/topology/core_cpus_list "
2019-02-19 10:58:15 +01:00
# define NODE_ONLINE_FMT \
" %s/devices/system/node/online "
# define NODE_MEMINFO_FMT \
" %s/devices/system/node/node%d/meminfo "
# define NODE_CPULIST_FMT \
" %s/devices/system/node/node%d/cpulist "
2019-02-19 10:58:13 +01:00
static int build_cpu_topology ( struct cpu_topology * tp , int cpu )
{
FILE * fp ;
char filename [ MAXPATHLEN ] ;
char * buf = NULL , * p ;
size_t len = 0 ;
ssize_t sret ;
u32 i = 0 ;
int ret = - 1 ;
2019-02-19 10:58:15 +01:00
scnprintf ( filename , MAXPATHLEN , CORE_SIB_FMT ,
sysfs__mountpoint ( ) , cpu ) ;
2019-02-19 10:58:13 +01:00
fp = fopen ( filename , " r " ) ;
if ( ! fp )
2019-06-04 15:50:41 -07:00
goto try_dies ;
2019-02-19 10:58:13 +01:00
sret = getline ( & buf , & len , fp ) ;
fclose ( fp ) ;
if ( sret < = 0 )
2019-06-04 15:50:41 -07:00
goto try_dies ;
2019-02-19 10:58:13 +01:00
p = strchr ( buf , ' \n ' ) ;
if ( p )
* p = ' \0 ' ;
for ( i = 0 ; i < tp - > core_sib ; i + + ) {
if ( ! strcmp ( buf , tp - > core_siblings [ i ] ) )
break ;
}
if ( i = = tp - > core_sib ) {
tp - > core_siblings [ i ] = buf ;
tp - > core_sib + + ;
buf = NULL ;
len = 0 ;
}
ret = 0 ;
2019-06-04 15:50:41 -07:00
try_dies :
if ( ! tp - > die_siblings )
goto try_threads ;
scnprintf ( filename , MAXPATHLEN , DIE_SIB_FMT ,
sysfs__mountpoint ( ) , cpu ) ;
fp = fopen ( filename , " r " ) ;
if ( ! fp )
goto try_threads ;
sret = getline ( & buf , & len , fp ) ;
fclose ( fp ) ;
if ( sret < = 0 )
goto try_threads ;
p = strchr ( buf , ' \n ' ) ;
if ( p )
* p = ' \0 ' ;
for ( i = 0 ; i < tp - > die_sib ; i + + ) {
if ( ! strcmp ( buf , tp - > die_siblings [ i ] ) )
break ;
}
if ( i = = tp - > die_sib ) {
tp - > die_siblings [ i ] = buf ;
tp - > die_sib + + ;
buf = NULL ;
len = 0 ;
}
ret = 0 ;
2019-02-19 10:58:13 +01:00
try_threads :
2019-06-04 15:50:44 -07:00
scnprintf ( filename , MAXPATHLEN , THRD_SIB_FMT_NEW ,
2019-02-19 10:58:15 +01:00
sysfs__mountpoint ( ) , cpu ) ;
2019-06-04 15:50:44 -07:00
if ( access ( filename , F_OK ) = = - 1 ) {
scnprintf ( filename , MAXPATHLEN , THRD_SIB_FMT ,
sysfs__mountpoint ( ) , cpu ) ;
}
2019-02-19 10:58:13 +01:00
fp = fopen ( filename , " r " ) ;
if ( ! fp )
goto done ;
if ( getline ( & buf , & len , fp ) < = 0 )
goto done ;
p = strchr ( buf , ' \n ' ) ;
if ( p )
* p = ' \0 ' ;
for ( i = 0 ; i < tp - > thread_sib ; i + + ) {
if ( ! strcmp ( buf , tp - > thread_siblings [ i ] ) )
break ;
}
if ( i = = tp - > thread_sib ) {
tp - > thread_siblings [ i ] = buf ;
tp - > thread_sib + + ;
buf = NULL ;
}
ret = 0 ;
done :
if ( fp )
fclose ( fp ) ;
free ( buf ) ;
return ret ;
}
void cpu_topology__delete ( struct cpu_topology * tp )
{
u32 i ;
if ( ! tp )
return ;
for ( i = 0 ; i < tp - > core_sib ; i + + )
zfree ( & tp - > core_siblings [ i ] ) ;
2019-06-04 15:50:41 -07:00
if ( tp - > die_sib ) {
for ( i = 0 ; i < tp - > die_sib ; i + + )
zfree ( & tp - > die_siblings [ i ] ) ;
}
2019-02-19 10:58:13 +01:00
for ( i = 0 ; i < tp - > thread_sib ; i + + )
zfree ( & tp - > thread_siblings [ i ] ) ;
free ( tp ) ;
}
2019-06-04 15:50:41 -07:00
static bool has_die_topology ( void )
{
char filename [ MAXPATHLEN ] ;
struct utsname uts ;
if ( uname ( & uts ) < 0 )
return false ;
if ( strncmp ( uts . machine , " x86_64 " , 6 ) )
return false ;
scnprintf ( filename , MAXPATHLEN , DIE_SIB_FMT ,
sysfs__mountpoint ( ) , 0 ) ;
if ( access ( filename , F_OK ) = = - 1 )
return false ;
return true ;
}
2019-02-19 10:58:13 +01:00
struct cpu_topology * cpu_topology__new ( void )
{
struct cpu_topology * tp = NULL ;
void * addr ;
2019-06-04 15:50:41 -07:00
u32 nr , i , nr_addr ;
2019-02-19 10:58:13 +01:00
size_t sz ;
long ncpus ;
int ret = - 1 ;
2019-07-21 13:23:49 +02:00
struct perf_cpu_map * map ;
2019-06-04 15:50:41 -07:00
bool has_die = has_die_topology ( ) ;
2019-02-19 10:58:13 +01:00
ncpus = cpu__max_present_cpu ( ) ;
/* build online CPU map */
2019-07-21 13:24:30 +02:00
map = perf_cpu_map__new ( NULL ) ;
2019-02-19 10:58:13 +01:00
if ( map = = NULL ) {
pr_debug ( " failed to get system cpumap \n " ) ;
return NULL ;
}
nr = ( u32 ) ( ncpus & UINT_MAX ) ;
sz = nr * sizeof ( char * ) ;
2019-06-04 15:50:41 -07:00
if ( has_die )
nr_addr = 3 ;
else
nr_addr = 2 ;
addr = calloc ( 1 , sizeof ( * tp ) + nr_addr * sz ) ;
2019-02-19 10:58:13 +01:00
if ( ! addr )
goto out_free ;
tp = addr ;
addr + = sizeof ( * tp ) ;
tp - > core_siblings = addr ;
addr + = sz ;
2019-06-04 15:50:41 -07:00
if ( has_die ) {
tp - > die_siblings = addr ;
addr + = sz ;
}
2019-02-19 10:58:13 +01:00
tp - > thread_siblings = addr ;
for ( i = 0 ; i < nr ; i + + ) {
if ( ! cpu_map__has ( map , i ) )
continue ;
ret = build_cpu_topology ( tp , i ) ;
if ( ret < 0 )
break ;
}
out_free :
2019-07-21 13:24:17 +02:00
perf_cpu_map__put ( map ) ;
2019-02-19 10:58:13 +01:00
if ( ret ) {
cpu_topology__delete ( tp ) ;
tp = NULL ;
}
return tp ;
}
2019-02-19 10:58:14 +01:00
static int load_numa_node ( struct numa_topology_node * node , int nr )
{
char str [ MAXPATHLEN ] ;
char field [ 32 ] ;
char * buf = NULL , * p ;
size_t len = 0 ;
int ret = - 1 ;
FILE * fp ;
u64 mem ;
node - > node = ( u32 ) nr ;
2019-02-19 10:58:15 +01:00
scnprintf ( str , MAXPATHLEN , NODE_MEMINFO_FMT ,
sysfs__mountpoint ( ) , nr ) ;
2019-02-19 10:58:14 +01:00
fp = fopen ( str , " r " ) ;
if ( ! fp )
return - 1 ;
while ( getline ( & buf , & len , fp ) > 0 ) {
/* skip over invalid lines */
if ( ! strchr ( buf , ' : ' ) )
continue ;
if ( sscanf ( buf , " %*s %*d %31s % " PRIu64 , field , & mem ) ! = 2 )
goto err ;
if ( ! strcmp ( field , " MemTotal: " ) )
node - > mem_total = mem ;
if ( ! strcmp ( field , " MemFree: " ) )
node - > mem_free = mem ;
if ( node - > mem_total & & node - > mem_free )
break ;
}
fclose ( fp ) ;
fp = NULL ;
2019-02-19 10:58:15 +01:00
scnprintf ( str , MAXPATHLEN , NODE_CPULIST_FMT ,
sysfs__mountpoint ( ) , nr ) ;
2019-02-19 10:58:14 +01:00
fp = fopen ( str , " r " ) ;
if ( ! fp )
return - 1 ;
if ( getline ( & buf , & len , fp ) < = 0 )
goto err ;
p = strchr ( buf , ' \n ' ) ;
if ( p )
* p = ' \0 ' ;
node - > cpus = buf ;
fclose ( fp ) ;
return 0 ;
err :
free ( buf ) ;
if ( fp )
fclose ( fp ) ;
return ret ;
}
struct numa_topology * numa_topology__new ( void )
{
2019-07-21 13:23:49 +02:00
struct perf_cpu_map * node_map = NULL ;
2019-02-19 10:58:14 +01:00
struct numa_topology * tp = NULL ;
2019-02-19 10:58:15 +01:00
char path [ MAXPATHLEN ] ;
2019-02-19 10:58:14 +01:00
char * buf = NULL ;
size_t len = 0 ;
u32 nr , i ;
FILE * fp ;
char * c ;
2019-02-19 10:58:15 +01:00
scnprintf ( path , MAXPATHLEN , NODE_ONLINE_FMT ,
sysfs__mountpoint ( ) ) ;
fp = fopen ( path , " r " ) ;
2019-02-19 10:58:14 +01:00
if ( ! fp )
return NULL ;
if ( getline ( & buf , & len , fp ) < = 0 )
goto out ;
c = strchr ( buf , ' \n ' ) ;
if ( c )
* c = ' \0 ' ;
2019-07-21 13:24:30 +02:00
node_map = perf_cpu_map__new ( buf ) ;
2019-02-19 10:58:14 +01:00
if ( ! node_map )
goto out ;
nr = ( u32 ) node_map - > nr ;
tp = zalloc ( sizeof ( * tp ) + sizeof ( tp - > nodes [ 0 ] ) * nr ) ;
if ( ! tp )
goto out ;
tp - > nr = nr ;
for ( i = 0 ; i < nr ; i + + ) {
if ( load_numa_node ( & tp - > nodes [ i ] , node_map - > map [ i ] ) ) {
numa_topology__delete ( tp ) ;
tp = NULL ;
break ;
}
}
out :
free ( buf ) ;
fclose ( fp ) ;
2019-07-21 13:24:17 +02:00
perf_cpu_map__put ( node_map ) ;
2019-02-19 10:58:14 +01:00
return tp ;
}
void numa_topology__delete ( struct numa_topology * tp )
{
u32 i ;
for ( i = 0 ; i < tp - > nr ; i + + )
2019-07-04 12:06:20 -03:00
zfree ( & tp - > nodes [ i ] . cpus ) ;
2019-02-19 10:58:14 +01:00
free ( tp ) ;
}
2021-05-14 20:29:47 +08:00
static int load_hybrid_node ( struct hybrid_topology_node * node ,
struct perf_pmu * pmu )
{
const char * sysfs ;
char path [ PATH_MAX ] ;
char * buf = NULL , * p ;
FILE * fp ;
size_t len = 0 ;
node - > pmu_name = strdup ( pmu - > name ) ;
if ( ! node - > pmu_name )
return - 1 ;
sysfs = sysfs__mountpoint ( ) ;
if ( ! sysfs )
goto err ;
snprintf ( path , PATH_MAX , CPUS_TEMPLATE_CPU , sysfs , pmu - > name ) ;
fp = fopen ( path , " r " ) ;
if ( ! fp )
goto err ;
if ( getline ( & buf , & len , fp ) < = 0 ) {
fclose ( fp ) ;
goto err ;
}
p = strchr ( buf , ' \n ' ) ;
if ( p )
* p = ' \0 ' ;
fclose ( fp ) ;
node - > cpus = buf ;
return 0 ;
err :
zfree ( & node - > pmu_name ) ;
free ( buf ) ;
return - 1 ;
}
struct hybrid_topology * hybrid_topology__new ( void )
{
struct perf_pmu * pmu ;
struct hybrid_topology * tp = NULL ;
u32 nr , i = 0 ;
nr = perf_pmu__hybrid_pmu_num ( ) ;
if ( nr = = 0 )
return NULL ;
tp = zalloc ( sizeof ( * tp ) + sizeof ( tp - > nodes [ 0 ] ) * nr ) ;
if ( ! tp )
return NULL ;
tp - > nr = nr ;
perf_pmu__for_each_hybrid_pmu ( pmu ) {
if ( load_hybrid_node ( & tp - > nodes [ i ] , pmu ) ) {
hybrid_topology__delete ( tp ) ;
return NULL ;
}
i + + ;
}
return tp ;
}
void hybrid_topology__delete ( struct hybrid_topology * tp )
{
u32 i ;
for ( i = 0 ; i < tp - > nr ; i + + ) {
zfree ( & tp - > nodes [ i ] . pmu_name ) ;
zfree ( & tp - > nodes [ i ] . cpus ) ;
}
free ( tp ) ;
}