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
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-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 )
{
2016-04-14 12:35:22 +02:00
return __ecag ( ECAG_CACHE_ATTRIBUTE , ai < < 4 | li < < 1 | ti ) ;
2012-08-23 16:31:13 +02:00
}
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
s390/cachinfo: add missing facility check to init_cache_level()
Stephen Powell reported the following crash on a z890 machine:
Kernel BUG at 00000000001219d0 [verbose debug info unavailable]
illegal operation: 0001 ilc:3 [#1] SMP
Krnl PSW : 0704e00180000000 00000000001219d0 (init_cache_level+0x38/0xe0)
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 EA:3
Krnl Code: 00000000001219c2: a7840056 brc 8,121a6e
00000000001219c6: a7190000 lghi %r1,0
#00000000001219ca: eb101000004c ecag %r1,%r0,0(%r1)
>00000000001219d0: a7390000 lghi %r3,0
00000000001219d4: e310f0a00024 stg %r1,160(%r15)
00000000001219da: a7080000 lhi %r0,0
00000000001219de: a7b9f000 lghi %r11,-4096
00000000001219e2: c0a0002899d9 larl %r10,634d94
Call Trace:
[<0000000000478ee2>] detect_cache_attributes+0x2a/0x2b8
[<000000000097c9b0>] cacheinfo_sysfs_init+0x60/0xc8
[<00000000001001c0>] do_one_initcall+0x98/0x1c8
[<000000000094fdc2>] kernel_init_freeable+0x212/0x2d8
[<000000000062352e>] kernel_init+0x26/0x118
[<000000000062fd2e>] kernel_thread_starter+0x6/0xc
The illegal operation was executed because of a missing facility check,
which should have made sure that the ECAG execution would only be executed
on machines which have the general-instructions-extension facility
installed.
Reported-and-tested-by: Stephen Powell <zlinuxman@wowway.com>
Cc: stable@vger.kernel.org # v4.0+
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2015-07-27 09:53:49 +02:00
if ( ! test_facility ( 34 ) )
return - EOPNOTSUPP ;
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 ;
}