2012-08-23 16:31:13 +02:00
/*
* Extract CPU cache information and expose them via sysfs .
*
* Copyright IBM Corp . 2012
* Author ( s ) : Heiko Carstens < heiko . carstens @ de . ibm . com >
*/
2012-08-29 14:12:20 +02:00
# include <linux/seq_file.h>
2012-08-23 16:31:13 +02:00
# include <linux/cpu.h>
2015-01-08 07:41:52 +00:00
# include <linux/cacheinfo.h>
2012-08-23 16:31:13 +02:00
# include <asm/facility.h>
enum {
CACHE_SCOPE_NOTEXISTS ,
CACHE_SCOPE_PRIVATE ,
CACHE_SCOPE_SHARED ,
CACHE_SCOPE_RESERVED ,
} ;
enum {
2015-01-08 07:41:52 +00:00
CTYPE_SEPARATE ,
CTYPE_DATA ,
CTYPE_INSTRUCTION ,
CTYPE_UNIFIED ,
2012-08-23 16:31:13 +02:00
} ;
enum {
EXTRACT_TOPOLOGY ,
EXTRACT_LINE_SIZE ,
EXTRACT_SIZE ,
EXTRACT_ASSOCIATIVITY ,
} ;
enum {
CACHE_TI_UNIFIED = 0 ,
2012-10-18 13:13:41 +02:00
CACHE_TI_DATA = 0 ,
CACHE_TI_INSTRUCTION ,
2012-08-23 16:31:13 +02:00
} ;
struct cache_info {
unsigned char : 4 ;
unsigned char scope : 2 ;
unsigned char type : 2 ;
} ;
# define CACHE_MAX_LEVEL 8
union cache_topology {
struct cache_info ci [ CACHE_MAX_LEVEL ] ;
unsigned long long raw ;
} ;
static const char * const cache_type_string [ ] = {
2015-01-08 07:41:52 +00:00
" " ,
2012-08-23 16:31:13 +02:00
" Instruction " ,
2015-01-08 07:41:52 +00:00
" Data " ,
" " ,
2012-08-23 16:31:13 +02:00
" Unified " ,
} ;
2015-01-08 07:41:52 +00:00
static const enum cache_type cache_type_map [ ] = {
[ CTYPE_SEPARATE ] = CACHE_TYPE_SEPARATE ,
[ CTYPE_DATA ] = CACHE_TYPE_DATA ,
[ CTYPE_INSTRUCTION ] = CACHE_TYPE_INST ,
[ CTYPE_UNIFIED ] = CACHE_TYPE_UNIFIED ,
} ;
2012-08-23 16:31:13 +02:00
2012-08-29 14:12:20 +02:00
void show_cacheinfo ( struct seq_file * m )
{
2015-02-09 12:54:16 +01:00
struct cpu_cacheinfo * this_cpu_ci ;
2015-01-08 07:41:52 +00:00
struct cacheinfo * cache ;
2015-02-09 12:54:16 +01:00
int idx ;
2012-08-29 14:12:20 +02:00
2015-03-18 13:22:00 +01:00
if ( ! test_facility ( 34 ) )
return ;
2015-02-09 12:54:16 +01:00
get_online_cpus ( ) ;
this_cpu_ci = get_cpu_cacheinfo ( cpumask_any ( cpu_online_mask ) ) ;
2015-01-08 07:41:52 +00:00
for ( idx = 0 ; idx < this_cpu_ci - > num_leaves ; idx + + ) {
cache = this_cpu_ci - > info_list + idx ;
seq_printf ( m , " cache%-11d: " , idx ) ;
2012-08-29 14:12:20 +02:00
seq_printf ( m , " level=%d " , cache - > level ) ;
seq_printf ( m , " type=%s " , cache_type_string [ cache - > type ] ) ;
2015-01-08 07:41:52 +00:00
seq_printf ( m , " scope=%s " ,
cache - > disable_sysfs ? " Shared " : " Private " ) ;
seq_printf ( m , " size=%dK " , cache - > size > > 10 ) ;
seq_printf ( m , " line_size=%u " , cache - > coherency_line_size ) ;
seq_printf ( m , " associativity=%d " , cache - > ways_of_associativity ) ;
2012-08-29 14:12:20 +02:00
seq_puts ( m , " \n " ) ;
}
2015-02-09 12:54:16 +01:00
put_online_cpus ( ) ;
2012-08-29 14:12:20 +02:00
}
2015-01-08 07:41:52 +00:00
static inline enum cache_type get_cache_type ( struct cache_info * ci , int level )
{
if ( level > = CACHE_MAX_LEVEL )
return CACHE_TYPE_NOCACHE ;
ci + = level ;
if ( ci - > scope ! = CACHE_SCOPE_SHARED & & ci - > scope ! = CACHE_SCOPE_PRIVATE )
return CACHE_TYPE_NOCACHE ;
return cache_type_map [ ci - > type ] ;
}
2012-08-23 16:31:13 +02:00
static inline unsigned long ecag ( int ai , int li , int ti )
{
unsigned long cmd , val ;
cmd = ai < < 4 | li < < 1 | ti ;
asm volatile ( " .insn rsy,0xeb000000004c,%0,0,0(%1) " /* ecag */
: " =d " ( val ) : " a " ( cmd ) ) ;
return val ;
}
2015-01-08 07:41:52 +00:00
static void ci_leaf_init ( struct cacheinfo * this_leaf , int private ,
2015-02-11 14:50:10 +01:00
enum cache_type type , unsigned int level , int cpu )
2012-08-23 16:31:13 +02:00
{
2015-01-08 07:41:52 +00:00
int ti , num_sets ;
2012-08-23 16:31:13 +02:00
2015-01-08 07:41:52 +00:00
if ( type = = CACHE_TYPE_INST )
2012-10-18 13:13:41 +02:00
ti = CACHE_TI_INSTRUCTION ;
else
ti = CACHE_TI_UNIFIED ;
2015-01-08 07:41:52 +00:00
this_leaf - > level = level + 1 ;
this_leaf - > type = type ;
this_leaf - > coherency_line_size = ecag ( EXTRACT_LINE_SIZE , level , ti ) ;
2015-02-11 14:57:46 +01:00
this_leaf - > ways_of_associativity = ecag ( EXTRACT_ASSOCIATIVITY , level , ti ) ;
2015-01-08 07:41:52 +00:00
this_leaf - > size = ecag ( EXTRACT_SIZE , level , ti ) ;
num_sets = this_leaf - > size / this_leaf - > coherency_line_size ;
num_sets / = this_leaf - > ways_of_associativity ;
this_leaf - > number_of_sets = num_sets ;
cpumask_set_cpu ( cpu , & this_leaf - > shared_cpu_map ) ;
if ( ! private )
this_leaf - > disable_sysfs = true ;
2012-08-23 16:31:13 +02:00
}
2015-01-08 07:41:52 +00:00
int init_cache_level ( unsigned int cpu )
2012-08-23 16:31:13 +02:00
{
2015-01-08 07:41:52 +00:00
struct cpu_cacheinfo * this_cpu_ci = get_cpu_cacheinfo ( cpu ) ;
unsigned int level = 0 , leaves = 0 ;
union cache_topology ct ;
enum cache_type ctype ;
2012-08-23 16:31:13 +02:00
2015-01-08 07:41:52 +00:00
if ( ! this_cpu_ci )
return - EINVAL ;
ct . raw = ecag ( EXTRACT_TOPOLOGY , 0 , 0 ) ;
do {
ctype = get_cache_type ( & ct . ci [ 0 ] , level ) ;
if ( ctype = = CACHE_TYPE_NOCACHE )
2012-08-29 14:12:20 +02:00
break ;
2015-01-08 07:41:52 +00:00
/* Separate instruction and data caches */
leaves + = ( ctype = = CACHE_TYPE_SEPARATE ) ? 2 : 1 ;
} while ( + + level < CACHE_MAX_LEVEL ) ;
this_cpu_ci - > num_levels = level ;
this_cpu_ci - > num_leaves = leaves ;
return 0 ;
2012-08-23 16:31:13 +02:00
}
2015-01-08 07:41:52 +00:00
int populate_cache_leaves ( unsigned int cpu )
2012-08-23 16:31:13 +02:00
{
2015-02-11 14:57:46 +01:00
struct cpu_cacheinfo * this_cpu_ci = get_cpu_cacheinfo ( cpu ) ;
struct cacheinfo * this_leaf = this_cpu_ci - > info_list ;
2015-01-08 07:41:52 +00:00
unsigned int level , idx , pvt ;
union cache_topology ct ;
enum cache_type ctype ;
2012-08-23 16:31:13 +02:00
2015-03-18 13:22:00 +01:00
if ( ! test_facility ( 34 ) )
return - EOPNOTSUPP ;
2015-01-08 07:41:52 +00:00
ct . raw = ecag ( EXTRACT_TOPOLOGY , 0 , 0 ) ;
for ( idx = 0 , level = 0 ; level < this_cpu_ci - > num_levels & &
idx < this_cpu_ci - > num_leaves ; idx + + , level + + ) {
if ( ! this_leaf )
return - EINVAL ;
pvt = ( ct . ci [ level ] . scope = = CACHE_SCOPE_PRIVATE ) ? 1 : 0 ;
ctype = get_cache_type ( & ct . ci [ 0 ] , level ) ;
if ( ctype = = CACHE_TYPE_SEPARATE ) {
2015-02-11 14:50:10 +01:00
ci_leaf_init ( this_leaf + + , pvt , CACHE_TYPE_DATA , level , cpu ) ;
ci_leaf_init ( this_leaf + + , pvt , CACHE_TYPE_INST , level , cpu ) ;
2015-01-08 07:41:52 +00:00
} else {
2015-02-11 14:50:10 +01:00
ci_leaf_init ( this_leaf + + , pvt , ctype , level , cpu ) ;
2015-01-08 07:41:52 +00:00
}
2012-08-23 16:31:13 +02:00
}
return 0 ;
}