955 lines · 23.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Processor cache information made available to userspace via sysfs; * intended to be compatible with x86 intel_cacheinfo implementation. * * Copyright 2008 IBM Corporation * Author: Nathan Lynch */ #define pr_fmt(fmt) "cacheinfo: " fmt #include <linux/cpu.h>#include <linux/cpumask.h>#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/list.h>#include <linux/notifier.h>#include <linux/of.h>#include <linux/percpu.h>#include <linux/slab.h>#include <linux/sysfs.h>#include <asm/cputhreads.h>#include <asm/smp.h> #include "cacheinfo.h" /* per-cpu object for tracking: * - a "cache" kobject for the top-level directory * - a list of "index" objects representing the cpu's local cache hierarchy */struct cache_dir {	struct kobject *kobj; /* bare (not embedded) kobject for cache			       * directory */	struct cache_index_dir *index; /* list of index objects */}; /* "index" object: each cpu's cache directory has an index * subdirectory corresponding to a cache object associated with the * cpu.  This object's lifetime is managed via the embedded kobject. */struct cache_index_dir {	struct kobject kobj;	struct cache_index_dir *next; /* next index in parent directory */	struct cache *cache;}; /* Template for determining which OF properties to query for a given * cache type */struct cache_type_info {	const char *name;	const char *size_prop; 	/* Allow for both [di]-cache-line-size and	 * [di]-cache-block-size properties.  According to the PowerPC	 * Processor binding, -line-size should be provided if it	 * differs from the cache block size (that which is operated	 * on by cache instructions), so we look for -line-size first.	 * See cache_get_line_size(). */ 	const char *line_size_props[2];	const char *nr_sets_prop;}; /* These are used to index the cache_type_info array. */#define CACHE_TYPE_UNIFIED     0 /* cache-size, cache-block-size, etc. */#define CACHE_TYPE_UNIFIED_D   1 /* d-cache-size, d-cache-block-size, etc */#define CACHE_TYPE_INSTRUCTION 2#define CACHE_TYPE_DATA        3 static const struct cache_type_info cache_type_info[] = {	{		/* Embedded systems that use cache-size, cache-block-size,		 * etc. for the Unified (typically L2) cache. */		.name            = "Unified",		.size_prop       = "cache-size",		.line_size_props = { "cache-line-size",				     "cache-block-size", },		.nr_sets_prop    = "cache-sets",	},	{		/* PowerPC Processor binding says the [di]-cache-*		 * must be equal on unified caches, so just use		 * d-cache properties. */		.name            = "Unified",		.size_prop       = "d-cache-size",		.line_size_props = { "d-cache-line-size",				     "d-cache-block-size", },		.nr_sets_prop    = "d-cache-sets",	},	{		.name            = "Instruction",		.size_prop       = "i-cache-size",		.line_size_props = { "i-cache-line-size",				     "i-cache-block-size", },		.nr_sets_prop    = "i-cache-sets",	},	{		.name            = "Data",		.size_prop       = "d-cache-size",		.line_size_props = { "d-cache-line-size",				     "d-cache-block-size", },		.nr_sets_prop    = "d-cache-sets",	},}; /* Cache object: each instance of this corresponds to a distinct cache * in the system.  There are separate objects for Harvard caches: one * each for instruction and data, and each refers to the same OF node. * The refcount of the OF node is elevated for the lifetime of the * cache object.  A cache object is released when its shared_cpu_map * is cleared (see cache_cpu_clear). * * A cache object is on two lists: an unsorted global list * (cache_list) of cache objects; and a singly-linked list * representing the local cache hierarchy, which is ordered by level * (e.g. L1d -> L1i -> L2 -> L3). */struct cache {	struct device_node *ofnode;    /* OF node for this cache, may be cpu */	struct cpumask shared_cpu_map; /* online CPUs using this cache */	int type;                      /* split cache disambiguation */	int level;                     /* level not explicit in device tree */	int group_id;                  /* id of the group of threads that share this cache */	struct list_head list;         /* global list of cache objects */	struct cache *next_local;      /* next cache of >= level */}; static DEFINE_PER_CPU(struct cache_dir *, cache_dir_pcpu); /* traversal/modification of this list occurs only at cpu hotplug time; * access is serialized by cpu hotplug locking */static LIST_HEAD(cache_list); static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *k){	return container_of(k, struct cache_index_dir, kobj);} static const char *cache_type_string(const struct cache *cache){	return cache_type_info[cache->type].name;} static void cache_init(struct cache *cache, int type, int level,		       struct device_node *ofnode, int group_id){	cache->type = type;	cache->level = level;	cache->ofnode = of_node_get(ofnode);	cache->group_id = group_id;	INIT_LIST_HEAD(&cache->list);	list_add(&cache->list, &cache_list);} static struct cache *new_cache(int type, int level,			       struct device_node *ofnode, int group_id){	struct cache *cache; 	cache = kzalloc_obj(*cache);	if (cache)		cache_init(cache, type, level, ofnode, group_id); 	return cache;} static void release_cache_debugcheck(struct cache *cache){	struct cache *iter; 	list_for_each_entry(iter, &cache_list, list)		WARN_ONCE(iter->next_local == cache,			  "cache for %pOFP(%s) refers to cache for %pOFP(%s)\n",			  iter->ofnode,			  cache_type_string(iter),			  cache->ofnode,			  cache_type_string(cache));} static void release_cache(struct cache *cache){	if (!cache)		return; 	pr_debug("freeing L%d %s cache for %pOFP\n", cache->level,		 cache_type_string(cache), cache->ofnode); 	release_cache_debugcheck(cache);	list_del(&cache->list);	of_node_put(cache->ofnode);	kfree(cache);} static void cache_cpu_set(struct cache *cache, int cpu){	struct cache *next = cache; 	while (next) {		WARN_ONCE(cpumask_test_cpu(cpu, &next->shared_cpu_map),			  "CPU %i already accounted in %pOFP(%s)\n",			  cpu, next->ofnode,			  cache_type_string(next));		cpumask_set_cpu(cpu, &next->shared_cpu_map);		next = next->next_local;	}} static int cache_size(const struct cache *cache, unsigned int *ret){	const char *propname;	const __be32 *cache_size; 	propname = cache_type_info[cache->type].size_prop; 	cache_size = of_get_property(cache->ofnode, propname, NULL);	if (!cache_size)		return -ENODEV; 	*ret = of_read_number(cache_size, 1);	return 0;} static int cache_size_kb(const struct cache *cache, unsigned int *ret){	unsigned int size; 	if (cache_size(cache, &size))		return -ENODEV; 	*ret = size / 1024;	return 0;} /* not cache_line_size() because that's a macro in include/linux/cache.h */static int cache_get_line_size(const struct cache *cache, unsigned int *ret){	const __be32 *line_size;	int i, lim; 	lim = ARRAY_SIZE(cache_type_info[cache->type].line_size_props); 	for (i = 0; i < lim; i++) {		const char *propname; 		propname = cache_type_info[cache->type].line_size_props[i];		line_size = of_get_property(cache->ofnode, propname, NULL);		if (line_size)			break;	} 	if (!line_size)		return -ENODEV; 	*ret = of_read_number(line_size, 1);	return 0;} static int cache_nr_sets(const struct cache *cache, unsigned int *ret){	const char *propname;	const __be32 *nr_sets; 	propname = cache_type_info[cache->type].nr_sets_prop; 	nr_sets = of_get_property(cache->ofnode, propname, NULL);	if (!nr_sets)		return -ENODEV; 	*ret = of_read_number(nr_sets, 1);	return 0;} static int cache_associativity(const struct cache *cache, unsigned int *ret){	unsigned int line_size;	unsigned int nr_sets;	unsigned int size; 	if (cache_nr_sets(cache, &nr_sets))		goto err; 	/* If the cache is fully associative, there is no need to	 * check the other properties.	 */	if (nr_sets == 1) {		*ret = 0;		return 0;	} 	if (cache_get_line_size(cache, &line_size))		goto err;	if (cache_size(cache, &size))		goto err; 	if (!(nr_sets > 0 && size > 0 && line_size > 0))		goto err; 	*ret = (size / nr_sets) / line_size;	return 0;err:	return -ENODEV;} /* helper for dealing with split caches */static struct cache *cache_find_first_sibling(struct cache *cache){	struct cache *iter; 	if (cache->type == CACHE_TYPE_UNIFIED ||	    cache->type == CACHE_TYPE_UNIFIED_D)		return cache; 	list_for_each_entry(iter, &cache_list, list)		if (iter->ofnode == cache->ofnode &&		    iter->group_id == cache->group_id &&		    iter->next_local == cache)			return iter; 	return cache;} /* return the first cache on a local list matching node and thread-group id */static struct cache *cache_lookup_by_node_group(const struct device_node *node,						int group_id){	struct cache *cache = NULL;	struct cache *iter; 	list_for_each_entry(iter, &cache_list, list) {		if (iter->ofnode != node ||		    iter->group_id != group_id)			continue;		cache = cache_find_first_sibling(iter);		break;	} 	return cache;} static bool cache_node_is_unified(const struct device_node *np){	return of_get_property(np, "cache-unified", NULL);} /* * Unified caches can have two different sets of tags.  Most embedded * use cache-size, etc. for the unified cache size, but open firmware systems * use d-cache-size, etc.   Check on initialization for which type we have, and * return the appropriate structure type.  Assume it's embedded if it isn't * open firmware.  If it's yet a 3rd type, then there will be missing entries * in /sys/devices/system/cpu/cpu0/cache/index2/, and this code will need * to be extended further. */static int cache_is_unified_d(const struct device_node *np){	return of_get_property(np,		cache_type_info[CACHE_TYPE_UNIFIED_D].size_prop, NULL) ?		CACHE_TYPE_UNIFIED_D : CACHE_TYPE_UNIFIED;} static struct cache *cache_do_one_devnode_unified(struct device_node *node, int group_id,						  int level){	pr_debug("creating L%d ucache for %pOFP\n", level, node); 	return new_cache(cache_is_unified_d(node), level, node, group_id);} static struct cache *cache_do_one_devnode_split(struct device_node *node, int group_id,						int level){	struct cache *dcache, *icache; 	pr_debug("creating L%d dcache and icache for %pOFP\n", level,		 node); 	dcache = new_cache(CACHE_TYPE_DATA, level, node, group_id);	icache = new_cache(CACHE_TYPE_INSTRUCTION, level, node, group_id); 	if (!dcache || !icache)		goto err; 	dcache->next_local = icache; 	return dcache;err:	release_cache(dcache);	release_cache(icache);	return NULL;} static struct cache *cache_do_one_devnode(struct device_node *node, int group_id, int level){	struct cache *cache; 	if (cache_node_is_unified(node))		cache = cache_do_one_devnode_unified(node, group_id, level);	else		cache = cache_do_one_devnode_split(node, group_id, level); 	return cache;} static struct cache *cache_lookup_or_instantiate(struct device_node *node,						 int group_id,						 int level){	struct cache *cache; 	cache = cache_lookup_by_node_group(node, group_id); 	WARN_ONCE(cache && cache->level != level,		  "cache level mismatch on lookup (got %d, expected %d)\n",		  cache->level, level); 	if (!cache)		cache = cache_do_one_devnode(node, group_id, level); 	return cache;} static void link_cache_lists(struct cache *smaller, struct cache *bigger){	while (smaller->next_local) {		if (smaller->next_local == bigger)			return; /* already linked */		smaller = smaller->next_local;	} 	smaller->next_local = bigger; 	/*	 * The cache->next_local list sorts by level ascending:	 * L1d -> L1i -> L2 -> L3 ...	 */	WARN_ONCE((smaller->level == 1 && bigger->level > 2) ||		  (smaller->level > 1 && bigger->level != smaller->level + 1),		  "linking L%i cache %pOFP to L%i cache %pOFP; skipped a level?\n",		  smaller->level, smaller->ofnode, bigger->level, bigger->ofnode);} static void do_subsidiary_caches_debugcheck(struct cache *cache){	WARN_ONCE(cache->level != 1,		  "instantiating cache chain from L%d %s cache for "		  "%pOFP instead of an L1\n", cache->level,		  cache_type_string(cache), cache->ofnode);	WARN_ONCE(!of_node_is_type(cache->ofnode, "cpu"),		  "instantiating cache chain from node %pOFP of type '%s' "		  "instead of a cpu node\n", cache->ofnode,		  of_node_get_device_type(cache->ofnode));} /* * If sub-groups of threads in a core containing @cpu_id share the * L@level-cache (information obtained via "ibm,thread-groups" * device-tree property), then we identify the group by the first * thread-sibling in the group. We define this to be the group-id. * * In the absence of any thread-group information for L@level-cache, * this function returns -1. */static int get_group_id(unsigned int cpu_id, int level){	if (has_big_cores && level == 1)		return cpumask_first(per_cpu(thread_group_l1_cache_map,					     cpu_id));	else if (thread_group_shares_l2 && level == 2)		return cpumask_first(per_cpu(thread_group_l2_cache_map,					     cpu_id));	else if (thread_group_shares_l3 && level == 3)		return cpumask_first(per_cpu(thread_group_l3_cache_map,					     cpu_id));	return -1;} static void do_subsidiary_caches(struct cache *cache, unsigned int cpu_id){	struct device_node *subcache_node;	int level = cache->level; 	do_subsidiary_caches_debugcheck(cache); 	while ((subcache_node = of_find_next_cache_node(cache->ofnode))) {		struct cache *subcache;		int group_id; 		level++;		group_id = get_group_id(cpu_id, level);		subcache = cache_lookup_or_instantiate(subcache_node, group_id, level);		of_node_put(subcache_node);		if (!subcache)			break; 		link_cache_lists(cache, subcache);		cache = subcache;	}} 

Lines 1–500 of 955.