1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
// SPDX-License-Identifier: GPL-2.0
/*
* Extract CPU cache information and expose them via sysfs.
*
* Copyright IBM Corp. 2012
*/
#include <linux/seq_file.h>
#include <linux/cpu.h>
#include <linux/cacheinfo.h>
#include <asm/facility.h>
enum {
CACHE_SCOPE_NOTEXISTS,
CACHE_SCOPE_PRIVATE,
CACHE_SCOPE_SHARED,
CACHE_SCOPE_RESERVED,
};
enum {
CTYPE_SEPARATE,
CTYPE_DATA,
CTYPE_INSTRUCTION,
CTYPE_UNIFIED,
};
enum {
EXTRACT_TOPOLOGY,
EXTRACT_LINE_SIZE,
EXTRACT_SIZE,
EXTRACT_ASSOCIATIVITY,
};
enum {
CACHE_TI_UNIFIED = 0,
CACHE_TI_DATA = 0,
CACHE_TI_INSTRUCTION,
};
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[] = {
"",
"Instruction",
"Data",
"",
"Unified",
};
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,
};
void show_cacheinfo(struct seq_file *m)
{
struct cpu_cacheinfo *this_cpu_ci;
struct cacheinfo *cache;
int idx;
this_cpu_ci = get_cpu_cacheinfo(cpumask_any(cpu_online_mask));
for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) {
cache = this_cpu_ci->info_list + idx;
seq_printf(m, "cache%-11d: ", idx);
seq_printf(m, "level=%d ", cache->level);
seq_printf(m, "type=%s ", cache_type_string[cache->type]);
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);
seq_puts(m, "\n");
}
}
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];
}
static inline unsigned long ecag(int ai, int li, int ti)
{
return __ecag(ECAG_CACHE_ATTRIBUTE, ai << 4 | li << 1 | ti);
}
static void ci_leaf_init(struct cacheinfo *this_leaf, int private,
enum cache_type type, unsigned int level, int cpu)
{
int ti, num_sets;
if (type == CACHE_TYPE_INST)
ti = CACHE_TI_INSTRUCTION;
else
ti = CACHE_TI_UNIFIED;
this_leaf->level = level + 1;
this_leaf->type = type;
this_leaf->coherency_line_size = ecag(EXTRACT_LINE_SIZE, level, ti);
this_leaf->ways_of_associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti);
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;
}
int init_cache_level(unsigned int cpu)
{
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
unsigned int level = 0, leaves = 0;
union cache_topology ct;
enum cache_type ctype;
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)
break;
/* 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;
}
int populate_cache_leaves(unsigned int cpu)
{
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *this_leaf = this_cpu_ci->info_list;
unsigned int level, idx, pvt;
union cache_topology ct;
enum cache_type ctype;
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) {
ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_DATA, level, cpu);
ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_INST, level, cpu);
} else {
ci_leaf_init(this_leaf++, pvt, ctype, level, cpu);
}
}
this_cpu_ci->cpu_map_populated = true;
return 0;
}
|