xref: /linux/arch/s390/kernel/cache.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2881730adSHeiko Carstens /*
3881730adSHeiko Carstens  * Extract CPU cache information and expose them via sysfs.
4881730adSHeiko Carstens  *
5881730adSHeiko Carstens  *    Copyright IBM Corp. 2012
6881730adSHeiko Carstens  */
7881730adSHeiko Carstens 
86668022cSHeiko Carstens #include <linux/seq_file.h>
9881730adSHeiko Carstens #include <linux/cpu.h>
10d97d929fSSudeep Holla #include <linux/cacheinfo.h>
11881730adSHeiko Carstens #include <asm/facility.h>
12881730adSHeiko Carstens 
13881730adSHeiko Carstens enum {
14881730adSHeiko Carstens 	CACHE_SCOPE_NOTEXISTS,
15881730adSHeiko Carstens 	CACHE_SCOPE_PRIVATE,
16881730adSHeiko Carstens 	CACHE_SCOPE_SHARED,
17881730adSHeiko Carstens 	CACHE_SCOPE_RESERVED,
18881730adSHeiko Carstens };
19881730adSHeiko Carstens 
20881730adSHeiko Carstens enum {
21d97d929fSSudeep Holla 	CTYPE_SEPARATE,
22d97d929fSSudeep Holla 	CTYPE_DATA,
23d97d929fSSudeep Holla 	CTYPE_INSTRUCTION,
24d97d929fSSudeep Holla 	CTYPE_UNIFIED,
25881730adSHeiko Carstens };
26881730adSHeiko Carstens 
27881730adSHeiko Carstens enum {
28881730adSHeiko Carstens 	EXTRACT_TOPOLOGY,
29881730adSHeiko Carstens 	EXTRACT_LINE_SIZE,
30881730adSHeiko Carstens 	EXTRACT_SIZE,
31881730adSHeiko Carstens 	EXTRACT_ASSOCIATIVITY,
32881730adSHeiko Carstens };
33881730adSHeiko Carstens 
34881730adSHeiko Carstens enum {
35881730adSHeiko Carstens 	CACHE_TI_UNIFIED = 0,
36d18f99c2SHeiko Carstens 	CACHE_TI_DATA = 0,
37d18f99c2SHeiko Carstens 	CACHE_TI_INSTRUCTION,
38881730adSHeiko Carstens };
39881730adSHeiko Carstens 
40881730adSHeiko Carstens struct cache_info {
41881730adSHeiko Carstens 	unsigned char	    : 4;
42881730adSHeiko Carstens 	unsigned char scope : 2;
43881730adSHeiko Carstens 	unsigned char type  : 2;
44881730adSHeiko Carstens };
45881730adSHeiko Carstens 
46881730adSHeiko Carstens #define CACHE_MAX_LEVEL 8
47881730adSHeiko Carstens union cache_topology {
48881730adSHeiko Carstens 	struct cache_info ci[CACHE_MAX_LEVEL];
497a725b77SHeiko Carstens 	unsigned long raw;
50881730adSHeiko Carstens };
51881730adSHeiko Carstens 
52881730adSHeiko Carstens static const char * const cache_type_string[] = {
53d97d929fSSudeep Holla 	"",
54881730adSHeiko Carstens 	"Instruction",
55d97d929fSSudeep Holla 	"Data",
56d97d929fSSudeep Holla 	"",
57881730adSHeiko Carstens 	"Unified",
58881730adSHeiko Carstens };
59881730adSHeiko Carstens 
60d97d929fSSudeep Holla static const enum cache_type cache_type_map[] = {
61d97d929fSSudeep Holla 	[CTYPE_SEPARATE] = CACHE_TYPE_SEPARATE,
62d97d929fSSudeep Holla 	[CTYPE_DATA] = CACHE_TYPE_DATA,
63d97d929fSSudeep Holla 	[CTYPE_INSTRUCTION] = CACHE_TYPE_INST,
64d97d929fSSudeep Holla 	[CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED,
65d97d929fSSudeep Holla };
66881730adSHeiko Carstens 
show_cacheinfo(struct seq_file * m)676668022cSHeiko Carstens void show_cacheinfo(struct seq_file *m)
686668022cSHeiko Carstens {
6945cce4ccSHeiko Carstens 	struct cpu_cacheinfo *this_cpu_ci;
70d97d929fSSudeep Holla 	struct cacheinfo *cache;
7145cce4ccSHeiko Carstens 	int idx;
726668022cSHeiko Carstens 
7345cce4ccSHeiko Carstens 	this_cpu_ci = get_cpu_cacheinfo(cpumask_any(cpu_online_mask));
74d97d929fSSudeep Holla 	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) {
75d97d929fSSudeep Holla 		cache = this_cpu_ci->info_list + idx;
76d97d929fSSudeep Holla 		seq_printf(m, "cache%-11d: ", idx);
776668022cSHeiko Carstens 		seq_printf(m, "level=%d ", cache->level);
786668022cSHeiko Carstens 		seq_printf(m, "type=%s ", cache_type_string[cache->type]);
79d97d929fSSudeep Holla 		seq_printf(m, "scope=%s ",
80d97d929fSSudeep Holla 			   cache->disable_sysfs ? "Shared" : "Private");
81d97d929fSSudeep Holla 		seq_printf(m, "size=%dK ", cache->size >> 10);
82d97d929fSSudeep Holla 		seq_printf(m, "line_size=%u ", cache->coherency_line_size);
83d97d929fSSudeep Holla 		seq_printf(m, "associativity=%d", cache->ways_of_associativity);
846668022cSHeiko Carstens 		seq_puts(m, "\n");
856668022cSHeiko Carstens 	}
866668022cSHeiko Carstens }
876668022cSHeiko Carstens 
get_cache_type(struct cache_info * ci,int level)88d97d929fSSudeep Holla static inline enum cache_type get_cache_type(struct cache_info *ci, int level)
89d97d929fSSudeep Holla {
90d97d929fSSudeep Holla 	if (level >= CACHE_MAX_LEVEL)
91d97d929fSSudeep Holla 		return CACHE_TYPE_NOCACHE;
92d97d929fSSudeep Holla 	ci += level;
93d97d929fSSudeep Holla 	if (ci->scope != CACHE_SCOPE_SHARED && ci->scope != CACHE_SCOPE_PRIVATE)
94d97d929fSSudeep Holla 		return CACHE_TYPE_NOCACHE;
95d97d929fSSudeep Holla 	return cache_type_map[ci->type];
96d97d929fSSudeep Holla }
97d97d929fSSudeep Holla 
ecag(int ai,int li,int ti)98881730adSHeiko Carstens static inline unsigned long ecag(int ai, int li, int ti)
99881730adSHeiko Carstens {
100097a116cSHeiko Carstens 	return __ecag(ECAG_CACHE_ATTRIBUTE, ai << 4 | li << 1 | ti);
101881730adSHeiko Carstens }
102881730adSHeiko Carstens 
ci_leaf_init(struct cacheinfo * this_leaf,int private,enum cache_type type,unsigned int level,int cpu)103d97d929fSSudeep Holla static void ci_leaf_init(struct cacheinfo *this_leaf, int private,
1044fd4f1c7SHeiko Carstens 			 enum cache_type type, unsigned int level, int cpu)
105881730adSHeiko Carstens {
106d97d929fSSudeep Holla 	int ti, num_sets;
107881730adSHeiko Carstens 
108d97d929fSSudeep Holla 	if (type == CACHE_TYPE_INST)
109d18f99c2SHeiko Carstens 		ti = CACHE_TI_INSTRUCTION;
110d18f99c2SHeiko Carstens 	else
111d18f99c2SHeiko Carstens 		ti = CACHE_TI_UNIFIED;
112d97d929fSSudeep Holla 	this_leaf->level = level + 1;
113d97d929fSSudeep Holla 	this_leaf->type = type;
114d97d929fSSudeep Holla 	this_leaf->coherency_line_size = ecag(EXTRACT_LINE_SIZE, level, ti);
115f4dce5c9SHeiko Carstens 	this_leaf->ways_of_associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti);
116d97d929fSSudeep Holla 	this_leaf->size = ecag(EXTRACT_SIZE, level, ti);
117d97d929fSSudeep Holla 	num_sets = this_leaf->size / this_leaf->coherency_line_size;
118d97d929fSSudeep Holla 	num_sets /= this_leaf->ways_of_associativity;
119d97d929fSSudeep Holla 	this_leaf->number_of_sets = num_sets;
120d97d929fSSudeep Holla 	cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
121d97d929fSSudeep Holla 	if (!private)
122d97d929fSSudeep Holla 		this_leaf->disable_sysfs = true;
123881730adSHeiko Carstens }
124881730adSHeiko Carstens 
init_cache_level(unsigned int cpu)125d97d929fSSudeep Holla int init_cache_level(unsigned int cpu)
126881730adSHeiko Carstens {
127d97d929fSSudeep Holla 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
128d97d929fSSudeep Holla 	unsigned int level = 0, leaves = 0;
129881730adSHeiko Carstens 	union cache_topology ct;
130d97d929fSSudeep Holla 	enum cache_type ctype;
131d97d929fSSudeep Holla 
132d97d929fSSudeep Holla 	if (!this_cpu_ci)
133d97d929fSSudeep Holla 		return -EINVAL;
134881730adSHeiko Carstens 	ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
135d97d929fSSudeep Holla 	do {
136d97d929fSSudeep Holla 		ctype = get_cache_type(&ct.ci[0], level);
137d97d929fSSudeep Holla 		if (ctype == CACHE_TYPE_NOCACHE)
1386668022cSHeiko Carstens 			break;
139d97d929fSSudeep Holla 		/* Separate instruction and data caches */
140d97d929fSSudeep Holla 		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
141d97d929fSSudeep Holla 	} while (++level < CACHE_MAX_LEVEL);
142d97d929fSSudeep Holla 	this_cpu_ci->num_levels = level;
143d97d929fSSudeep Holla 	this_cpu_ci->num_leaves = leaves;
144d97d929fSSudeep Holla 	return 0;
145881730adSHeiko Carstens }
146d97d929fSSudeep Holla 
populate_cache_leaves(unsigned int cpu)147d97d929fSSudeep Holla int populate_cache_leaves(unsigned int cpu)
148d97d929fSSudeep Holla {
149f4dce5c9SHeiko Carstens 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
150f4dce5c9SHeiko Carstens 	struct cacheinfo *this_leaf = this_cpu_ci->info_list;
151d97d929fSSudeep Holla 	unsigned int level, idx, pvt;
152d97d929fSSudeep Holla 	union cache_topology ct;
153d97d929fSSudeep Holla 	enum cache_type ctype;
154d97d929fSSudeep Holla 
155d97d929fSSudeep Holla 	ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
156d97d929fSSudeep Holla 	for (idx = 0, level = 0; level < this_cpu_ci->num_levels &&
157d97d929fSSudeep Holla 	     idx < this_cpu_ci->num_leaves; idx++, level++) {
158d97d929fSSudeep Holla 		if (!this_leaf)
159d97d929fSSudeep Holla 			return -EINVAL;
160d97d929fSSudeep Holla 		pvt = (ct.ci[level].scope == CACHE_SCOPE_PRIVATE) ? 1 : 0;
161d97d929fSSudeep Holla 		ctype = get_cache_type(&ct.ci[0], level);
162d97d929fSSudeep Holla 		if (ctype == CACHE_TYPE_SEPARATE) {
1634fd4f1c7SHeiko Carstens 			ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_DATA, level, cpu);
1644fd4f1c7SHeiko Carstens 			ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_INST, level, cpu);
165881730adSHeiko Carstens 		} else {
1664fd4f1c7SHeiko Carstens 			ci_leaf_init(this_leaf++, pvt, ctype, level, cpu);
167881730adSHeiko Carstens 		}
168881730adSHeiko Carstens 	}
169*cb0cd4eeSHeiko Carstens 	this_cpu_ci->cpu_map_populated = true;
170881730adSHeiko Carstens 	return 0;
171881730adSHeiko Carstens }
172