linux/arch/s390/kernel/cache.c
Heiko Carstens cb0cd4ee11 s390/cache: prevent rebuild of shared_cpu_list
With commit 36bbc5b4ff ("cacheinfo: Allow early detection and population
of cache attributes") the shared cpu list for each cache level higher than
L1 is rebuilt even if the list already has been set up.

This is caused by the removal of the cpumask_empty() check within
cache_shared_cpu_map_setup().

However architectures can enforce that the shared cpu list is not rebuilt
by simply setting cpu_map_populated of the per cpu cache info structure to
true, which is also the fix for this problem.

Before:
$ cat /sys/devices/system/cpu/cpu1/cache/index2/shared_cpu_list
0-7

After:
$ cat /sys/devices/system/cpu/cpu1/cache/index2/shared_cpu_list
1

Fixes: 36bbc5b4ff ("cacheinfo: Allow early detection and population of cache attributes")
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
2024-03-07 14:41:15 +01:00

172 lines
4.3 KiB
C

// 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 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;
}