/* OF properties to query for a given cache type */ struct cache_type_info { constchar *size_prop; constchar *line_size_props[2]; constchar *nr_sets_prop;
};
/* not cache_line_size() because that's a macro in include/linux/cache.h */ staticvoid cache_get_line_size(struct cacheinfo *this_leaf, struct device_node *np)
{ int i, lim, ct_idx;
/* * If the cache is fully associative, there is no need to * check the other properties.
*/ if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0))
this_leaf->ways_of_associativity = (size / nr_sets) / line_size;
}
staticvoid cache_of_set_props(struct cacheinfo *this_leaf, struct device_node *np)
{ /* * init_cache_level must setup the cache level correctly * overriding the architecturally specified levels, so * if type is NONE at this stage, it should be unified
*/ if (this_leaf->type == CACHE_TYPE_NOCACHE &&
cache_node_is_unified(this_leaf, np))
this_leaf->type = CACHE_TYPE_UNIFIED;
cache_size(this_leaf, np);
cache_get_line_size(this_leaf, np);
cache_nr_sets(this_leaf, np);
cache_associativity(this_leaf);
cache_of_set_id(this_leaf, np);
}
if (of_property_present(np, "cache-size"))
++leaves; if (of_property_present(np, "i-cache-size"))
++leaves; if (of_property_present(np, "d-cache-size"))
++leaves;
if (!leaves) { /* The '[i-|d-|]cache-size' property is required, but * if absent, fallback on the 'cache-unified' property.
*/ if (of_property_read_bool(np, "cache-unified")) return 1; else return 2;
}
int __weak cache_setup_acpi(unsignedint cpu)
{ return -ENOTSUPP;
}
unsignedint coherency_max_size;
staticint cache_setup_properties(unsignedint cpu)
{ int ret = 0;
if (of_have_populated_dt())
ret = cache_setup_of_node(cpu); elseif (!acpi_disabled)
ret = cache_setup_acpi(cpu);
// Assume there is no cache information available in DT/ACPI from now. if (ret && use_arch_cache_info())
use_arch_info = true;
return ret;
}
staticint cache_shared_cpu_map_setup(unsignedint cpu)
{ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf, *sib_leaf; unsignedint index, sib_index; int ret = 0;
if (this_cpu_ci->cpu_map_populated) return 0;
/* * skip setting up cache properties if LLC is valid, just need * to update the shared cpu_map if the cache attributes were * populated early before all the cpus are brought online
*/ if (!last_level_cache_is_valid(cpu) && !use_arch_info) {
ret = cache_setup_properties(cpu); if (ret) return ret;
}
for (index = 0; index < cache_leaves(cpu); index++) { unsignedint i;
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
for_each_online_cpu(i) { if (i == cpu || !per_cpu_cacheinfo(i)) continue;/* skip if itself or no cacheinfo */ for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) {
sib_leaf = per_cpu_cacheinfo_idx(i, sib_index);
/* * Comparing cache IDs only makes sense if the leaves * belong to the same cache level of same type. Skip * the check if level and type do not match.
*/ if (sib_leaf->level != this_leaf->level ||
sib_leaf->type != this_leaf->type) continue;
if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
cpumask_set_cpu(i, &this_leaf->shared_cpu_map); break;
}
}
} /* record the maximum cache line size */ if (this_leaf->coherency_line_size > coherency_max_size)
coherency_max_size = this_leaf->coherency_line_size;
}
/* shared_cpu_map is now populated for the cpu */
this_cpu_ci->cpu_map_populated = true; return 0;
}
for (index = 0; index < cache_leaves(cpu); index++) {
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
for_each_cpu(sibling, &this_leaf->shared_cpu_map) { if (sibling == cpu || !per_cpu_cacheinfo(sibling)) continue;/* skip if itself or no cacheinfo */
/* * Comparing cache IDs only makes sense if the leaves * belong to the same cache level of same type. Skip * the check if level and type do not match.
*/ if (sib_leaf->level != this_leaf->level ||
sib_leaf->type != this_leaf->type) continue;
int fetch_cache_info(unsignedint cpu)
{ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); unsignedint levels = 0, split_levels = 0; int ret;
if (acpi_disabled) {
ret = init_of_cache_level(cpu);
} else {
ret = acpi_get_cache_info(cpu, &levels, &split_levels); if (!ret) {
this_cpu_ci->num_levels = levels; /* * This assumes that: * - there cannot be any split caches (data/instruction) * above a unified cache * - data/instruction caches come by pair
*/
this_cpu_ci->num_leaves = levels + split_levels;
}
}
if (ret || !cache_leaves(cpu)) {
ret = early_cache_level(cpu); if (ret) return ret;
/* Since early initialization/allocation of the cacheinfo is allowed * via fetch_cache_info() and this also gets called as CPU hotplug * callbacks via cacheinfo_cpu_online, the init/alloc can be skipped * as it will happen only once (the cacheinfo memory is never freed). * Just populate the cacheinfo. However, if the cacheinfo has been * allocated early through the arch-specific early_cache_level() call, * there is a chance the info is wrong (this can happen on arm64). In * that case, call init_cache_level() anyway to give the arch-specific * code a chance to make things right.
*/ if (per_cpu_cacheinfo(cpu) && !ci_cacheinfo(cpu)->early_ci_levels) return 0;
if (init_cache_level(cpu) || !cache_leaves(cpu)) return -ENOENT;
/* * Now that we have properly initialized the cache level info, make * sure we don't try to do that again the next time we are called * (e.g. as CPU hotplug callbacks).
*/
ci_cacheinfo(cpu)->early_ci_levels = false;
/* * Some architectures (e.g., x86) do not use early initialization. * Allocate memory now in such case.
*/ if (cache_leaves(cpu) <= early_leaves && per_cpu_cacheinfo(cpu)) return 0;
int detect_cache_attributes(unsignedint cpu)
{ int ret;
ret = init_level_allocate_ci(cpu); if (ret) return ret;
/* * If LLC is valid the cache leaves were already populated so just go to * update the cpu map.
*/ if (!last_level_cache_is_valid(cpu)) { /* * populate_cache_leaves() may completely setup the cache leaves and * shared_cpu_map or it may leave it partially setup.
*/
ret = populate_cache_leaves(cpu); if (ret) goto free_ci;
}
/* * For systems using DT for cache hierarchy, fw_token * and shared_cpu_map will be set up here only if they are * not populated already
*/
ret = cache_shared_cpu_map_setup(cpu); if (ret) {
pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); goto free_ci;
}
if (llc->type != CACHE_TYPE_DATA && llc->type != CACHE_TYPE_UNIFIED) return 0;
if (online) {
*map = &llc->shared_cpu_map; return cpumask_weight(*map);
}
/* shared_cpu_map of offlined CPU will be cleared, so use sibling map */
for_each_cpu(sibling, &llc->shared_cpu_map) { if (sibling == cpu || !last_level_cache_is_valid(sibling)) continue;
sib_llc = per_cpu_cacheinfo_idx(sibling, cache_leaves(sibling) - 1);
*map = &sib_llc->shared_cpu_map; return cpumask_weight(*map);
}
return 0;
}
/* * Calculate the size of the per-CPU data cache slice. This can be * used to estimate the size of the data cache slice that can be used * by one CPU under ideal circumstances. UNIFIED caches are counted * in addition to DATA caches. So, please consider code cache usage * when use the result. * * Because the cache inclusive/non-inclusive information isn't * available, we just use the size of the per-CPU slice of LLC to make * the result more predictable across architectures.
*/ staticvoid update_per_cpu_data_slice_size_cpu(unsignedint cpu)
{ struct cpu_cacheinfo *ci; struct cacheinfo *llc; unsignedint nr_shared;
if (!last_level_cache_is_valid(cpu)) return;
ci = ci_cacheinfo(cpu);
llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1);
if (llc->type != CACHE_TYPE_DATA && llc->type != CACHE_TYPE_UNIFIED) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.