All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support
@ 2014-02-07 16:49 Sudeep Holla
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
                   ` (7 more replies)
  0 siblings, 8 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: sudeep.holla

From: Sudeep Holla <sudeep.holla@arm.com>

Hi,

This series adds a generic cacheinfo support similar to topology. The
implementation is based on x86 cacheinfo support. Currently x86, powerpc,
ia64 and s390 have their own implementations. While adding similar support
to ARM and ARM64, here is the attempt to make it generic quite similar to
topology info support. It also adds the missing ABI documentation for
the cacheinfo sysfs which is already being used.

It moves all the existing different implementations on x86, ia64, powerpc
and s390 to use the generic cacheinfo infrastructure introduced here.
These changes on non-ARM platforms are only compile tested and hence
the request for testing too.

This series also adds support for ARM and ARM64 architectures based on
the generic support.

Changes v1->v2:
	- Extended the generic cacheinfo support to accomodate all
	  the existing implementations
	- Moved all the existing implementations to use this new
	  generic infrastructure
	- Added missing ABI documentation as suggested by Greg KH
	- Added support for unimplemented CTR on pre-ARMv6 implementations
	  as suggested by Russell. However the ctr_info_list is not yet
	  populated
	- not yet changed to device_attr as suggested by Greg KH,
	  registering cache as device won't eliminate the need of kobject
	  unless each index of cache is registered as a device which don't
	  seem to be good idea, but now it's unified it can be done easily
	  in one place if needed

Sudeep Holla (8):
  drivers: base: support cpu cache information interface to userspace
    via sysfs
  ia64: move cacheinfo sysfs to generic cacheinfo infrastructure
  s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  x86: move cacheinfo sysfs to generic cacheinfo infrastructure
  powerpc: move cacheinfo sysfs to generic cacheinfo infrastructure
  ARM64: kernel: add support for cpu cache information
  ARM: kernel: add support for cpu cache information
  ARM: kernel: add outer cache support for cacheinfo implementation

 Documentation/ABI/testing/sysfs-devices-system-cpu |  40 +
 arch/arm/include/asm/outercache.h                  |  13 +
 arch/arm/kernel/Makefile                           |   1 +
 arch/arm/kernel/cacheinfo.c                        | 248 ++++++
 arch/arm/mm/Kconfig                                |  13 +
 arch/arm/mm/cache-l2x0.c                           |  14 +
 arch/arm/mm/cache-tauros2.c                        |  35 +
 arch/arm/mm/cache-xsc3l2.c                         |  15 +
 arch/arm64/kernel/Makefile                         |   2 +-
 arch/arm64/kernel/cacheinfo.c                      | 134 ++++
 arch/ia64/kernel/topology.c                        | 399 ++--------
 arch/powerpc/kernel/cacheinfo.c                    | 828 +++------------------
 arch/powerpc/kernel/cacheinfo.h                    |   8 -
 arch/powerpc/kernel/sysfs.c                        |   4 -
 arch/s390/include/asm/processor.h                  |   6 -
 arch/s390/kernel/cache.c                           | 380 ++--------
 arch/s390/kernel/processor.c                       |   1 -
 arch/x86/kernel/cpu/intel_cacheinfo.c              | 628 ++++------------
 drivers/base/Makefile                              |   2 +-
 drivers/base/cacheinfo.c                           | 520 +++++++++++++
 include/linux/cacheinfo.h                          |  60 ++
 21 files changed, 1495 insertions(+), 1856 deletions(-)
 create mode 100644 arch/arm/kernel/cacheinfo.c
 create mode 100644 arch/arm64/kernel/cacheinfo.c
 delete mode 100644 arch/powerpc/kernel/cacheinfo.h
 create mode 100644 drivers/base/cacheinfo.c
 create mode 100644 include/linux/cacheinfo.h

-- 
1.8.3.2


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49 ` Sudeep Holla
  2014-02-07 19:29   ` Greg Kroah-Hartman
  2014-02-07 16:49   ` Sudeep Holla
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: sudeep.holla, Greg Kroah-Hartman, Rob Herring, linux-doc

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds initial support for providing processor cache information
to userspace through sysfs interface. This is based on already existing
implementations(x86, ia64, s390 and powerpc) and hence the interface is
intended to be fully compatible.

The main purpose of this generic support is to avoid further code
duplication to support new architectures and also to unify all the existing
different implementations.

This implementation maintains the hierarchy of cache objects which reflects
the system's cache topology. Cache objects are instantiated as needed as
CPUs come online. The cache objects are replicated per-cpu even if they are
shared. A per-cpu array of cache information maintained is used mainly for
sysfs-related book keeping.

It also implements the shared_cpu_map attribute, which is essential for
enabling both kernel and user-space to discover the system's overall cache
topology.

This patch also add the missing ABI documentation for the cacheinfo sysfs
interface already, which is well defined and widely used.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Rob Herring <robh@kernel.org>
Cc: linux-doc@vger.kernel.org
---
 Documentation/ABI/testing/sysfs-devices-system-cpu |  40 ++
 drivers/base/Makefile                              |   2 +-
 drivers/base/cacheinfo.c                           | 520 +++++++++++++++++++++
 include/linux/cacheinfo.h                          |  60 +++
 4 files changed, 621 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/cacheinfo.c
 create mode 100644 include/linux/cacheinfo.h

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index d5a0d33..dabe03e 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -224,3 +224,43 @@ Description:	Parameters for the Intel P-state driver
 		frequency range.
 
 		More details can be found in Documentation/cpu-freq/intel-pstate.txt
+
+What:		/sys/devices/system/cpu/cpu*/cache/index*/<set_of_attributes_mentioned_below>
+Date:		February 2014
+Contact:	Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description:	Parameters for the CPU cache attributes
+
+		attributes:
+			- writethrough: data is written to both the cache line
+					and to the block in the lower-level memory
+			- writeback: data is written only to the cache line and
+				     the modified cache line is written to main
+				     memory only when it is replaced
+			- writeallocate: allocate a memory location to a cache line
+					 on a cache miss because of a write
+			- readallocate: allocate a memory location to a cache line
+					on a cache miss because of a read
+
+		coherency_line_size: the minimum amount of data that gets transferred
+
+		level: the cache hierarcy in the multi-level cache configuration
+
+		number_of_sets: total number of sets in the cache, a set is a
+				collection of cache lines with the same cache index
+
+		physical_line_partition: number of physical cache line per cache tag
+
+		shared_cpu_list: the list of cpus sharing the cache
+
+		shared_cpu_map: logical cpu mask containing the list of cpus sharing
+				the cache
+
+		size: the total cache size in kB
+
+		type:
+			- instruction: cache that only holds instructions
+			- data: cache that only caches data
+			- unified: cache that holds both data and instructions
+
+		ways_of_associativity: degree of freedom in placing a particular block
+					of memory in the cache
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 04b314e..bad2ff8 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -4,7 +4,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
 			   driver.o class.o platform.o \
 			   cpu.o firmware.o init.o map.o devres.o \
 			   attribute_container.o transport_class.o \
-			   topology.o container.o
+			   topology.o container.o cacheinfo.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
 obj-y			+= power/
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
new file mode 100644
index 0000000..43bbd34
--- /dev/null
+++ b/drivers/base/cacheinfo.c
@@ -0,0 +1,520 @@
+/*
+ * cacheinfo support - processor cache information via sysfs
+ *
+ * Based on arch/x86/kernel/cpu/intel_cacheinfo.c
+ * Author: Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/compiler.h>
+#include <linux/cpu.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/sysfs.h>
+
+/* pointer to cpu_cacheinfo array (for each cache leaf) */
+static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cache_info);
+#define ci_cacheinfo(cpu)	(&per_cpu(ci_cpu_cache_info, cpu))
+#define cache_leaves(cpu)	(ci_cacheinfo(cpu)->num_leaves)
+#define per_cpu_cacheinfo(cpu)	(ci_cacheinfo(cpu)->info_list)
+
+struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu)
+{
+	return ci_cacheinfo(cpu);
+}
+
+#ifdef CONFIG_OF
+static int cache_setup_of_node(unsigned int cpu)
+{
+	struct device_node *np;
+	struct cache_info *this_leaf;
+	struct device *cpu_dev = get_cpu_device(cpu);
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned int index = 0;
+
+	/* skip if of_node is already populated */
+	if (this_cpu_ci->info_list->of_node)
+		return 0;
+
+	if (!cpu_dev) {
+		pr_err("No cpu device for CPU %d\n", cpu);
+		return -ENODEV;
+	}
+	np = cpu_dev->of_node;
+	if (!np) {
+		pr_err("Failed to find cpu%d device node\n", cpu);
+		return -ENOENT;
+	}
+
+	while (np && index < cache_leaves(cpu)) {
+		this_leaf = this_cpu_ci->info_list + index;
+		if (this_leaf->level != 1)
+			np = of_find_next_cache_node(np);
+		else
+			np = of_node_get(np);/* cpu node itself */
+		this_leaf->of_node = np;
+		index++;
+	}
+	return 0;
+}
+
+static inline bool cache_leaves_are_shared(struct cache_info *this_leaf,
+					   struct cache_info *sib_leaf)
+{
+	return sib_leaf->of_node == this_leaf->of_node;
+}
+
+static int of_cache_shared_cpu_map_setup(unsigned int cpu)
+{
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf, *sib_leaf;
+	unsigned int index;
+	int ret;
+
+	ret = cache_setup_of_node(cpu);
+	if (ret)
+		return ret;
+
+	for (index = 0; index < cache_leaves(cpu); index++) {
+		unsigned int i;
+		this_leaf = this_cpu_ci->info_list + index;
+		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
+
+		for_each_online_cpu(i) {
+			struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
+			if (i == cpu || !sib_cpu_ci->info_list)
+				continue;/* skip if itself or no cacheinfo */
+			sib_leaf = sib_cpu_ci->info_list + index;
+			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);
+			}
+		}
+	}
+
+	return 0;
+}
+#else
+static inline int of_cache_shared_cpu_map_setup(unsigned int cpu)
+{
+	return 0;
+}
+#endif
+
+static void cache_shared_cpu_map_remove(unsigned int cpu)
+{
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf, *sib_leaf;
+	unsigned int sibling, index;
+
+	for (index = 0; index < cache_leaves(cpu); index++) {
+		this_leaf = this_cpu_ci->info_list + index;
+		for_each_cpu(sibling, &this_leaf->shared_cpu_map) {
+			struct cpu_cacheinfo *sib_cpu_ci;
+			if (sibling == cpu) /* skip itself */
+				continue;
+			sib_cpu_ci = get_cpu_cacheinfo(sibling);
+			sib_leaf = sib_cpu_ci->info_list + index;
+			cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
+			cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
+		}
+		of_node_put(this_leaf->of_node);
+	}
+}
+
+int __weak init_cache_level(unsigned int cpu)
+{
+	return -ENOENT;
+}
+
+int __weak populate_cache_leaves(unsigned int cpu)
+{
+	return -ENOENT;
+}
+
+static void free_cache_attributes(unsigned int cpu)
+{
+	cache_shared_cpu_map_remove(cpu);
+
+	kfree(per_cpu_cacheinfo(cpu));
+	per_cpu_cacheinfo(cpu) = NULL;
+}
+
+/* must be executed on the cpu whose cache attributes are being detected */
+static int detect_cache_attributes(unsigned int cpu)
+{
+	int ret;
+
+	if (init_cache_level(cpu))
+		return -ENOENT;
+
+	per_cpu_cacheinfo(cpu) = kzalloc(sizeof(struct cache_info) *
+					 cache_leaves(cpu), GFP_KERNEL);
+	if (per_cpu_cacheinfo(cpu) == NULL)
+		return -ENOMEM;
+
+	ret = populate_cache_leaves(cpu);
+	if (ret)
+		goto free_ci;
+	/*
+	 * For systems using DT for cache hierarcy, of_node and shared_cpu_map
+	 * will be set up here. Otherwise populate_cache_leaves needs to set
+	 * shared_cpu_map and next-level-cache should not be specified in DT
+	 */
+	ret = of_cache_shared_cpu_map_setup(cpu);
+	if (ret)
+		goto free_ci;
+	return 0;
+
+free_ci:
+	free_cache_attributes(cpu);
+	return ret;
+}
+
+#ifdef CONFIG_SYSFS
+
+/* pointer to kobject for cpuX/cache */
+static DEFINE_PER_CPU(struct kobject *, ci_cache_kobject);
+#define per_cpu_cache_kobject(cpu)	(per_cpu(ci_cache_kobject, cpu))
+
+struct index_kobject {
+	struct kobject kobj;
+	unsigned int cpu;
+	unsigned short index;
+};
+
+static cpumask_t cache_dev_map;
+
+/* pointer to array of kobjects for cpuX/cache/indexY */
+static DEFINE_PER_CPU(struct index_kobject *, ci_index_kobject);
+#define per_cpu_index_kobject(cpu)	(per_cpu(ci_index_kobject, cpu))
+#define INDEX_KOBJECT_PTR(cpu, idx)	(&((per_cpu_index_kobject(cpu))[idx]))
+
+#define show_one_plus(file_name, object)			\
+static ssize_t file_name##_show(struct cache_info *this_leaf,	\
+				char *buf, unsigned int cpu)	\
+{								\
+	if (!this_leaf->object)					\
+		return sprintf(buf, "Unknown\n");		\
+	return sprintf(buf, "%lu\n", (unsigned long)this_leaf->object); \
+}
+
+show_one_plus(level, level);
+show_one_plus(coherency_line_size, coherency_line_size);
+show_one_plus(ways_of_associativity, ways_of_associativity);
+show_one_plus(number_of_sets, number_of_sets);
+show_one_plus(physical_line_partition, physical_line_partition);
+
+static ssize_t size_show(struct cache_info *this_leaf, char *buf,
+			 unsigned int cpu)
+{
+	return sprintf(buf, "%dK\n", this_leaf->size >> 10);
+}
+
+static ssize_t shared_cpu_map_show_func(struct cache_info *this_leaf,
+					int type, char *buf)
+{
+	ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
+	int n = 0;
+
+	if (len > 1) {
+		const struct cpumask *mask = &this_leaf->shared_cpu_map;
+		n = type ? cpulist_scnprintf(buf, len - 2, mask) :
+			   cpumask_scnprintf(buf, len - 2, mask);
+		buf[n++] = '\n';
+		buf[n] = '\0';
+	}
+	return n;
+}
+
+static inline ssize_t shared_cpu_map_show(struct cache_info *leaf, char *buf,
+					  unsigned int cpu)
+{
+	return shared_cpu_map_show_func(leaf, 0, buf);
+}
+
+static inline ssize_t shared_cpu_list_show(struct cache_info *leaf,
+					   char *buf, unsigned int cpu)
+{
+	return shared_cpu_map_show_func(leaf, 1, buf);
+}
+
+static ssize_t type_show(struct cache_info *this_leaf, char *buf,
+			 unsigned int cpu)
+{
+	switch (this_leaf->type) {
+	case CACHE_TYPE_DATA:
+		return sprintf(buf, "Data\n");
+	case CACHE_TYPE_INST:
+		return sprintf(buf, "Instruction\n");
+	case CACHE_TYPE_UNIFIED:
+		return sprintf(buf, "Unified\n");
+	default:
+		return sprintf(buf, "Unknown\n");
+	}
+}
+
+static ssize_t attributes_show(struct cache_info *this_leaf, char *buf,
+			       unsigned int cpu)
+{
+	unsigned int ci_attr = this_leaf->attributes;
+	ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf - 2;
+	int n = 0;
+
+	if (!ci_attr)
+		return sprintf(buf, "Unknown\n");
+
+	if (ci_attr & CACHE_WRITE_THROUGH)
+		n += snprintf(buf + n, len - n, "WriteThrough\n");
+	if (ci_attr & CACHE_WRITE_BACK)
+		n += snprintf(buf + n, len - n, "WriteBack\n");
+	if (ci_attr & CACHE_READ_ALLOCATE)
+		n += snprintf(buf + n, len - n, "ReadAllocate\n");
+	if (ci_attr & CACHE_WRITE_ALLOCATE)
+		n += snprintf(buf + n, len - n, "WriteAllocate\n");
+	buf[n] = '\0';
+	return n;
+}
+
+#define to_object(k)	container_of(k, struct index_kobject, kobj)
+#define to_attr(a)	container_of(a, struct cache_attr, attr)
+
+#define define_one_ro(_name) \
+static struct cache_attr _name = __ATTR_RO(_name)
+
+define_one_ro(level);
+define_one_ro(type);
+define_one_ro(coherency_line_size);
+define_one_ro(ways_of_associativity);
+define_one_ro(number_of_sets);
+define_one_ro(size);
+define_one_ro(attributes);
+define_one_ro(shared_cpu_map);
+define_one_ro(shared_cpu_list);
+define_one_ro(physical_line_partition);
+
+static struct attribute *default_attrs[] = {
+	&type.attr,
+	&level.attr,
+	&coherency_line_size.attr,
+	&ways_of_associativity.attr,
+	&number_of_sets.attr,
+	&size.attr,
+	&attributes.attr,
+	&physical_line_partition.attr,
+	&shared_cpu_map.attr,
+	&shared_cpu_list.attr,
+	NULL
+};
+
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct cache_attr *fattr = to_attr(attr);
+	struct index_kobject *this_idx = to_object(kobj);
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(this_idx->cpu);
+	ssize_t ret;
+
+	ret = fattr->show ?
+		fattr->show(this_cpu_ci->info_list + this_idx->index, buf,
+			    this_idx->cpu) : 0;
+	return ret;
+}
+
+static ssize_t store(struct kobject *kobj, struct attribute *attr,
+		     const char *buf, size_t count)
+{
+	struct cache_attr *fattr = to_attr(attr);
+	struct index_kobject *this_idx = to_object(kobj);
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(this_idx->cpu);
+	ssize_t ret;
+
+	ret = fattr->store ?
+		fattr->store(this_cpu_ci->info_list + this_idx->index, buf,
+			     count, this_idx->cpu) : 0;
+	return ret;
+}
+
+static const struct sysfs_ops sysfs_ops = {
+	.show = show,
+	.store = store,
+};
+
+static struct kobj_type ktype_cache = {
+	.sysfs_ops = &sysfs_ops,
+	.default_attrs = default_attrs,
+};
+
+static void cpu_cache_sysfs_exit(unsigned int cpu)
+{
+	kfree(per_cpu_index_kobject(cpu));
+	per_cpu_index_kobject(cpu) = NULL;
+	kobject_put(per_cpu_cache_kobject(cpu));
+	per_cpu_cache_kobject(cpu) = NULL;
+}
+
+static int cpu_cache_sysfs_init(unsigned int cpu)
+{
+	struct device *dev = get_cpu_device(cpu);
+
+	if (per_cpu_cacheinfo(cpu) == NULL)
+		return -ENOENT;
+
+	per_cpu_cache_kobject(cpu) = kobject_create_and_add("cache",
+							    &dev->kobj);
+	if (unlikely(!per_cpu_cache_kobject(cpu)))
+		goto err_out;
+
+	/* Allocate all required memory */
+	per_cpu_index_kobject(cpu) = kzalloc(sizeof(struct index_kobject) *
+					     cache_leaves(cpu), GFP_KERNEL);
+	if (unlikely(per_cpu_index_kobject(cpu) == NULL))
+		goto err_out;
+
+	return 0;
+
+err_out:
+	cpu_cache_sysfs_exit(cpu);
+	return -ENOMEM;
+}
+
+int __weak cache_add_private_attributes(struct kobject *kobj, unsigned int cpu,
+					unsigned short index)
+{
+	return 0;
+}
+
+/* Add/Remove cache interface for CPU device */
+static int cache_add_dev(unsigned int cpu)
+{
+	struct index_kobject *this_object;
+	unsigned long i, j;
+	int rc;
+
+	rc = cpu_cache_sysfs_init(cpu);
+	if (unlikely(rc < 0))
+		return rc;
+
+	for (i = 0; i < cache_leaves(cpu); i++) {
+		this_object = INDEX_KOBJECT_PTR(cpu, i);
+		this_object->cpu = cpu;
+		this_object->index = i;
+
+		rc = kobject_init_and_add(&(this_object->kobj),
+					      &ktype_cache,
+					      per_cpu_cache_kobject(cpu),
+					      "index%1lu", i);
+		if (unlikely(rc))
+			goto kobj_err;
+
+		rc = cache_add_private_attributes(&(this_object->kobj), cpu, i);
+		if (unlikely(rc)) {
+			i++; /* delete including current kobject */
+			goto kobj_err;
+		}
+
+		kobject_uevent(&(this_object->kobj), KOBJ_ADD);
+	}
+	cpumask_set_cpu(cpu, &cache_dev_map);
+
+	kobject_uevent(per_cpu_cache_kobject(cpu), KOBJ_ADD);
+	return 0;
+kobj_err:
+	for (j = 0; j < i; j++)
+		kobject_put(&(INDEX_KOBJECT_PTR(cpu, j)->kobj));
+	cpu_cache_sysfs_exit(cpu);
+	return rc;
+}
+
+static void cache_remove_dev(unsigned int cpu)
+{
+	unsigned long i;
+
+	if (!cpumask_test_cpu(cpu, &cache_dev_map))
+		return;
+	cpumask_clear_cpu(cpu, &cache_dev_map);
+
+	for (i = 0; i < cache_leaves(cpu); i++)
+		kobject_put(&(INDEX_KOBJECT_PTR(cpu, i)->kobj));
+	cpu_cache_sysfs_exit(cpu);
+}
+
+static int cacheinfo_cpu_callback(struct notifier_block *nfb,
+				  unsigned long action, void *hcpu)
+{
+	unsigned int cpu = (unsigned long)hcpu;
+	int rc = 0;
+
+	switch (action) {
+	case CPU_STARTING:
+	case CPU_STARTING_FROZEN:
+		rc = detect_cache_attributes(cpu);
+		break;
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		rc = cache_add_dev(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		cache_remove_dev(cpu);
+	case CPU_UP_CANCELED:
+	case CPU_UP_CANCELED_FROZEN:
+		if (per_cpu_cacheinfo(cpu))
+			free_cache_attributes(cpu);
+		break;
+	}
+	return notifier_from_errno(rc);
+}
+
+/* Helpers to make sure detect_cache_attributes is called on right cpu */
+static void _detect_cache_attributes(void *retval)
+{
+	int cpu = smp_processor_id();
+	*(int *)retval = detect_cache_attributes(cpu);
+}
+
+static int __detect_cache_attributes(unsigned int cpu)
+{
+	int retval;
+	smp_call_function_single(cpu, _detect_cache_attributes, &retval, true);
+	return retval;
+}
+
+static int __init cacheinfo_sysfs_init(void)
+{
+	int cpu;
+	int rc;
+
+	for_each_online_cpu(cpu) {
+		rc = __detect_cache_attributes(cpu);
+		if (rc) {
+			pr_err("error detecting cacheinfo..cpu%d\n", cpu);
+			return rc;
+		}
+		rc = cache_add_dev(cpu);
+		if (rc) {
+			free_cache_attributes(cpu);
+			pr_err("error populating cacheinfo..cpu%d\n", cpu);
+			return rc;
+		}
+	}
+	hotcpu_notifier(cacheinfo_cpu_callback, 0);
+	return 0;
+}
+
+device_initcall(cacheinfo_sysfs_init);
+
+#endif	/* CONFIG_SYSFS */
diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h
new file mode 100644
index 0000000..a015a17
--- /dev/null
+++ b/include/linux/cacheinfo.h
@@ -0,0 +1,60 @@
+#ifndef _LINUX_CACHEINFO_H
+#define _LINUX_CACHEINFO_H
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/cpumask.h>
+#include <linux/of.h>
+#include <linux/sysfs.h>
+
+enum cache_type {
+	CACHE_TYPE_NOCACHE = 0,
+	CACHE_TYPE_INST = BIT(0),
+	CACHE_TYPE_DATA = BIT(1),
+	CACHE_TYPE_SEPARATE = CACHE_TYPE_INST | CACHE_TYPE_DATA,
+	CACHE_TYPE_UNIFIED = BIT(2),
+};
+
+struct cache_info {
+	/* core properties */
+	enum cache_type type; /* data, inst or unified */
+	unsigned int level;
+	unsigned int coherency_line_size; /* cache line size  */
+	unsigned int number_of_sets; /* no. of sets per way */
+	unsigned int ways_of_associativity; /* no. of ways */
+	unsigned int physical_line_partition; /* no. of lines per tag */
+	unsigned int size; /* total cache size */
+	cpumask_t shared_cpu_map;
+	unsigned int attributes;
+#define CACHE_WRITE_THROUGH	BIT(0)
+#define CACHE_WRITE_BACK	BIT(1)
+#define CACHE_READ_ALLOCATE	BIT(2)
+#define CACHE_WRITE_ALLOCATE	BIT(3)
+
+	/* book keeping */
+	struct device_node *of_node;	/* cpu if no explicit cache node */
+	void *priv;
+};
+
+struct cpu_cacheinfo {
+	struct cache_info *info_list;
+	unsigned int num_levels;
+	unsigned int num_leaves;
+};
+
+struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu);
+int init_cache_level(unsigned int cpu);
+int populate_cache_leaves(unsigned int cpu);
+
+#ifdef CONFIG_SYSFS
+struct cache_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct cache_info *, char *, unsigned int);
+	ssize_t (*store)(struct cache_info *, const char *, size_t count,
+			 unsigned int);
+};
+int cache_add_private_attributes(struct kobject *kobj, unsigned int cpu,
+				 unsigned short index);
+#endif
+
+#endif /* _LINUX_CACHEINFO_H */
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 2/8] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49   ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: sudeep.holla, Tony Luck, Fenghua Yu, linux-ia64

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: linux-ia64@vger.kernel.org
---
 arch/ia64/kernel/topology.c | 399 +++++++++-----------------------------------
 1 file changed, 79 insertions(+), 320 deletions(-)

diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index ca69a5a..78cdeda 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -13,6 +13,7 @@
  *	Populate cpu cache entries in sysfs for cpu cache info
  */
 
+#include <linux/cacheinfo.h>
 #include <linux/cpu.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -21,7 +22,6 @@
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/nodemask.h>
-#include <linux/notifier.h>
 #include <linux/export.h>
 #include <asm/mmzone.h>
 #include <asm/numa.h>
@@ -103,60 +103,25 @@ subsys_initcall(topology_init);
 /*
  * Export cpu cache information through sysfs
  */
-
-/*
- *  A bunch of string array to get pretty printing
- */
-static const char *cache_types[] = {
-	"",			/* not used */
-	"Instruction",
-	"Data",
-	"Unified"	/* unified */
-};
-
-static const char *cache_mattrib[]={
-	"WriteThrough",
-	"WriteBack",
-	"",		/* reserved */
-	""		/* reserved */
-};
-
-struct cache_info {
-	pal_cache_config_info_t	cci;
-	cpumask_t shared_cpu_map;
-	int level;
-	int type;
-	struct kobject kobj;
-};
-
-struct cpu_cache_info {
-	struct cache_info *cache_leaves;
-	int	num_cache_leaves;
-	struct kobject kobj;
-};
-
-static struct cpu_cache_info	all_cpu_cache_info[NR_CPUS];
-#define LEAF_KOBJECT_PTR(x,y)    (&all_cpu_cache_info[x].cache_leaves[y])
-
 #ifdef CONFIG_SMP
-static void cache_shared_cpu_map_setup(unsigned int cpu,
-		struct cache_info * this_leaf)
+static int __cache_cpumap_setup(unsigned int cpu, struct cache_info *this_leaf)
 {
 	pal_cache_shared_info_t	csi;
-	int num_shared, i = 0;
-	unsigned int j;
+	int num_shared, i = 0, j;
+	enum cache_type type = this_leaf->type;
 
 	if (cpu_data(cpu)->threads_per_core <= 1 &&
 		cpu_data(cpu)->cores_per_socket <= 1) {
 		cpu_set(cpu, this_leaf->shared_cpu_map);
-		return;
+		return 0;
 	}
 
-	if (ia64_pal_cache_shared_info(this_leaf->level,
-					this_leaf->type,
-					0,
-					&csi) != PAL_STATUS_SUCCESS)
-		return;
+	if (type == CACHE_TYPE_UNIFIED)
+		type = CACHE_TYPE_DATA;
+
+	if (ia64_pal_cache_shared_info(this_leaf->level, type, 0,
+				       &csi) != PAL_STATUS_SUCCESS)
+		return -EIO;
 
 	num_shared = (int) csi.num_shared;
 	do {
@@ -168,301 +133,95 @@ static void cache_shared_cpu_map_setup(unsigned int cpu,
 
 		i++;
 	} while (i < num_shared &&
-		ia64_pal_cache_shared_info(this_leaf->level,
-				this_leaf->type,
-				i,
-				&csi) == PAL_STATUS_SUCCESS);
-}
-#else
-static void cache_shared_cpu_map_setup(unsigned int cpu,
-		struct cache_info * this_leaf)
-{
-	cpu_set(cpu, this_leaf->shared_cpu_map);
-	return;
-}
-#endif
-
-static ssize_t show_coherency_line_size(struct cache_info *this_leaf,
-					char *buf)
-{
-	return sprintf(buf, "%u\n", 1 << this_leaf->cci.pcci_line_size);
-}
-
-static ssize_t show_ways_of_associativity(struct cache_info *this_leaf,
-					char *buf)
-{
-	return sprintf(buf, "%u\n", this_leaf->cci.pcci_assoc);
-}
-
-static ssize_t show_attributes(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf,
-			"%s\n",
-			cache_mattrib[this_leaf->cci.pcci_cache_attr]);
-}
-
-static ssize_t show_size(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf, "%uK\n", this_leaf->cci.pcci_cache_size / 1024);
-}
-
-static ssize_t show_number_of_sets(struct cache_info *this_leaf, char *buf)
-{
-	unsigned number_of_sets = this_leaf->cci.pcci_cache_size;
-	number_of_sets /= this_leaf->cci.pcci_assoc;
-	number_of_sets /= 1 << this_leaf->cci.pcci_line_size;
-
-	return sprintf(buf, "%u\n", number_of_sets);
-}
-
-static ssize_t show_shared_cpu_map(struct cache_info *this_leaf, char *buf)
-{
-	ssize_t	len;
-	cpumask_t shared_cpu_map;
-
-	cpumask_and(&shared_cpu_map,
-				&this_leaf->shared_cpu_map, cpu_online_mask);
-	len = cpumask_scnprintf(buf, NR_CPUS+1, &shared_cpu_map);
-	len += sprintf(buf+len, "\n");
-	return len;
-}
-
-static ssize_t show_type(struct cache_info *this_leaf, char *buf)
-{
-	int type = this_leaf->type + this_leaf->cci.pcci_unified;
-	return sprintf(buf, "%s\n", cache_types[type]);
-}
-
-static ssize_t show_level(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf, "%u\n", this_leaf->level);
+		 ia64_pal_cache_shared_info(this_leaf->level, type, i,
+					    &csi) == PAL_STATUS_SUCCESS);
+	return 0;
 }
 
-struct cache_attr {
-	struct attribute attr;
-	ssize_t (*show)(struct cache_info *, char *);
-	ssize_t (*store)(struct cache_info *, const char *, size_t count);
-};
-
-#ifdef define_one_ro
-	#undef define_one_ro
-#endif
-#define define_one_ro(_name) \
-	static struct cache_attr _name = \
-__ATTR(_name, 0444, show_##_name, NULL)
-
-define_one_ro(level);
-define_one_ro(type);
-define_one_ro(coherency_line_size);
-define_one_ro(ways_of_associativity);
-define_one_ro(size);
-define_one_ro(number_of_sets);
-define_one_ro(shared_cpu_map);
-define_one_ro(attributes);
-
-static struct attribute * cache_default_attrs[] = {
-	&type.attr,
-	&level.attr,
-	&coherency_line_size.attr,
-	&ways_of_associativity.attr,
-	&attributes.attr,
-	&size.attr,
-	&number_of_sets.attr,
-	&shared_cpu_map.attr,
-	NULL
-};
-
-#define to_object(k) container_of(k, struct cache_info, kobj)
-#define to_attr(a) container_of(a, struct cache_attr, attr)
-
-static ssize_t ia64_cache_show(struct kobject * kobj, struct attribute * attr, char * buf)
+static int cache_shared_cpu_map_setup(unsigned int cpu)
 {
-	struct cache_attr *fattr = to_attr(attr);
-	struct cache_info *this_leaf = to_object(kobj);
-	ssize_t ret;
-
-	ret = fattr->show ? fattr->show(this_leaf, buf) : 0;
+	unsigned int idx;
+	int ret = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++, this_leaf++) {
+		ret = __cache_cpumap_setup(cpu, this_leaf);
+		if (ret)
+			break;
+	}
 	return ret;
 }
-
-static const struct sysfs_ops cache_sysfs_ops = {
-	.show   = ia64_cache_show
-};
-
-static struct kobj_type cache_ktype = {
-	.sysfs_ops	= &cache_sysfs_ops,
-	.default_attrs	= cache_default_attrs,
-};
-
-static struct kobj_type cache_ktype_percpu_entry = {
-	.sysfs_ops	= &cache_sysfs_ops,
-};
-
-static void cpu_cache_sysfs_exit(unsigned int cpu)
-{
-	kfree(all_cpu_cache_info[cpu].cache_leaves);
-	all_cpu_cache_info[cpu].cache_leaves = NULL;
-	all_cpu_cache_info[cpu].num_cache_leaves = 0;
-	memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject));
-	return;
-}
-
-static int cpu_cache_sysfs_init(unsigned int cpu)
+#else
+static int cache_shared_cpu_map_setup(unsigned int cpu)
 {
-	unsigned long i, levels, unique_caches;
-	pal_cache_config_info_t cci;
-	int j;
-	long status;
-	struct cache_info *this_cache;
-	int num_cache_leaves = 0;
-
-	if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
-		printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
-		return -1;
-	}
-
-	this_cache=kzalloc(sizeof(struct cache_info)*unique_caches,
-			GFP_KERNEL);
-	if (this_cache == NULL)
-		return -ENOMEM;
-
-	for (i=0; i < levels; i++) {
-		for (j=2; j >0 ; j--) {
-			if ((status=ia64_pal_cache_config_info(i,j, &cci)) !=
-					PAL_STATUS_SUCCESS)
-				continue;
-
-			this_cache[num_cache_leaves].cci = cci;
-			this_cache[num_cache_leaves].level = i + 1;
-			this_cache[num_cache_leaves].type = j;
-
-			cache_shared_cpu_map_setup(cpu,
-					&this_cache[num_cache_leaves]);
-			num_cache_leaves ++;
-		}
-	}
-
-	all_cpu_cache_info[cpu].cache_leaves = this_cache;
-	all_cpu_cache_info[cpu].num_cache_leaves = num_cache_leaves;
-
-	memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject));
-
+	int idx;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++, this_leaf++)
+		cpu_set(cpu, this_leaf->shared_cpu_map);
 	return 0;
 }
+#endif
 
-/* Add cache interface for CPU device */
-static int cache_add_dev(struct device *sys_dev)
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 pal_cache_config_info_t *cci,
+			 enum cache_type type, unsigned int level)
 {
-	unsigned int cpu = sys_dev->id;
-	unsigned long i, j;
-	struct cache_info *this_object;
-	int retval = 0;
-	cpumask_t oldmask;
-
-	if (all_cpu_cache_info[cpu].kobj.parent)
-		return 0;
-
-	oldmask = current->cpus_allowed;
-	retval = set_cpus_allowed_ptr(current, cpumask_of(cpu));
-	if (unlikely(retval))
-		return retval;
-
-	retval = cpu_cache_sysfs_init(cpu);
-	set_cpus_allowed_ptr(current, &oldmask);
-	if (unlikely(retval < 0))
-		return retval;
+	unsigned number_of_sets = cci->pcci_cache_size;
+	number_of_sets /= cci->pcci_assoc;
+	number_of_sets /= 1 << cci->pcci_line_size;
 
-	retval = kobject_init_and_add(&all_cpu_cache_info[cpu].kobj,
-				      &cache_ktype_percpu_entry, &sys_dev->kobj,
-				      "%s", "cache");
-	if (unlikely(retval < 0)) {
-		cpu_cache_sysfs_exit(cpu);
-		return retval;
-	}
-
-	for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) {
-		this_object = LEAF_KOBJECT_PTR(cpu,i);
-		retval = kobject_init_and_add(&(this_object->kobj),
-					      &cache_ktype,
-					      &all_cpu_cache_info[cpu].kobj,
-					      "index%1lu", i);
-		if (unlikely(retval)) {
-			for (j = 0; j < i; j++) {
-				kobject_put(&(LEAF_KOBJECT_PTR(cpu,j)->kobj));
-			}
-			kobject_put(&all_cpu_cache_info[cpu].kobj);
-			cpu_cache_sysfs_exit(cpu);
-			return retval;
-		}
-		kobject_uevent(&(this_object->kobj), KOBJ_ADD);
-	}
-	kobject_uevent(&all_cpu_cache_info[cpu].kobj, KOBJ_ADD);
-	return retval;
+	this_leaf->level = level;
+	this_leaf->type = cci->pcci_unified ? CACHE_TYPE_UNIFIED : type;
+	this_leaf->coherency_line_size = cci->pcci_line_size;
+	this_leaf->ways_of_associativity = cci->pcci_assoc;
+	this_leaf->size = cci->pcci_cache_size;
+	this_leaf->attributes = cci->pcci_cache_attr;
+	this_leaf->number_of_sets = number_of_sets;
 }
 
-/* Remove cache interface for CPU device */
-static int cache_remove_dev(struct device *sys_dev)
+int init_cache_level(unsigned int cpu)
 {
-	unsigned int cpu = sys_dev->id;
-	unsigned long i;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned long levels, unique_caches;
+	long status;
 
-	for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++)
-		kobject_put(&(LEAF_KOBJECT_PTR(cpu,i)->kobj));
+	if (!this_cpu_ci)
+		return -EINVAL;
 
-	if (all_cpu_cache_info[cpu].kobj.parent) {
-		kobject_put(&all_cpu_cache_info[cpu].kobj);
-		memset(&all_cpu_cache_info[cpu].kobj,
-			0,
-			sizeof(struct kobject));
+	status = ia64_pal_cache_summary(&levels, &unique_caches);
+	if (status != PAL_STATUS_SUCCESS) {
+		pr_err("ia64_pal_cache_summary = %ld\n", status);
+		return -EIO;
 	}
-
-	cpu_cache_sysfs_exit(cpu);
+	this_cpu_ci->num_levels = levels;
+	this_cpu_ci->num_leaves = unique_caches;
 
 	return 0;
 }
 
-/*
- * When a cpu is hot-plugged, do a check and initiate
- * cache kobject if necessary
- */
-static int cache_cpu_callback(struct notifier_block *nfb,
-		unsigned long action, void *hcpu)
-{
-	unsigned int cpu = (unsigned long)hcpu;
-	struct device *sys_dev;
-
-	sys_dev = get_cpu_device(cpu);
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		cache_add_dev(sys_dev);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		cache_remove_dev(sys_dev);
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block cache_cpu_notifier =
+int populate_cache_leaves(unsigned int cpu)
 {
-	.notifier_call = cache_cpu_callback
-};
-
-static int __init cache_sysfs_init(void)
-{
-	int i;
-
-	for_each_online_cpu(i) {
-		struct device *sys_dev = get_cpu_device((unsigned int)i);
-		cache_add_dev(sys_dev);
+	unsigned int level, idx;
+	s64 status;
+	pal_cache_config_info_t cci;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = CACHE_TYPE_INST;
+		status = ia64_pal_cache_config_info(level - 1, type, &cci);
+		if (status == PAL_STATUS_SUCCESS)
+			ci_leaf_init(this_leaf++, &cci, type, level);
+		type = CACHE_TYPE_DATA;
+		status = ia64_pal_cache_config_info(level - 1, type, &cci);
+		if (status == PAL_STATUS_SUCCESS)
+			ci_leaf_init(this_leaf++, &cci, type, level);
 	}
-
-	register_hotcpu_notifier(&cache_cpu_notifier);
-
-	return 0;
+	return cache_shared_cpu_map_setup(cpu);
 }
-
-device_initcall(cache_sysfs_init);
-
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 2/8] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure
@ 2014-02-07 16:49   ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: sudeep.holla, Tony Luck, Fenghua Yu, linux-ia64

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: linux-ia64@vger.kernel.org
---
 arch/ia64/kernel/topology.c | 399 +++++++++-----------------------------------
 1 file changed, 79 insertions(+), 320 deletions(-)

diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index ca69a5a..78cdeda 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -13,6 +13,7 @@
  *	Populate cpu cache entries in sysfs for cpu cache info
  */
 
+#include <linux/cacheinfo.h>
 #include <linux/cpu.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -21,7 +22,6 @@
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/nodemask.h>
-#include <linux/notifier.h>
 #include <linux/export.h>
 #include <asm/mmzone.h>
 #include <asm/numa.h>
@@ -103,60 +103,25 @@ subsys_initcall(topology_init);
 /*
  * Export cpu cache information through sysfs
  */
-
-/*
- *  A bunch of string array to get pretty printing
- */
-static const char *cache_types[] = {
-	"",			/* not used */
-	"Instruction",
-	"Data",
-	"Unified"	/* unified */
-};
-
-static const char *cache_mattrib[]={
-	"WriteThrough",
-	"WriteBack",
-	"",		/* reserved */
-	""		/* reserved */
-};
-
-struct cache_info {
-	pal_cache_config_info_t	cci;
-	cpumask_t shared_cpu_map;
-	int level;
-	int type;
-	struct kobject kobj;
-};
-
-struct cpu_cache_info {
-	struct cache_info *cache_leaves;
-	int	num_cache_leaves;
-	struct kobject kobj;
-};
-
-static struct cpu_cache_info	all_cpu_cache_info[NR_CPUS];
-#define LEAF_KOBJECT_PTR(x,y)    (&all_cpu_cache_info[x].cache_leaves[y])
-
 #ifdef CONFIG_SMP
-static void cache_shared_cpu_map_setup(unsigned int cpu,
-		struct cache_info * this_leaf)
+static int __cache_cpumap_setup(unsigned int cpu, struct cache_info *this_leaf)
 {
 	pal_cache_shared_info_t	csi;
-	int num_shared, i = 0;
-	unsigned int j;
+	int num_shared, i = 0, j;
+	enum cache_type type = this_leaf->type;
 
 	if (cpu_data(cpu)->threads_per_core <= 1 &&
 		cpu_data(cpu)->cores_per_socket <= 1) {
 		cpu_set(cpu, this_leaf->shared_cpu_map);
-		return;
+		return 0;
 	}
 
-	if (ia64_pal_cache_shared_info(this_leaf->level,
-					this_leaf->type,
-					0,
-					&csi) != PAL_STATUS_SUCCESS)
-		return;
+	if (type = CACHE_TYPE_UNIFIED)
+		type = CACHE_TYPE_DATA;
+
+	if (ia64_pal_cache_shared_info(this_leaf->level, type, 0,
+				       &csi) != PAL_STATUS_SUCCESS)
+		return -EIO;
 
 	num_shared = (int) csi.num_shared;
 	do {
@@ -168,301 +133,95 @@ static void cache_shared_cpu_map_setup(unsigned int cpu,
 
 		i++;
 	} while (i < num_shared &&
-		ia64_pal_cache_shared_info(this_leaf->level,
-				this_leaf->type,
-				i,
-				&csi) = PAL_STATUS_SUCCESS);
-}
-#else
-static void cache_shared_cpu_map_setup(unsigned int cpu,
-		struct cache_info * this_leaf)
-{
-	cpu_set(cpu, this_leaf->shared_cpu_map);
-	return;
-}
-#endif
-
-static ssize_t show_coherency_line_size(struct cache_info *this_leaf,
-					char *buf)
-{
-	return sprintf(buf, "%u\n", 1 << this_leaf->cci.pcci_line_size);
-}
-
-static ssize_t show_ways_of_associativity(struct cache_info *this_leaf,
-					char *buf)
-{
-	return sprintf(buf, "%u\n", this_leaf->cci.pcci_assoc);
-}
-
-static ssize_t show_attributes(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf,
-			"%s\n",
-			cache_mattrib[this_leaf->cci.pcci_cache_attr]);
-}
-
-static ssize_t show_size(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf, "%uK\n", this_leaf->cci.pcci_cache_size / 1024);
-}
-
-static ssize_t show_number_of_sets(struct cache_info *this_leaf, char *buf)
-{
-	unsigned number_of_sets = this_leaf->cci.pcci_cache_size;
-	number_of_sets /= this_leaf->cci.pcci_assoc;
-	number_of_sets /= 1 << this_leaf->cci.pcci_line_size;
-
-	return sprintf(buf, "%u\n", number_of_sets);
-}
-
-static ssize_t show_shared_cpu_map(struct cache_info *this_leaf, char *buf)
-{
-	ssize_t	len;
-	cpumask_t shared_cpu_map;
-
-	cpumask_and(&shared_cpu_map,
-				&this_leaf->shared_cpu_map, cpu_online_mask);
-	len = cpumask_scnprintf(buf, NR_CPUS+1, &shared_cpu_map);
-	len += sprintf(buf+len, "\n");
-	return len;
-}
-
-static ssize_t show_type(struct cache_info *this_leaf, char *buf)
-{
-	int type = this_leaf->type + this_leaf->cci.pcci_unified;
-	return sprintf(buf, "%s\n", cache_types[type]);
-}
-
-static ssize_t show_level(struct cache_info *this_leaf, char *buf)
-{
-	return sprintf(buf, "%u\n", this_leaf->level);
+		 ia64_pal_cache_shared_info(this_leaf->level, type, i,
+					    &csi) = PAL_STATUS_SUCCESS);
+	return 0;
 }
 
-struct cache_attr {
-	struct attribute attr;
-	ssize_t (*show)(struct cache_info *, char *);
-	ssize_t (*store)(struct cache_info *, const char *, size_t count);
-};
-
-#ifdef define_one_ro
-	#undef define_one_ro
-#endif
-#define define_one_ro(_name) \
-	static struct cache_attr _name = \
-__ATTR(_name, 0444, show_##_name, NULL)
-
-define_one_ro(level);
-define_one_ro(type);
-define_one_ro(coherency_line_size);
-define_one_ro(ways_of_associativity);
-define_one_ro(size);
-define_one_ro(number_of_sets);
-define_one_ro(shared_cpu_map);
-define_one_ro(attributes);
-
-static struct attribute * cache_default_attrs[] = {
-	&type.attr,
-	&level.attr,
-	&coherency_line_size.attr,
-	&ways_of_associativity.attr,
-	&attributes.attr,
-	&size.attr,
-	&number_of_sets.attr,
-	&shared_cpu_map.attr,
-	NULL
-};
-
-#define to_object(k) container_of(k, struct cache_info, kobj)
-#define to_attr(a) container_of(a, struct cache_attr, attr)
-
-static ssize_t ia64_cache_show(struct kobject * kobj, struct attribute * attr, char * buf)
+static int cache_shared_cpu_map_setup(unsigned int cpu)
 {
-	struct cache_attr *fattr = to_attr(attr);
-	struct cache_info *this_leaf = to_object(kobj);
-	ssize_t ret;
-
-	ret = fattr->show ? fattr->show(this_leaf, buf) : 0;
+	unsigned int idx;
+	int ret = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++, this_leaf++) {
+		ret = __cache_cpumap_setup(cpu, this_leaf);
+		if (ret)
+			break;
+	}
 	return ret;
 }
-
-static const struct sysfs_ops cache_sysfs_ops = {
-	.show   = ia64_cache_show
-};
-
-static struct kobj_type cache_ktype = {
-	.sysfs_ops	= &cache_sysfs_ops,
-	.default_attrs	= cache_default_attrs,
-};
-
-static struct kobj_type cache_ktype_percpu_entry = {
-	.sysfs_ops	= &cache_sysfs_ops,
-};
-
-static void cpu_cache_sysfs_exit(unsigned int cpu)
-{
-	kfree(all_cpu_cache_info[cpu].cache_leaves);
-	all_cpu_cache_info[cpu].cache_leaves = NULL;
-	all_cpu_cache_info[cpu].num_cache_leaves = 0;
-	memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject));
-	return;
-}
-
-static int cpu_cache_sysfs_init(unsigned int cpu)
+#else
+static int cache_shared_cpu_map_setup(unsigned int cpu)
 {
-	unsigned long i, levels, unique_caches;
-	pal_cache_config_info_t cci;
-	int j;
-	long status;
-	struct cache_info *this_cache;
-	int num_cache_leaves = 0;
-
-	if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
-		printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
-		return -1;
-	}
-
-	this_cache=kzalloc(sizeof(struct cache_info)*unique_caches,
-			GFP_KERNEL);
-	if (this_cache = NULL)
-		return -ENOMEM;
-
-	for (i=0; i < levels; i++) {
-		for (j=2; j >0 ; j--) {
-			if ((status=ia64_pal_cache_config_info(i,j, &cci)) !-					PAL_STATUS_SUCCESS)
-				continue;
-
-			this_cache[num_cache_leaves].cci = cci;
-			this_cache[num_cache_leaves].level = i + 1;
-			this_cache[num_cache_leaves].type = j;
-
-			cache_shared_cpu_map_setup(cpu,
-					&this_cache[num_cache_leaves]);
-			num_cache_leaves ++;
-		}
-	}
-
-	all_cpu_cache_info[cpu].cache_leaves = this_cache;
-	all_cpu_cache_info[cpu].num_cache_leaves = num_cache_leaves;
-
-	memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject));
-
+	int idx;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++, this_leaf++)
+		cpu_set(cpu, this_leaf->shared_cpu_map);
 	return 0;
 }
+#endif
 
-/* Add cache interface for CPU device */
-static int cache_add_dev(struct device *sys_dev)
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 pal_cache_config_info_t *cci,
+			 enum cache_type type, unsigned int level)
 {
-	unsigned int cpu = sys_dev->id;
-	unsigned long i, j;
-	struct cache_info *this_object;
-	int retval = 0;
-	cpumask_t oldmask;
-
-	if (all_cpu_cache_info[cpu].kobj.parent)
-		return 0;
-
-	oldmask = current->cpus_allowed;
-	retval = set_cpus_allowed_ptr(current, cpumask_of(cpu));
-	if (unlikely(retval))
-		return retval;
-
-	retval = cpu_cache_sysfs_init(cpu);
-	set_cpus_allowed_ptr(current, &oldmask);
-	if (unlikely(retval < 0))
-		return retval;
+	unsigned number_of_sets = cci->pcci_cache_size;
+	number_of_sets /= cci->pcci_assoc;
+	number_of_sets /= 1 << cci->pcci_line_size;
 
-	retval = kobject_init_and_add(&all_cpu_cache_info[cpu].kobj,
-				      &cache_ktype_percpu_entry, &sys_dev->kobj,
-				      "%s", "cache");
-	if (unlikely(retval < 0)) {
-		cpu_cache_sysfs_exit(cpu);
-		return retval;
-	}
-
-	for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) {
-		this_object = LEAF_KOBJECT_PTR(cpu,i);
-		retval = kobject_init_and_add(&(this_object->kobj),
-					      &cache_ktype,
-					      &all_cpu_cache_info[cpu].kobj,
-					      "index%1lu", i);
-		if (unlikely(retval)) {
-			for (j = 0; j < i; j++) {
-				kobject_put(&(LEAF_KOBJECT_PTR(cpu,j)->kobj));
-			}
-			kobject_put(&all_cpu_cache_info[cpu].kobj);
-			cpu_cache_sysfs_exit(cpu);
-			return retval;
-		}
-		kobject_uevent(&(this_object->kobj), KOBJ_ADD);
-	}
-	kobject_uevent(&all_cpu_cache_info[cpu].kobj, KOBJ_ADD);
-	return retval;
+	this_leaf->level = level;
+	this_leaf->type = cci->pcci_unified ? CACHE_TYPE_UNIFIED : type;
+	this_leaf->coherency_line_size = cci->pcci_line_size;
+	this_leaf->ways_of_associativity = cci->pcci_assoc;
+	this_leaf->size = cci->pcci_cache_size;
+	this_leaf->attributes = cci->pcci_cache_attr;
+	this_leaf->number_of_sets = number_of_sets;
 }
 
-/* Remove cache interface for CPU device */
-static int cache_remove_dev(struct device *sys_dev)
+int init_cache_level(unsigned int cpu)
 {
-	unsigned int cpu = sys_dev->id;
-	unsigned long i;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned long levels, unique_caches;
+	long status;
 
-	for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++)
-		kobject_put(&(LEAF_KOBJECT_PTR(cpu,i)->kobj));
+	if (!this_cpu_ci)
+		return -EINVAL;
 
-	if (all_cpu_cache_info[cpu].kobj.parent) {
-		kobject_put(&all_cpu_cache_info[cpu].kobj);
-		memset(&all_cpu_cache_info[cpu].kobj,
-			0,
-			sizeof(struct kobject));
+	status = ia64_pal_cache_summary(&levels, &unique_caches);
+	if (status != PAL_STATUS_SUCCESS) {
+		pr_err("ia64_pal_cache_summary = %ld\n", status);
+		return -EIO;
 	}
-
-	cpu_cache_sysfs_exit(cpu);
+	this_cpu_ci->num_levels = levels;
+	this_cpu_ci->num_leaves = unique_caches;
 
 	return 0;
 }
 
-/*
- * When a cpu is hot-plugged, do a check and initiate
- * cache kobject if necessary
- */
-static int cache_cpu_callback(struct notifier_block *nfb,
-		unsigned long action, void *hcpu)
-{
-	unsigned int cpu = (unsigned long)hcpu;
-	struct device *sys_dev;
-
-	sys_dev = get_cpu_device(cpu);
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		cache_add_dev(sys_dev);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		cache_remove_dev(sys_dev);
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block cache_cpu_notifier +int populate_cache_leaves(unsigned int cpu)
 {
-	.notifier_call = cache_cpu_callback
-};
-
-static int __init cache_sysfs_init(void)
-{
-	int i;
-
-	for_each_online_cpu(i) {
-		struct device *sys_dev = get_cpu_device((unsigned int)i);
-		cache_add_dev(sys_dev);
+	unsigned int level, idx;
+	s64 status;
+	pal_cache_config_info_t cci;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = CACHE_TYPE_INST;
+		status = ia64_pal_cache_config_info(level - 1, type, &cci);
+		if (status = PAL_STATUS_SUCCESS)
+			ci_leaf_init(this_leaf++, &cci, type, level);
+		type = CACHE_TYPE_DATA;
+		status = ia64_pal_cache_config_info(level - 1, type, &cci);
+		if (status = PAL_STATUS_SUCCESS)
+			ci_leaf_init(this_leaf++, &cci, type, level);
 	}
-
-	register_hotcpu_notifier(&cache_cpu_notifier);
-
-	return 0;
+	return cache_shared_cpu_map_setup(cpu);
 }
-
-device_initcall(cache_sysfs_init);
-
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
@ 2014-02-07 16:49 ` Sudeep Holla
  2014-02-10  9:50   ` Heiko Carstens
  2014-02-10 11:30   ` Heiko Carstens
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 4/8] x86: " Sudeep Holla
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: sudeep.holla, Martin Schwidefsky, Heiko Carstens, linux390, linux-s390

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: linux390@de.ibm.com
Cc: linux-s390@vger.kernel.org
---
 arch/s390/include/asm/processor.h |   6 -
 arch/s390/kernel/cache.c          | 380 ++++++++------------------------------
 arch/s390/kernel/processor.c      |   1 -
 3 files changed, 76 insertions(+), 311 deletions(-)

diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 0a876bc..84addf4 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -153,12 +153,6 @@ struct task_struct;
 struct mm_struct;
 struct seq_file;
 
-#ifdef CONFIG_64BIT
-extern void show_cacheinfo(struct seq_file *m);
-#else
-static inline void show_cacheinfo(struct seq_file *m) { }
-#endif
-
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c
index 3a414c0..e55dcd4 100644
--- a/arch/s390/kernel/cache.c
+++ b/arch/s390/kernel/cache.c
@@ -5,37 +5,11 @@
  *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
  */
 
-#include <linux/notifier.h>
 #include <linux/seq_file.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/slab.h>
 #include <linux/cpu.h>
+#include <linux/cacheinfo.h>
 #include <asm/facility.h>
 
-struct cache {
-	unsigned long size;
-	unsigned int line_size;
-	unsigned int associativity;
-	unsigned int nr_sets;
-	unsigned int level   : 3;
-	unsigned int type    : 2;
-	unsigned int private : 1;
-	struct list_head list;
-};
-
-struct cache_dir {
-	struct kobject *kobj;
-	struct cache_index_dir *index;
-};
-
-struct cache_index_dir {
-	struct kobject kobj;
-	int cpu;
-	struct cache *cache;
-	struct cache_index_dir *next;
-};
-
 enum {
 	CACHE_SCOPE_NOTEXISTS,
 	CACHE_SCOPE_PRIVATE,
@@ -44,10 +18,10 @@ enum {
 };
 
 enum {
-	CACHE_TYPE_SEPARATE,
-	CACHE_TYPE_DATA,
-	CACHE_TYPE_INSTRUCTION,
-	CACHE_TYPE_UNIFIED,
+	CTYPE_SEPARATE,
+	CTYPE_DATA,
+	CTYPE_INSTRUCTION,
+	CTYPE_UNIFIED,
 };
 
 enum {
@@ -63,44 +37,36 @@ enum {
 	CACHE_TI_INSTRUCTION,
 };
 
-struct cache_info {
+struct _cacheinfo {
 	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];
+	struct _cacheinfo ci[CACHE_MAX_LEVEL];
 	unsigned long long raw;
 };
 
-static const char * const cache_type_string[] = {
-	"Data",
-	"Instruction",
-	"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,
 };
 
-static struct cache_dir *cache_dir_cpu[NR_CPUS];
-static LIST_HEAD(cache_list);
-
-void show_cacheinfo(struct seq_file *m)
+static inline enum cache_type get_cache_type(struct _cacheinfo *ci, int level)
 {
-	struct cache *cache;
-	int index = 0;
+	if (level >= CACHE_MAX_LEVEL)
+		return CACHE_TYPE_NOCACHE;
 
-	list_for_each_entry(cache, &cache_list, list) {
-		seq_printf(m, "cache%-11d: ", index);
-		seq_printf(m, "level=%d ", cache->level);
-		seq_printf(m, "type=%s ", cache_type_string[cache->type]);
-		seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared");
-		seq_printf(m, "size=%luK ", cache->size >> 10);
-		seq_printf(m, "line_size=%u ", cache->line_size);
-		seq_printf(m, "associativity=%d", cache->associativity);
-		seq_puts(m, "\n");
-		index++;
-	}
+	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)
@@ -113,274 +79,80 @@ static inline unsigned long ecag(int ai, int li, int ti)
 	return val;
 }
 
-static int __init cache_add(int level, int private, int type)
+static void ci_leaf_init(struct cache_info *this_leaf, int private,
+			 enum cache_type type, unsigned int level)
 {
-	struct cache *cache;
-	int ti;
+	int ti, num_sets;
+	int cpu = smp_processor_id();
 
-	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
-	if (!cache)
-		return -ENOMEM;
-	if (type == CACHE_TYPE_INSTRUCTION)
+	if (type == CACHE_TYPE_INST)
 		ti = CACHE_TI_INSTRUCTION;
 	else
 		ti = CACHE_TI_UNIFIED;
-	cache->size = ecag(EXTRACT_SIZE, level, ti);
-	cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti);
-	cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti);
-	cache->nr_sets = cache->size / cache->associativity;
-	cache->nr_sets /= cache->line_size;
-	cache->private = private;
-	cache->level = level + 1;
-	cache->type = type - 1;
-	list_add_tail(&cache->list, &cache_list);
-	return 0;
-}
-
-static void __init cache_build_info(void)
-{
-	struct cache *cache, *next;
-	union cache_topology ct;
-	int level, private, rc;
-
-	ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
-	for (level = 0; level < CACHE_MAX_LEVEL; level++) {
-		switch (ct.ci[level].scope) {
-		case CACHE_SCOPE_SHARED:
-			private = 0;
-			break;
-		case CACHE_SCOPE_PRIVATE:
-			private = 1;
-			break;
-		default:
-			return;
-		}
-		if (ct.ci[level].type == CACHE_TYPE_SEPARATE) {
-			rc  = cache_add(level, private, CACHE_TYPE_DATA);
-			rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION);
-		} else {
-			rc = cache_add(level, private, ct.ci[level].type);
-		}
-		if (rc)
-			goto error;
-	}
-	return;
-error:
-	list_for_each_entry_safe(cache, next, &cache_list, list) {
-		list_del(&cache->list);
-		kfree(cache);
-	}
-}
-
-static struct cache_dir *cache_create_cache_dir(int cpu)
-{
-	struct cache_dir *cache_dir;
-	struct kobject *kobj = NULL;
-	struct device *dev;
-
-	dev = get_cpu_device(cpu);
-	if (!dev)
-		goto out;
-	kobj = kobject_create_and_add("cache", &dev->kobj);
-	if (!kobj)
-		goto out;
-	cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL);
-	if (!cache_dir)
-		goto out;
-	cache_dir->kobj = kobj;
-	cache_dir_cpu[cpu] = cache_dir;
-	return cache_dir;
-out:
-	kobject_put(kobj);
-	return NULL;
-}
-
-static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj)
-{
-	return container_of(kobj, struct cache_index_dir, kobj);
-}
-
-static void cache_index_release(struct kobject *kobj)
-{
-	struct cache_index_dir *index;
-
-	index = kobj_to_cache_index_dir(kobj);
-	kfree(index);
-}
-
-static ssize_t cache_index_show(struct kobject *kobj,
-				struct attribute *attr, char *buf)
-{
-	struct kobj_attribute *kobj_attr;
-
-	kobj_attr = container_of(attr, struct kobj_attribute, attr);
-	return kobj_attr->show(kobj, kobj_attr, buf);
-}
-
-#define DEFINE_CACHE_ATTR(_name, _format, _value)			\
-static ssize_t cache_##_name##_show(struct kobject *kobj,		\
-				    struct kobj_attribute *attr,	\
-				    char *buf)				\
-{									\
-	struct cache_index_dir *index;					\
-									\
-	index = kobj_to_cache_index_dir(kobj);				\
-	return sprintf(buf, _format, _value);				\
-}									\
-static struct kobj_attribute cache_##_name##_attr =			\
-	__ATTR(_name, 0444, cache_##_name##_show, NULL);
-
-DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10);
-DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size);
-DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets);
-DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity);
-DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]);
-DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level);
 
-static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
-{
-	struct cache_index_dir *index;
-	int len;
+	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);
+	if (private)
+		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
+	else /* System wide */
+		cpumask_copy(&this_leaf->shared_cpu_map, cpu_online_mask);
 
-	index = kobj_to_cache_index_dir(kobj);
-	len = type ?
-		cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) :
-		cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu));
-	len += sprintf(&buf[len], "\n");
-	return len;
+	num_sets = this_leaf->size / this_leaf->coherency_line_size;
+	num_sets /= this_leaf->ways_of_associativity;
+	this_leaf->number_of_sets = num_sets;
 }
 
-static ssize_t shared_cpu_map_show(struct kobject *kobj,
-				   struct kobj_attribute *attr, char *buf)
+int init_cache_level(unsigned int cpu)
 {
-	return shared_cpu_map_func(kobj, 0, buf);
-}
-static struct kobj_attribute cache_shared_cpu_map_attr =
-	__ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
-
-static ssize_t shared_cpu_list_show(struct kobject *kobj,
-				    struct kobj_attribute *attr, char *buf)
-{
-	return shared_cpu_map_func(kobj, 1, buf);
-}
-static struct kobj_attribute cache_shared_cpu_list_attr =
-	__ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL);
-
-static struct attribute *cache_index_default_attrs[] = {
-	&cache_type_attr.attr,
-	&cache_size_attr.attr,
-	&cache_number_of_sets_attr.attr,
-	&cache_ways_of_associativity_attr.attr,
-	&cache_level_attr.attr,
-	&cache_coherency_line_size_attr.attr,
-	&cache_shared_cpu_map_attr.attr,
-	&cache_shared_cpu_list_attr.attr,
-	NULL,
-};
-
-static const struct sysfs_ops cache_index_ops = {
-	.show = cache_index_show,
-};
-
-static struct kobj_type cache_index_type = {
-	.sysfs_ops = &cache_index_ops,
-	.release = cache_index_release,
-	.default_attrs = cache_index_default_attrs,
-};
-
-static int cache_create_index_dir(struct cache_dir *cache_dir,
-				  struct cache *cache, int index, int cpu)
-{
-	struct cache_index_dir *index_dir;
-	int rc;
-
-	index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL);
-	if (!index_dir)
-		return -ENOMEM;
-	index_dir->cache = cache;
-	index_dir->cpu = cpu;
-	rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
-				  cache_dir->kobj, "index%d", index);
-	if (rc)
-		goto out;
-	index_dir->next = cache_dir->index;
-	cache_dir->index = index_dir;
-	return 0;
-out:
-	kfree(index_dir);
-	return rc;
-}
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned int level = 0, leaves = 0;
+	union cache_topology ct;
+	enum cache_type ctype;
 
-static int cache_add_cpu(int cpu)
-{
-	struct cache_dir *cache_dir;
-	struct cache *cache;
-	int rc, index = 0;
+	if (!this_cpu_ci)
+		return -EINVAL;
 
-	if (list_empty(&cache_list))
-		return 0;
-	cache_dir = cache_create_cache_dir(cpu);
-	if (!cache_dir)
-		return -ENOMEM;
-	list_for_each_entry(cache, &cache_list, list) {
-		if (!cache->private)
+	ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
+	do {
+		ctype = get_cache_type(&ct.ci[0], level);
+		if (ctype == CACHE_TYPE_NOCACHE)
 			break;
-		rc = cache_create_index_dir(cache_dir, cache, index, cpu);
-		if (rc)
-			return rc;
-		index++;
-	}
-	return 0;
-}
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level < CACHE_MAX_LEVEL);
 
-static void cache_remove_cpu(int cpu)
-{
-	struct cache_index_dir *index, *next;
-	struct cache_dir *cache_dir;
+	this_cpu_ci->num_levels = level;
+	this_cpu_ci->num_leaves = leaves;
 
-	cache_dir = cache_dir_cpu[cpu];
-	if (!cache_dir)
-		return;
-	index = cache_dir->index;
-	while (index) {
-		next = index->next;
-		kobject_put(&index->kobj);
-		index = next;
-	}
-	kobject_put(cache_dir->kobj);
-	kfree(cache_dir);
-	cache_dir_cpu[cpu] = NULL;
+	return 0;
 }
 
-static int cache_hotplug(struct notifier_block *nfb, unsigned long action,
-			 void *hcpu)
+int populate_cache_leaves(unsigned int cpu)
 {
-	int cpu = (long)hcpu;
-	int rc = 0;
+	unsigned int level, idx, pvt;
+	union cache_topology ct;
+	enum cache_type ctype;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
 
-	switch (action & ~CPU_TASKS_FROZEN) {
-	case CPU_ONLINE:
-		rc = cache_add_cpu(cpu);
-		if (rc)
-			cache_remove_cpu(cpu);
-		break;
-	case CPU_DEAD:
-		cache_remove_cpu(cpu);
-		break;
+	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);
+			ci_leaf_init(this_leaf++, pvt, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, pvt, ctype, level);
+		}
 	}
-	return rc ? NOTIFY_BAD : NOTIFY_OK;
-}
-
-static int __init cache_init(void)
-{
-	int cpu;
-
-	if (!test_facility(34))
-		return 0;
-	cache_build_info();
-	for_each_online_cpu(cpu)
-		cache_add_cpu(cpu);
-	hotcpu_notifier(cache_hotplug, 0);
 	return 0;
 }
-device_initcall(cache_init);
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 2461202..1f875db 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -58,7 +58,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
 			if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
 				seq_printf(m, "%s ", hwcap_str[i]);
 		seq_puts(m, "\n");
-		show_cacheinfo(m);
 	}
 	get_online_cpus();
 	if (cpu_online(n)) {
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 4/8] x86: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
                   ` (2 preceding siblings ...)
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 3/8] s390: " Sudeep Holla
@ 2014-02-07 16:49 ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: sudeep.holla, Thomas Gleixner, Ingo Molnar, x86

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

The private pointer provided by the cache_info is used to implement
the AMD L3 cache specific attributes.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: x86@kernel.org
---
 arch/x86/kernel/cpu/intel_cacheinfo.c | 628 ++++++++--------------------------
 1 file changed, 136 insertions(+), 492 deletions(-)

diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index 0641113..dca5619 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -7,16 +7,14 @@
  *	Andi Kleen / Andreas Herrmann	: CPUID4 emulation on AMD.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/compiler.h>
+#include <linux/cacheinfo.h>
 #include <linux/cpu.h>
 #include <linux/sched.h>
+#include <linux/sysfs.h>
 #include <linux/pci.h>
 
 #include <asm/processor.h>
-#include <linux/smp.h>
 #include <asm/amd_nb.h>
 #include <asm/smp.h>
 
@@ -116,10 +114,10 @@ static const struct _cache_table cache_table[] =
 
 
 enum _cache_type {
-	CACHE_TYPE_NULL	= 0,
-	CACHE_TYPE_DATA = 1,
-	CACHE_TYPE_INST = 2,
-	CACHE_TYPE_UNIFIED = 3
+	CTYPE_NULL = 0,
+	CTYPE_DATA = 1,
+	CTYPE_INST = 2,
+	CTYPE_UNIFIED = 3
 };
 
 union _cpuid4_leaf_eax {
@@ -159,12 +157,7 @@ struct _cpuid4_info_regs {
 	struct amd_northbridge *nb;
 };
 
-struct _cpuid4_info {
-	struct _cpuid4_info_regs base;
-	DECLARE_BITMAP(shared_cpu_map, NR_CPUS);
-};
-
-unsigned short			num_cache_leaves;
+unsigned short                 num_cache_leaves;
 
 /* AMD doesn't have CPUID4. Emulate it here to report the same
    information to the user.  This makes some assumptions about the machine:
@@ -291,14 +284,9 @@ amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
 		(ebx->split.ways_of_associativity + 1) - 1;
 }
 
-struct _cache_attr {
-	struct attribute attr;
-	ssize_t (*show)(struct _cpuid4_info *, char *, unsigned int);
-	ssize_t (*store)(struct _cpuid4_info *, const char *, size_t count,
-			 unsigned int);
-};
-
 #if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS)
+
+static struct attribute **amd_l3_attrs;
 /*
  * L3 cache descriptors
  */
@@ -325,6 +313,7 @@ static void amd_calc_l3_indices(struct amd_northbridge *nb)
 	l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1;
 }
 
+static void init_amd_l3_attrs(void);
 static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index)
 {
 	int node;
@@ -335,8 +324,10 @@ static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index)
 
 	node = amd_get_nb_id(smp_processor_id());
 	this_leaf->nb = node_to_amd_nb(node);
-	if (this_leaf->nb && !this_leaf->nb->l3_cache.indices)
+	if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) {
 		amd_calc_l3_indices(this_leaf->nb);
+		init_amd_l3_attrs();
+	}
 }
 
 /*
@@ -359,15 +350,16 @@ int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot)
 	return -1;
 }
 
-static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf,
+static ssize_t show_cache_disable(struct cache_info *this_leaf, char *buf,
 				  unsigned int slot)
 {
 	int index;
+	struct amd_northbridge *nb = this_leaf->priv;
 
-	if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
+	if (!nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
 		return -EINVAL;
 
-	index = amd_get_l3_disable_slot(this_leaf->base.nb, slot);
+	index = amd_get_l3_disable_slot(nb, slot);
 	if (index >= 0)
 		return sprintf(buf, "%d\n", index);
 
@@ -376,7 +368,7 @@ static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf,
 
 #define SHOW_CACHE_DISABLE(slot)					\
 static ssize_t								\
-show_cache_disable_##slot(struct _cpuid4_info *this_leaf, char *buf,	\
+cache_disable_##slot##_show(struct cache_info *this_leaf, char *buf,	\
 			  unsigned int cpu)				\
 {									\
 	return show_cache_disable(this_leaf, buf, slot);		\
@@ -446,25 +438,26 @@ int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu, unsigned slot,
 	return 0;
 }
 
-static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf,
+static ssize_t store_cache_disable(struct cache_info *this_leaf,
 				  const char *buf, size_t count,
 				  unsigned int slot)
 {
 	unsigned long val = 0;
 	int cpu, err = 0;
+	struct amd_northbridge *nb = this_leaf->priv;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
+	if (!nb || !amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
 		return -EINVAL;
 
-	cpu = cpumask_first(to_cpumask(this_leaf->shared_cpu_map));
+	cpu = cpumask_first(&this_leaf->shared_cpu_map);
 
 	if (strict_strtoul(buf, 10, &val) < 0)
 		return -EINVAL;
 
-	err = amd_set_l3_disable_slot(this_leaf->base.nb, cpu, slot, val);
+	err = amd_set_l3_disable_slot(nb, cpu, slot, val);
 	if (err) {
 		if (err == -EEXIST)
 			pr_warning("L3 slot %d in use/index already disabled!\n",
@@ -476,7 +469,7 @@ static ssize_t store_cache_disable(struct _cpuid4_info *this_leaf,
 
 #define STORE_CACHE_DISABLE(slot)					\
 static ssize_t								\
-store_cache_disable_##slot(struct _cpuid4_info *this_leaf,		\
+cache_disable_##slot##_store(struct cache_info *this_leaf,		\
 			   const char *buf, size_t count,		\
 			   unsigned int cpu)				\
 {									\
@@ -485,22 +478,17 @@ store_cache_disable_##slot(struct _cpuid4_info *this_leaf,		\
 STORE_CACHE_DISABLE(0)
 STORE_CACHE_DISABLE(1)
 
-static struct _cache_attr cache_disable_0 = __ATTR(cache_disable_0, 0644,
-		show_cache_disable_0, store_cache_disable_0);
-static struct _cache_attr cache_disable_1 = __ATTR(cache_disable_1, 0644,
-		show_cache_disable_1, store_cache_disable_1);
-
 static ssize_t
-show_subcaches(struct _cpuid4_info *this_leaf, char *buf, unsigned int cpu)
+subcaches_show(struct cache_info *this_leaf, char *buf, unsigned int cpu)
 {
-	if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+	if (!this_leaf->priv || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
 		return -EINVAL;
 
 	return sprintf(buf, "%x\n", amd_get_subcaches(cpu));
 }
 
 static ssize_t
-store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count,
+subcaches_store(struct cache_info *this_leaf, const char *buf, size_t count,
 		unsigned int cpu)
 {
 	unsigned long val;
@@ -508,7 +496,7 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count,
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+	if (!this_leaf->priv || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
 		return -EINVAL;
 
 	if (strict_strtoul(buf, 16, &val) < 0)
@@ -520,9 +508,49 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count,
 	return count;
 }
 
-static struct _cache_attr subcaches =
-	__ATTR(subcaches, 0644, show_subcaches, store_subcaches);
+#define define_one_rw(_name) \
+static struct cache_attr _name = __ATTR_RW(_name)
+
+define_one_rw(cache_disable_0);
+define_one_rw(cache_disable_1);
+define_one_rw(subcaches);
+
+static void init_amd_l3_attrs(void)
+{
+	int n = 0;
+
+	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
+		n += 2;
+	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+		n += 1;
+
+	amd_l3_attrs = kzalloc(n * sizeof(struct attribute *), GFP_KERNEL);
+	if (!amd_l3_attrs)
+		return;
+
+	n = 0;
+	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {
+		amd_l3_attrs[n++] = &cache_disable_0.attr;
+		amd_l3_attrs[n++] = &cache_disable_1.attr;
+	}
+	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+		amd_l3_attrs[n++] = &subcaches.attr;
+
+	return;
+}
+
+int cache_add_private_attributes(struct kobject *kobj, unsigned int cpu,
+				 unsigned short index)
+{
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list + index;
+
+	if (!this_leaf->priv || !amd_l3_attrs)
+		return 0;
 
+	return sysfs_create_files(kobj,
+				  (const struct attribute **)amd_l3_attrs);
+}
 #else
 #define amd_init_l3_cache(x, y)
 #endif  /* CONFIG_AMD_NB && CONFIG_SYSFS */
@@ -546,7 +574,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf)
 		cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
 	}
 
-	if (eax.split.type == CACHE_TYPE_NULL)
+	if (eax.split.type == CTYPE_NULL)
 		return -EIO; /* better error ? */
 
 	this_leaf->eax = eax;
@@ -575,7 +603,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c)
 		/* Do cpuid(op) loop to find out num_cache_leaves */
 		cpuid_count(op, i, &eax, &ebx, &ecx, &edx);
 		cache_eax.full = eax;
-	} while (cache_eax.split.type != CACHE_TYPE_NULL);
+	} while (cache_eax.split.type != CTYPE_NULL);
 	return i;
 }
 
@@ -626,9 +654,9 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
 
 			switch (this_leaf.eax.split.level) {
 			case 1:
-				if (this_leaf.eax.split.type == CACHE_TYPE_DATA)
+				if (this_leaf.eax.split.type == CTYPE_DATA)
 					new_l1d = this_leaf.size/1024;
-				else if (this_leaf.eax.split.type == CACHE_TYPE_INST)
+				else if (this_leaf.eax.split.type == CTYPE_INST)
 					new_l1i = this_leaf.size/1024;
 				break;
 			case 2:
@@ -735,55 +763,46 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	return l2;
 }
 
-#ifdef CONFIG_SYSFS
-
-/* pointer to _cpuid4_info array (for each cache leaf) */
-static DEFINE_PER_CPU(struct _cpuid4_info *, ici_cpuid4_info);
-#define CPUID4_INFO_IDX(x, y)	(&((per_cpu(ici_cpuid4_info, x))[y]))
-
-#ifdef CONFIG_SMP
-
-static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index)
+static int __cache_amd_cpumap_setup(unsigned int cpu, int index,
+				    struct _cpuid4_info_regs *base)
 {
-	struct _cpuid4_info *this_leaf;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf;
 	int i, sibling;
 
 	if (cpu_has_topoext) {
 		unsigned int apicid, nshared, first, last;
 
-		if (!per_cpu(ici_cpuid4_info, cpu))
-			return 0;
-
-		this_leaf = CPUID4_INFO_IDX(cpu, index);
-		nshared = this_leaf->base.eax.split.num_threads_sharing + 1;
+		this_leaf = this_cpu_ci->info_list + index;
+		nshared = base->eax.split.num_threads_sharing + 1;
 		apicid = cpu_data(cpu).apicid;
 		first = apicid - (apicid % nshared);
 		last = first + nshared - 1;
 
 		for_each_online_cpu(i) {
+			this_cpu_ci = get_cpu_cacheinfo(i);
 			apicid = cpu_data(i).apicid;
 			if ((apicid < first) || (apicid > last))
 				continue;
-			if (!per_cpu(ici_cpuid4_info, i))
-				continue;
-			this_leaf = CPUID4_INFO_IDX(i, index);
+			this_leaf = this_cpu_ci->info_list + index;
 
 			for_each_online_cpu(sibling) {
 				apicid = cpu_data(sibling).apicid;
 				if ((apicid < first) || (apicid > last))
 					continue;
-				set_bit(sibling, this_leaf->shared_cpu_map);
+				cpumask_set_cpu(sibling,
+						&this_leaf->shared_cpu_map);
 			}
 		}
 	} else if (index == 3) {
 		for_each_cpu(i, cpu_llc_shared_mask(cpu)) {
-			if (!per_cpu(ici_cpuid4_info, i))
-				continue;
-			this_leaf = CPUID4_INFO_IDX(i, index);
+			this_cpu_ci = get_cpu_cacheinfo(i);
+			this_leaf = this_cpu_ci->info_list + index;
 			for_each_cpu(sibling, cpu_llc_shared_mask(cpu)) {
 				if (!cpu_online(sibling))
 					continue;
-				set_bit(sibling, this_leaf->shared_cpu_map);
+				cpumask_set_cpu(sibling,
+						&this_leaf->shared_cpu_map);
 			}
 		}
 	} else
@@ -792,456 +811,81 @@ static int cache_shared_amd_cpu_map_setup(unsigned int cpu, int index)
 	return 1;
 }
 
-static void cache_shared_cpu_map_setup(unsigned int cpu, int index)
+static void __cache_cpumap_setup(unsigned int cpu, int index,
+				 struct _cpuid4_info_regs *base)
 {
-	struct _cpuid4_info *this_leaf, *sibling_leaf;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf, *sibling_leaf;
 	unsigned long num_threads_sharing;
 	int index_msb, i;
 	struct cpuinfo_x86 *c = &cpu_data(cpu);
 
 	if (c->x86_vendor == X86_VENDOR_AMD) {
-		if (cache_shared_amd_cpu_map_setup(cpu, index))
+		if (__cache_amd_cpumap_setup(cpu, index, base))
 			return;
 	}
 
-	this_leaf = CPUID4_INFO_IDX(cpu, index);
-	num_threads_sharing = 1 + this_leaf->base.eax.split.num_threads_sharing;
+	this_leaf = this_cpu_ci->info_list + index;
+	num_threads_sharing = 1 + base->eax.split.num_threads_sharing;
 
+	cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
 	if (num_threads_sharing == 1)
-		cpumask_set_cpu(cpu, to_cpumask(this_leaf->shared_cpu_map));
-	else {
-		index_msb = get_count_order(num_threads_sharing);
-
-		for_each_online_cpu(i) {
-			if (cpu_data(i).apicid >> index_msb ==
-			    c->apicid >> index_msb) {
-				cpumask_set_cpu(i,
-					to_cpumask(this_leaf->shared_cpu_map));
-				if (i != cpu && per_cpu(ici_cpuid4_info, i))  {
-					sibling_leaf =
-						CPUID4_INFO_IDX(i, index);
-					cpumask_set_cpu(cpu, to_cpumask(
-						sibling_leaf->shared_cpu_map));
-				}
-			}
-		}
-	}
-}
-static void cache_remove_shared_cpu_map(unsigned int cpu, int index)
-{
-	struct _cpuid4_info	*this_leaf, *sibling_leaf;
-	int sibling;
-
-	this_leaf = CPUID4_INFO_IDX(cpu, index);
-	for_each_cpu(sibling, to_cpumask(this_leaf->shared_cpu_map)) {
-		sibling_leaf = CPUID4_INFO_IDX(sibling, index);
-		cpumask_clear_cpu(cpu,
-				  to_cpumask(sibling_leaf->shared_cpu_map));
-	}
-}
-#else
-static void cache_shared_cpu_map_setup(unsigned int cpu, int index)
-{
-}
-
-static void cache_remove_shared_cpu_map(unsigned int cpu, int index)
-{
-}
-#endif
-
-static void free_cache_attributes(unsigned int cpu)
-{
-	int i;
-
-	for (i = 0; i < num_cache_leaves; i++)
-		cache_remove_shared_cpu_map(cpu, i);
-
-	kfree(per_cpu(ici_cpuid4_info, cpu));
-	per_cpu(ici_cpuid4_info, cpu) = NULL;
-}
-
-static void get_cpu_leaves(void *_retval)
-{
-	int j, *retval = _retval, cpu = smp_processor_id();
-
-	/* Do cpuid and store the results */
-	for (j = 0; j < num_cache_leaves; j++) {
-		struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, j);
+		return;
 
-		*retval = cpuid4_cache_lookup_regs(j, &this_leaf->base);
-		if (unlikely(*retval < 0)) {
-			int i;
+	index_msb = get_count_order(num_threads_sharing);
 
-			for (i = 0; i < j; i++)
-				cache_remove_shared_cpu_map(cpu, i);
-			break;
+	for_each_online_cpu(i)
+		if (cpu_data(i).apicid >> index_msb == c->apicid >> index_msb) {
+			struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
+			if (i == cpu || !sib_cpu_ci->info_list)
+				continue;/* skip if itself or no cacheinfo */
+			sibling_leaf = sib_cpu_ci->info_list + index;
+			cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
+			cpumask_set_cpu(cpu, &sibling_leaf->shared_cpu_map);
 		}
-		cache_shared_cpu_map_setup(cpu, j);
-	}
 }
 
-static int detect_cache_attributes(unsigned int cpu)
+static void ci_leaf_init(struct cache_info *this_leaf,
+				struct _cpuid4_info_regs *base)
 {
-	int			retval;
-
-	if (num_cache_leaves == 0)
-		return -ENOENT;
-
-	per_cpu(ici_cpuid4_info, cpu) = kzalloc(
-	    sizeof(struct _cpuid4_info) * num_cache_leaves, GFP_KERNEL);
-	if (per_cpu(ici_cpuid4_info, cpu) == NULL)
-		return -ENOMEM;
-
-	smp_call_function_single(cpu, get_cpu_leaves, &retval, true);
-	if (retval) {
-		kfree(per_cpu(ici_cpuid4_info, cpu));
-		per_cpu(ici_cpuid4_info, cpu) = NULL;
-	}
-
-	return retval;
-}
-
-#include <linux/kobject.h>
-#include <linux/sysfs.h>
-#include <linux/cpu.h>
-
-/* pointer to kobject for cpuX/cache */
-static DEFINE_PER_CPU(struct kobject *, ici_cache_kobject);
-
-struct _index_kobject {
-	struct kobject kobj;
-	unsigned int cpu;
-	unsigned short index;
-};
-
-/* pointer to array of kobjects for cpuX/cache/indexY */
-static DEFINE_PER_CPU(struct _index_kobject *, ici_index_kobject);
-#define INDEX_KOBJECT_PTR(x, y)		(&((per_cpu(ici_index_kobject, x))[y]))
-
-#define show_one_plus(file_name, object, val)				\
-static ssize_t show_##file_name(struct _cpuid4_info *this_leaf, char *buf, \
-				unsigned int cpu)			\
-{									\
-	return sprintf(buf, "%lu\n", (unsigned long)this_leaf->object + val); \
-}
-
-show_one_plus(level, base.eax.split.level, 0);
-show_one_plus(coherency_line_size, base.ebx.split.coherency_line_size, 1);
-show_one_plus(physical_line_partition, base.ebx.split.physical_line_partition, 1);
-show_one_plus(ways_of_associativity, base.ebx.split.ways_of_associativity, 1);
-show_one_plus(number_of_sets, base.ecx.split.number_of_sets, 1);
-
-static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf,
-			 unsigned int cpu)
-{
-	return sprintf(buf, "%luK\n", this_leaf->base.size / 1024);
-}
-
-static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf,
-					int type, char *buf)
-{
-	ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
-	int n = 0;
-
-	if (len > 1) {
-		const struct cpumask *mask;
-
-		mask = to_cpumask(this_leaf->shared_cpu_map);
-		n = type ?
-			cpulist_scnprintf(buf, len-2, mask) :
-			cpumask_scnprintf(buf, len-2, mask);
-		buf[n++] = '\n';
-		buf[n] = '\0';
-	}
-	return n;
+	this_leaf->level = base->eax.split.level;
+	this_leaf->type = base->eax.split.type;
+	this_leaf->coherency_line_size = base->ebx.split.coherency_line_size;
+	this_leaf->ways_of_associativity =
+				base->ebx.split.ways_of_associativity;
+	this_leaf->size = base->size;
+	this_leaf->number_of_sets = base->ecx.split.number_of_sets;
+	this_leaf->physical_line_partition =
+				base->ebx.split.physical_line_partition;
+	this_leaf->priv = base->nb;
 }
 
-static inline ssize_t show_shared_cpu_map(struct _cpuid4_info *leaf, char *buf,
-					  unsigned int cpu)
+int init_cache_level(unsigned int cpu)
 {
-	return show_shared_cpu_map_func(leaf, 0, buf);
-}
-
-static inline ssize_t show_shared_cpu_list(struct _cpuid4_info *leaf, char *buf,
-					   unsigned int cpu)
-{
-	return show_shared_cpu_map_func(leaf, 1, buf);
-}
-
-static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf,
-			 unsigned int cpu)
-{
-	switch (this_leaf->base.eax.split.type) {
-	case CACHE_TYPE_DATA:
-		return sprintf(buf, "Data\n");
-	case CACHE_TYPE_INST:
-		return sprintf(buf, "Instruction\n");
-	case CACHE_TYPE_UNIFIED:
-		return sprintf(buf, "Unified\n");
-	default:
-		return sprintf(buf, "Unknown\n");
-	}
-}
-
-#define to_object(k)	container_of(k, struct _index_kobject, kobj)
-#define to_attr(a)	container_of(a, struct _cache_attr, attr)
-
-#define define_one_ro(_name) \
-static struct _cache_attr _name = \
-	__ATTR(_name, 0444, show_##_name, NULL)
-
-define_one_ro(level);
-define_one_ro(type);
-define_one_ro(coherency_line_size);
-define_one_ro(physical_line_partition);
-define_one_ro(ways_of_associativity);
-define_one_ro(number_of_sets);
-define_one_ro(size);
-define_one_ro(shared_cpu_map);
-define_one_ro(shared_cpu_list);
-
-static struct attribute *default_attrs[] = {
-	&type.attr,
-	&level.attr,
-	&coherency_line_size.attr,
-	&physical_line_partition.attr,
-	&ways_of_associativity.attr,
-	&number_of_sets.attr,
-	&size.attr,
-	&shared_cpu_map.attr,
-	&shared_cpu_list.attr,
-	NULL
-};
-
-#ifdef CONFIG_AMD_NB
-static struct attribute **amd_l3_attrs(void)
-{
-	static struct attribute **attrs;
-	int n;
-
-	if (attrs)
-		return attrs;
-
-	n = ARRAY_SIZE(default_attrs);
-
-	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
-		n += 2;
-
-	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
-		n += 1;
-
-	attrs = kzalloc(n * sizeof (struct attribute *), GFP_KERNEL);
-	if (attrs == NULL)
-		return attrs = default_attrs;
-
-	for (n = 0; default_attrs[n]; n++)
-		attrs[n] = default_attrs[n];
-
-	if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {
-		attrs[n++] = &cache_disable_0.attr;
-		attrs[n++] = &cache_disable_1.attr;
-	}
-
-	if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
-		attrs[n++] = &subcaches.attr;
-
-	return attrs;
-}
-#endif
-
-static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
-{
-	struct _cache_attr *fattr = to_attr(attr);
-	struct _index_kobject *this_leaf = to_object(kobj);
-	ssize_t ret;
-
-	ret = fattr->show ?
-		fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index),
-			buf, this_leaf->cpu) :
-		0;
-	return ret;
-}
-
-static ssize_t store(struct kobject *kobj, struct attribute *attr,
-		     const char *buf, size_t count)
-{
-	struct _cache_attr *fattr = to_attr(attr);
-	struct _index_kobject *this_leaf = to_object(kobj);
-	ssize_t ret;
-
-	ret = fattr->store ?
-		fattr->store(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index),
-			buf, count, this_leaf->cpu) :
-		0;
-	return ret;
-}
-
-static const struct sysfs_ops sysfs_ops = {
-	.show   = show,
-	.store  = store,
-};
-
-static struct kobj_type ktype_cache = {
-	.sysfs_ops	= &sysfs_ops,
-	.default_attrs	= default_attrs,
-};
-
-static struct kobj_type ktype_percpu_entry = {
-	.sysfs_ops	= &sysfs_ops,
-};
-
-static void cpuid4_cache_sysfs_exit(unsigned int cpu)
-{
-	kfree(per_cpu(ici_cache_kobject, cpu));
-	kfree(per_cpu(ici_index_kobject, cpu));
-	per_cpu(ici_cache_kobject, cpu) = NULL;
-	per_cpu(ici_index_kobject, cpu) = NULL;
-	free_cache_attributes(cpu);
-}
-
-static int cpuid4_cache_sysfs_init(unsigned int cpu)
-{
-	int err;
-
-	if (num_cache_leaves == 0)
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	if (!num_cache_leaves)
 		return -ENOENT;
+	if (!this_cpu_ci)
+		return -EINVAL;
 
-	err = detect_cache_attributes(cpu);
-	if (err)
-		return err;
-
-	/* Allocate all required memory */
-	per_cpu(ici_cache_kobject, cpu) =
-		kzalloc(sizeof(struct kobject), GFP_KERNEL);
-	if (unlikely(per_cpu(ici_cache_kobject, cpu) == NULL))
-		goto err_out;
-
-	per_cpu(ici_index_kobject, cpu) = kzalloc(
-	    sizeof(struct _index_kobject) * num_cache_leaves, GFP_KERNEL);
-	if (unlikely(per_cpu(ici_index_kobject, cpu) == NULL))
-		goto err_out;
-
+	this_cpu_ci->num_levels = 3;
+	this_cpu_ci->num_leaves = num_cache_leaves;
 	return 0;
-
-err_out:
-	cpuid4_cache_sysfs_exit(cpu);
-	return -ENOMEM;
 }
 
-static DECLARE_BITMAP(cache_dev_map, NR_CPUS);
-
-/* Add/Remove cache interface for CPU device */
-static int cache_add_dev(struct device *dev)
+int populate_cache_leaves(unsigned int cpu)
 {
-	unsigned int cpu = dev->id;
-	unsigned long i, j;
-	struct _index_kobject *this_object;
-	struct _cpuid4_info   *this_leaf;
-	int retval;
-
-	retval = cpuid4_cache_sysfs_init(cpu);
-	if (unlikely(retval < 0))
-		return retval;
-
-	retval = kobject_init_and_add(per_cpu(ici_cache_kobject, cpu),
-				      &ktype_percpu_entry,
-				      &dev->kobj, "%s", "cache");
-	if (retval < 0) {
-		cpuid4_cache_sysfs_exit(cpu);
-		return retval;
-	}
+	unsigned int idx, ret;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	struct _cpuid4_info_regs id4_regs = {};
 
-	for (i = 0; i < num_cache_leaves; i++) {
-		this_object = INDEX_KOBJECT_PTR(cpu, i);
-		this_object->cpu = cpu;
-		this_object->index = i;
-
-		this_leaf = CPUID4_INFO_IDX(cpu, i);
-
-		ktype_cache.default_attrs = default_attrs;
-#ifdef CONFIG_AMD_NB
-		if (this_leaf->base.nb)
-			ktype_cache.default_attrs = amd_l3_attrs();
-#endif
-		retval = kobject_init_and_add(&(this_object->kobj),
-					      &ktype_cache,
-					      per_cpu(ici_cache_kobject, cpu),
-					      "index%1lu", i);
-		if (unlikely(retval)) {
-			for (j = 0; j < i; j++)
-				kobject_put(&(INDEX_KOBJECT_PTR(cpu, j)->kobj));
-			kobject_put(per_cpu(ici_cache_kobject, cpu));
-			cpuid4_cache_sysfs_exit(cpu);
-			return retval;
-		}
-		kobject_uevent(&(this_object->kobj), KOBJ_ADD);
+	for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) {
+		ret = cpuid4_cache_lookup_regs(idx, &id4_regs);
+		if (ret)
+			return ret;
+		ci_leaf_init(this_leaf++, &id4_regs);
+		__cache_cpumap_setup(cpu, idx, &id4_regs);
 	}
-	cpumask_set_cpu(cpu, to_cpumask(cache_dev_map));
-
-	kobject_uevent(per_cpu(ici_cache_kobject, cpu), KOBJ_ADD);
 	return 0;
 }
-
-static void cache_remove_dev(struct device *dev)
-{
-	unsigned int cpu = dev->id;
-	unsigned long i;
-
-	if (per_cpu(ici_cpuid4_info, cpu) == NULL)
-		return;
-	if (!cpumask_test_cpu(cpu, to_cpumask(cache_dev_map)))
-		return;
-	cpumask_clear_cpu(cpu, to_cpumask(cache_dev_map));
-
-	for (i = 0; i < num_cache_leaves; i++)
-		kobject_put(&(INDEX_KOBJECT_PTR(cpu, i)->kobj));
-	kobject_put(per_cpu(ici_cache_kobject, cpu));
-	cpuid4_cache_sysfs_exit(cpu);
-}
-
-static int cacheinfo_cpu_callback(struct notifier_block *nfb,
-				  unsigned long action, void *hcpu)
-{
-	unsigned int cpu = (unsigned long)hcpu;
-	struct device *dev;
-
-	dev = get_cpu_device(cpu);
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		cache_add_dev(dev);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		cache_remove_dev(dev);
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block cacheinfo_cpu_notifier = {
-	.notifier_call = cacheinfo_cpu_callback,
-};
-
-static int __init cache_sysfs_init(void)
-{
-	int i;
-
-	if (num_cache_leaves == 0)
-		return 0;
-
-	for_each_online_cpu(i) {
-		int err;
-		struct device *dev = get_cpu_device(i);
-
-		err = cache_add_dev(dev);
-		if (err)
-			return err;
-	}
-	register_hotcpu_notifier(&cacheinfo_cpu_notifier);
-	return 0;
-}
-
-device_initcall(cache_sysfs_init);
-
-#endif
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 5/8] powerpc: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49   ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: sudeep.holla, Benjamin Herrenschmidt, Paul Mackerras, linuxppc-dev

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: linuxppc-dev@lists.ozlabs.org
---
 arch/powerpc/kernel/cacheinfo.c | 828 ++++++----------------------------------
 arch/powerpc/kernel/cacheinfo.h |   8 -
 arch/powerpc/kernel/sysfs.c     |   4 -
 3 files changed, 109 insertions(+), 731 deletions(-)
 delete mode 100644 arch/powerpc/kernel/cacheinfo.h

diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c
index abfa011..05b7580 100644
--- a/arch/powerpc/kernel/cacheinfo.c
+++ b/arch/powerpc/kernel/cacheinfo.c
@@ -10,38 +10,10 @@
  * 2 as published by the Free Software Foundation.
  */
 
+#include <linux/cacheinfo.h>
 #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 <asm/prom.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 */
@@ -60,11 +32,6 @@ struct cache_type_info {
 	const char *nr_sets_prop;
 };
 
-/* These are used to index the cache_type_info array. */
-#define CACHE_TYPE_UNIFIED     0
-#define CACHE_TYPE_INSTRUCTION 1
-#define CACHE_TYPE_DATA        2
-
 static const struct cache_type_info cache_type_info[] = {
 	{
 		/* PowerPC Processor binding says the [di]-cache-*
@@ -77,246 +44,115 @@ static const struct cache_type_info cache_type_info[] = {
 		.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",
 	},
+	{
+		.name            = "Instruction",
+		.size_prop       = "i-cache-size",
+		.line_size_props = { "i-cache-line-size",
+				     "i-cache-block-size", },
+		.nr_sets_prop    = "i-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 */
-	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)
+static inline int get_cacheinfo_idx(enum cache_type type)
 {
-	return cache_type_info[cache->type].name;
-}
-
-static void cache_init(struct cache *cache, int type, int level,
-		       struct device_node *ofnode)
-{
-	cache->type = type;
-	cache->level = level;
-	cache->ofnode = of_node_get(ofnode);
-	INIT_LIST_HEAD(&cache->list);
-	list_add(&cache->list, &cache_list);
-}
-
-static struct cache *new_cache(int type, int level, struct device_node *ofnode)
-{
-	struct cache *cache;
-
-	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
-	if (cache)
-		cache_init(cache, type, level, ofnode);
-
-	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 %s(%s) refers to cache for %s(%s)\n",
-			  iter->ofnode->full_name,
-			  cache_type_string(iter),
-			  cache->ofnode->full_name,
-			  cache_type_string(cache));
-}
-
-static void release_cache(struct cache *cache)
-{
-	if (!cache)
-		return;
-
-	pr_debug("freeing L%d %s cache for %s\n", cache->level,
-		 cache_type_string(cache), cache->ofnode->full_name);
-
-	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 %s(%s)\n",
-			  cpu, next->ofnode->full_name,
-			  cache_type_string(next));
-		cpumask_set_cpu(cpu, &next->shared_cpu_map);
-		next = next->next_local;
-	}
+	if (type == CACHE_TYPE_UNIFIED)
+		return 0;
+	else
+		return type;
 }
 
-static int cache_size(const struct cache *cache, unsigned int *ret)
+static int cache_size(struct cache_info *this_leaf)
 {
 	const char *propname;
 	const __be32 *cache_size;
+	int ct_idx;
 
-	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;
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	propname = cache_type_info[ct_idx].size_prop;
 
-	if (cache_size(cache, &size))
+	cache_size = of_get_property(this_leaf->of_node, propname, NULL);
+	if (!cache_size) {
+		this_leaf->size = 0;
 		return -ENODEV;
-
-	*ret = size / 1024;
-	return 0;
+	} else {
+		this_leaf->size = of_read_number(cache_size, 1);
+		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)
+static int cache_get_line_size(struct cache_info *this_leaf)
 {
 	const __be32 *line_size;
-	int i, lim;
+	int i, lim, ct_idx;
 
-	lim = ARRAY_SIZE(cache_type_info[cache->type].line_size_props);
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	lim = ARRAY_SIZE(cache_type_info[ct_idx].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);
+		propname = cache_type_info[ct_idx].line_size_props[i];
+		line_size = of_get_property(this_leaf->of_node, propname, NULL);
 		if (line_size)
 			break;
 	}
 
-	if (!line_size)
+	if (!line_size) {
+		this_leaf->coherency_line_size = 0;
 		return -ENODEV;
-
-	*ret = of_read_number(line_size, 1);
-	return 0;
+	} else {
+		this_leaf->coherency_line_size = of_read_number(line_size, 1);
+		return 0;
+	}
 }
 
-static int cache_nr_sets(const struct cache *cache, unsigned int *ret)
+static int cache_nr_sets(struct cache_info *this_leaf)
 {
 	const char *propname;
 	const __be32 *nr_sets;
+	int ct_idx;
 
-	propname = cache_type_info[cache->type].nr_sets_prop;
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	propname = cache_type_info[ct_idx].nr_sets_prop;
 
-	nr_sets = of_get_property(cache->ofnode, propname, NULL);
-	if (!nr_sets)
+	nr_sets = of_get_property(this_leaf->of_node, propname, NULL);
+	if (!nr_sets) {
+		this_leaf->number_of_sets = 0;
 		return -ENODEV;
-
-	*ret = of_read_number(nr_sets, 1);
-	return 0;
+	} else {
+		this_leaf->number_of_sets = of_read_number(nr_sets, 1);
+		return 0;
+	}
 }
 
-static int cache_associativity(const struct cache *cache, unsigned int *ret)
+static int cache_associativity(struct cache_info *this_leaf)
 {
-	unsigned int line_size;
-	unsigned int nr_sets;
-	unsigned int size;
-
-	if (cache_nr_sets(cache, &nr_sets))
-		goto err;
+	unsigned int line_size = this_leaf->coherency_line_size;
+	unsigned int nr_sets = this_leaf->number_of_sets;
+	unsigned int size = this_leaf->size;
 
 	/* If the cache is fully associative, there is no need to
 	 * check the other properties.
 	 */
 	if (nr_sets == 1) {
-		*ret = 0;
+		this_leaf->ways_of_associativity = 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)
-		return cache;
-
-	list_for_each_entry(iter, &cache_list, list)
-		if (iter->ofnode == cache->ofnode && iter->next_local == cache)
-			return iter;
-
-	return cache;
-}
-
-/* return the first cache on a local list matching node */
-static struct cache *cache_lookup_by_node(const struct device_node *node)
-{
-	struct cache *cache = NULL;
-	struct cache *iter;
-
-	list_for_each_entry(iter, &cache_list, list) {
-		if (iter->ofnode != node)
-			continue;
-		cache = cache_find_first_sibling(iter);
-		break;
+	if (!(nr_sets > 0 && size > 0 && line_size > 0)) {
+		this_leaf->ways_of_associativity = 0;
+		return -ENODEV;
+	} else {
+		this_leaf->ways_of_associativity = (size / nr_sets) / line_size;
+		return 0;
 	}
-
-	return cache;
 }
 
 static bool cache_node_is_unified(const struct device_node *np)
@@ -324,520 +160,74 @@ static bool cache_node_is_unified(const struct device_node *np)
 	return of_get_property(np, "cache-unified", NULL);
 }
 
-static struct cache *cache_do_one_devnode_unified(struct device_node *node,
-						  int level)
-{
-	struct cache *cache;
-
-	pr_debug("creating L%d ucache for %s\n", level, node->full_name);
-
-	cache = new_cache(CACHE_TYPE_UNIFIED, level, node);
-
-	return cache;
-}
-
-static struct cache *cache_do_one_devnode_split(struct device_node *node,
-						int level)
+static void ci_leaf_init(struct cache_info *this_leaf,
+				enum cache_type type, unsigned int level)
 {
-	struct cache *dcache, *icache;
-
-	pr_debug("creating L%d dcache and icache for %s\n", level,
-		 node->full_name);
-
-	dcache = new_cache(CACHE_TYPE_DATA, level, node);
-	icache = new_cache(CACHE_TYPE_INSTRUCTION, level, node);
-
-	if (!dcache || !icache)
-		goto err;
-
-	dcache->next_local = icache;
-
-	return dcache;
-err:
-	release_cache(dcache);
-	release_cache(icache);
-	return NULL;
+	this_leaf->level = level;
+	this_leaf->type = type;
+	cache_size(this_leaf);
+	cache_get_line_size(this_leaf);
+	cache_nr_sets(this_leaf);
+	cache_associativity(this_leaf);
 }
 
-static struct cache *cache_do_one_devnode(struct device_node *node, int level)
+int init_cache_level(unsigned int cpu)
 {
-	struct cache *cache;
-
-	if (cache_node_is_unified(node))
-		cache = cache_do_one_devnode_unified(node, level);
-	else
-		cache = cache_do_one_devnode_split(node, level);
-
-	return cache;
-}
+	struct device_node *np;
+	struct device *cpu_dev = get_cpu_device(cpu);
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned int level = 0, leaves = 0;
 
-static struct cache *cache_lookup_or_instantiate(struct device_node *node,
-						 int level)
-{
-	struct cache *cache;
-
-	cache = cache_lookup_by_node(node);
-
-	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, 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;
-}
-
-static void do_subsidiary_caches_debugcheck(struct cache *cache)
-{
-	WARN_ON_ONCE(cache->level != 1);
-	WARN_ON_ONCE(strcmp(cache->ofnode->type, "cpu"));
-}
-
-static void do_subsidiary_caches(struct cache *cache)
-{
-	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;
-
-		level++;
-		subcache = cache_lookup_or_instantiate(subcache_node, level);
-		of_node_put(subcache_node);
-		if (!subcache)
-			break;
-
-		link_cache_lists(cache, subcache);
-		cache = subcache;
-	}
-}
-
-static struct cache *cache_chain_instantiate(unsigned int cpu_id)
-{
-	struct device_node *cpu_node;
-	struct cache *cpu_cache = NULL;
-
-	pr_debug("creating cache object(s) for CPU %i\n", cpu_id);
-
-	cpu_node = of_get_cpu_node(cpu_id, NULL);
-	WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
-	if (!cpu_node)
-		goto out;
-
-	cpu_cache = cache_lookup_or_instantiate(cpu_node, 1);
-	if (!cpu_cache)
-		goto out;
-
-	do_subsidiary_caches(cpu_cache);
-
-	cache_cpu_set(cpu_cache, cpu_id);
-out:
-	of_node_put(cpu_node);
-
-	return cpu_cache;
-}
-
-static struct cache_dir *cacheinfo_create_cache_dir(unsigned int cpu_id)
-{
-	struct cache_dir *cache_dir;
-	struct device *dev;
-	struct kobject *kobj = NULL;
-
-	dev = get_cpu_device(cpu_id);
-	WARN_ONCE(!dev, "no dev for CPU %i\n", cpu_id);
-	if (!dev)
-		goto err;
-
-	kobj = kobject_create_and_add("cache", &dev->kobj);
-	if (!kobj)
-		goto err;
-
-	cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL);
-	if (!cache_dir)
-		goto err;
-
-	cache_dir->kobj = kobj;
-
-	WARN_ON_ONCE(per_cpu(cache_dir_pcpu, cpu_id) != NULL);
-
-	per_cpu(cache_dir_pcpu, cpu_id) = cache_dir;
-
-	return cache_dir;
-err:
-	kobject_put(kobj);
-	return NULL;
-}
-
-static void cache_index_release(struct kobject *kobj)
-{
-	struct cache_index_dir *index;
-
-	index = kobj_to_cache_index_dir(kobj);
-
-	pr_debug("freeing index directory for L%d %s cache\n",
-		 index->cache->level, cache_type_string(index->cache));
-
-	kfree(index);
-}
-
-static ssize_t cache_index_show(struct kobject *k, struct attribute *attr, char *buf)
-{
-	struct kobj_attribute *kobj_attr;
-
-	kobj_attr = container_of(attr, struct kobj_attribute, attr);
-
-	return kobj_attr->show(k, kobj_attr, buf);
-}
-
-static struct cache *index_kobj_to_cache(struct kobject *k)
-{
-	struct cache_index_dir *index;
-
-	index = kobj_to_cache_index_dir(k);
-
-	return index->cache;
-}
-
-static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int size_kb;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_size_kb(cache, &size_kb))
+	if (!cpu_dev) {
+		pr_err("No cpu device for CPU %d\n", cpu);
 		return -ENODEV;
-
-	return sprintf(buf, "%uK\n", size_kb);
-}
-
-static struct kobj_attribute cache_size_attr =
-	__ATTR(size, 0444, size_show, NULL);
-
-
-static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int line_size;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_get_line_size(cache, &line_size))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", line_size);
-}
-
-static struct kobj_attribute cache_line_size_attr =
-	__ATTR(coherency_line_size, 0444, line_size_show, NULL);
-
-static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int nr_sets;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_nr_sets(cache, &nr_sets))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", nr_sets);
-}
-
-static struct kobj_attribute cache_nr_sets_attr =
-	__ATTR(number_of_sets, 0444, nr_sets_show, NULL);
-
-static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int associativity;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_associativity(cache, &associativity))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", associativity);
-}
-
-static struct kobj_attribute cache_assoc_attr =
-	__ATTR(ways_of_associativity, 0444, associativity_show, NULL);
-
-static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	return sprintf(buf, "%s\n", cache_type_string(cache));
-}
-
-static struct kobj_attribute cache_type_attr =
-	__ATTR(type, 0444, type_show, NULL);
-
-static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache_index_dir *index;
-	struct cache *cache;
-
-	index = kobj_to_cache_index_dir(k);
-	cache = index->cache;
-
-	return sprintf(buf, "%d\n", cache->level);
-}
-
-static struct kobj_attribute cache_level_attr =
-	__ATTR(level, 0444, level_show, NULL);
-
-static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache_index_dir *index;
-	struct cache *cache;
-	int len;
-	int n = 0;
-
-	index = kobj_to_cache_index_dir(k);
-	cache = index->cache;
-	len = PAGE_SIZE - 2;
-
-	if (len > 1) {
-		n = cpumask_scnprintf(buf, len, &cache->shared_cpu_map);
-		buf[n++] = '\n';
-		buf[n] = '\0';
 	}
-	return n;
-}
-
-static struct kobj_attribute cache_shared_cpu_map_attr =
-	__ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
-
-/* Attributes which should always be created -- the kobject/sysfs core
- * does this automatically via kobj_type->default_attrs.  This is the
- * minimum data required to uniquely identify a cache.
- */
-static struct attribute *cache_index_default_attrs[] = {
-	&cache_type_attr.attr,
-	&cache_level_attr.attr,
-	&cache_shared_cpu_map_attr.attr,
-	NULL,
-};
-
-/* Attributes which should be created if the cache device node has the
- * right properties -- see cacheinfo_create_index_opt_attrs
- */
-static struct kobj_attribute *cache_index_opt_attrs[] = {
-	&cache_size_attr,
-	&cache_line_size_attr,
-	&cache_nr_sets_attr,
-	&cache_assoc_attr,
-};
-
-static const struct sysfs_ops cache_index_ops = {
-	.show = cache_index_show,
-};
-
-static struct kobj_type cache_index_type = {
-	.release = cache_index_release,
-	.sysfs_ops = &cache_index_ops,
-	.default_attrs = cache_index_default_attrs,
-};
-
-static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir)
-{
-	const char *cache_name;
-	const char *cache_type;
-	struct cache *cache;
-	char *buf;
-	int i;
-
-	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!buf)
-		return;
-
-	cache = dir->cache;
-	cache_name = cache->ofnode->full_name;
-	cache_type = cache_type_string(cache);
-
-	/* We don't want to create an attribute that can't provide a
-	 * meaningful value.  Check the return value of each optional
-	 * attribute's ->show method before registering the
-	 * attribute.
-	 */
-	for (i = 0; i < ARRAY_SIZE(cache_index_opt_attrs); i++) {
-		struct kobj_attribute *attr;
-		ssize_t rc;
-
-		attr = cache_index_opt_attrs[i];
-
-		rc = attr->show(&dir->kobj, attr, buf);
-		if (rc <= 0) {
-			pr_debug("not creating %s attribute for "
-				 "%s(%s) (rc = %zd)\n",
-				 attr->attr.name, cache_name,
-				 cache_type, rc);
-			continue;
-		}
-		if (sysfs_create_file(&dir->kobj, &attr->attr))
-			pr_debug("could not create %s attribute for %s(%s)\n",
-				 attr->attr.name, cache_name, cache_type);
+	np = cpu_dev->of_node;
+	if (!np) {
+		pr_err("Failed to find cpu%d device node\n", cpu);
+		return -ENOENT;
 	}
 
-	kfree(buf);
-}
-
-static void cacheinfo_create_index_dir(struct cache *cache, int index,
-				       struct cache_dir *cache_dir)
-{
-	struct cache_index_dir *index_dir;
-	int rc;
-
-	index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL);
-	if (!index_dir)
-		goto err;
-
-	index_dir->cache = cache;
-
-	rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
-				  cache_dir->kobj, "index%d", index);
-	if (rc)
-		goto err;
-
-	index_dir->next = cache_dir->index;
-	cache_dir->index = index_dir;
-
-	cacheinfo_create_index_opt_attrs(index_dir);
-
-	return;
-err:
-	kfree(index_dir);
-}
-
-static void cacheinfo_sysfs_populate(unsigned int cpu_id,
-				     struct cache *cache_list)
-{
-	struct cache_dir *cache_dir;
-	struct cache *cache;
-	int index = 0;
-
-	cache_dir = cacheinfo_create_cache_dir(cpu_id);
-	if (!cache_dir)
-		return;
-
-	cache = cache_list;
-	while (cache) {
-		cacheinfo_create_index_dir(cache, index, cache_dir);
-		index++;
-		cache = cache->next_local;
+	while (np) {
+		leaves += cache_node_is_unified(np) ? 1 : 2;
+		level++;
+		of_node_put(np);
+		np = of_find_next_cache_node(np);
 	}
-}
-
-void cacheinfo_cpu_online(unsigned int cpu_id)
-{
-	struct cache *cache;
-
-	cache = cache_chain_instantiate(cpu_id);
-	if (!cache)
-		return;
-
-	cacheinfo_sysfs_populate(cpu_id, cache);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU /* functions needed for cpu offline */
+	this_cpu_ci->num_levels = level;
+	this_cpu_ci->num_leaves = leaves;
 
-static struct cache *cache_lookup_by_cpu(unsigned int cpu_id)
-{
-	struct device_node *cpu_node;
-	struct cache *cache;
-
-	cpu_node = of_get_cpu_node(cpu_id, NULL);
-	WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
-	if (!cpu_node)
-		return NULL;
-
-	cache = cache_lookup_by_node(cpu_node);
-	of_node_put(cpu_node);
-
-	return cache;
+	return 0;
 }
 
-static void remove_index_dirs(struct cache_dir *cache_dir)
+int populate_cache_leaves(unsigned int cpu)
 {
-	struct cache_index_dir *index;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	struct device *cpu_dev = get_cpu_device(cpu);
+	struct device_node *np;
+	unsigned int level, idx;
 
-	index = cache_dir->index;
-
-	while (index) {
-		struct cache_index_dir *next;
-
-		next = index->next;
-		kobject_put(&index->kobj);
-		index = next;
+	np = of_node_get(cpu_dev->of_node);
+	if (!np) {
+		pr_err("Failed to find cpu%d device node\n", cpu);
+		return -ENOENT;
 	}
-}
 
-static void remove_cache_dir(struct cache_dir *cache_dir)
-{
-	remove_index_dirs(cache_dir);
-
-	kobject_put(cache_dir->kobj);
-
-	kfree(cache_dir);
-}
-
-static void cache_cpu_clear(struct cache *cache, int cpu)
-{
-	while (cache) {
-		struct cache *next = cache->next_local;
-
-		WARN_ONCE(!cpumask_test_cpu(cpu, &cache->shared_cpu_map),
-			  "CPU %i not accounted in %s(%s)\n",
-			  cpu, cache->ofnode->full_name,
-			  cache_type_string(cache));
-
-		cpumask_clear_cpu(cpu, &cache->shared_cpu_map);
-
-		/* Release the cache object if all the cpus using it
-		 * are offline */
-		if (cpumask_empty(&cache->shared_cpu_map))
-			release_cache(cache);
-
-		cache = next;
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		this_leaf->of_node = np;
+		if (cache_node_is_unified(np)) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_UNIFIED, level);
+		} else {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		}
+		np = of_find_next_cache_node(np);
 	}
+	return 0;
 }
 
-void cacheinfo_cpu_offline(unsigned int cpu_id)
-{
-	struct cache_dir *cache_dir;
-	struct cache *cache;
-
-	/* Prevent userspace from seeing inconsistent state - remove
-	 * the sysfs hierarchy first */
-	cache_dir = per_cpu(cache_dir_pcpu, cpu_id);
-
-	/* careful, sysfs population may have failed */
-	if (cache_dir)
-		remove_cache_dir(cache_dir);
-
-	per_cpu(cache_dir_pcpu, cpu_id) = NULL;
-
-	/* clear the CPU's bit in its cache chain, possibly freeing
-	 * cache objects */
-	cache = cache_lookup_by_cpu(cpu_id);
-	if (cache)
-		cache_cpu_clear(cache, cpu_id);
-}
-#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/powerpc/kernel/cacheinfo.h b/arch/powerpc/kernel/cacheinfo.h
deleted file mode 100644
index a7b74d3..0000000
--- a/arch/powerpc/kernel/cacheinfo.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _PPC_CACHEINFO_H
-#define _PPC_CACHEINFO_H
-
-/* These are just hooks for sysfs.c to use. */
-extern void cacheinfo_cpu_online(unsigned int cpu_id);
-extern void cacheinfo_cpu_offline(unsigned int cpu_id);
-
-#endif /* _PPC_CACHEINFO_H */
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index d4a43e6..935929b 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -19,8 +19,6 @@
 #include <asm/pmc.h>
 #include <asm/firmware.h>
 
-#include "cacheinfo.h"
-
 #ifdef CONFIG_PPC64
 #include <asm/paca.h>
 #include <asm/lppaca.h>
@@ -732,7 +730,6 @@ static void register_cpu_online(unsigned int cpu)
 		device_create_file(s, &dev_attr_altivec_idle_wait_time);
 	}
 #endif
-	cacheinfo_cpu_online(cpu);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -813,7 +810,6 @@ static void unregister_cpu_online(unsigned int cpu)
 		device_remove_file(s, &dev_attr_altivec_idle_wait_time);
 	}
 #endif
-	cacheinfo_cpu_offline(cpu);
 }
 
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 5/8] powerpc: move cacheinfo sysfs to generic cacheinfo infrastructure
@ 2014-02-07 16:49   ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel; +Cc: Paul Mackerras, linuxppc-dev, sudeep.holla

From: Sudeep Holla <sudeep.holla@arm.com>

This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: linuxppc-dev@lists.ozlabs.org
---
 arch/powerpc/kernel/cacheinfo.c | 828 ++++++----------------------------------
 arch/powerpc/kernel/cacheinfo.h |   8 -
 arch/powerpc/kernel/sysfs.c     |   4 -
 3 files changed, 109 insertions(+), 731 deletions(-)
 delete mode 100644 arch/powerpc/kernel/cacheinfo.h

diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c
index abfa011..05b7580 100644
--- a/arch/powerpc/kernel/cacheinfo.c
+++ b/arch/powerpc/kernel/cacheinfo.c
@@ -10,38 +10,10 @@
  * 2 as published by the Free Software Foundation.
  */
 
+#include <linux/cacheinfo.h>
 #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 <asm/prom.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 */
@@ -60,11 +32,6 @@ struct cache_type_info {
 	const char *nr_sets_prop;
 };
 
-/* These are used to index the cache_type_info array. */
-#define CACHE_TYPE_UNIFIED     0
-#define CACHE_TYPE_INSTRUCTION 1
-#define CACHE_TYPE_DATA        2
-
 static const struct cache_type_info cache_type_info[] = {
 	{
 		/* PowerPC Processor binding says the [di]-cache-*
@@ -77,246 +44,115 @@ static const struct cache_type_info cache_type_info[] = {
 		.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",
 	},
+	{
+		.name            = "Instruction",
+		.size_prop       = "i-cache-size",
+		.line_size_props = { "i-cache-line-size",
+				     "i-cache-block-size", },
+		.nr_sets_prop    = "i-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 */
-	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)
+static inline int get_cacheinfo_idx(enum cache_type type)
 {
-	return cache_type_info[cache->type].name;
-}
-
-static void cache_init(struct cache *cache, int type, int level,
-		       struct device_node *ofnode)
-{
-	cache->type = type;
-	cache->level = level;
-	cache->ofnode = of_node_get(ofnode);
-	INIT_LIST_HEAD(&cache->list);
-	list_add(&cache->list, &cache_list);
-}
-
-static struct cache *new_cache(int type, int level, struct device_node *ofnode)
-{
-	struct cache *cache;
-
-	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
-	if (cache)
-		cache_init(cache, type, level, ofnode);
-
-	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 %s(%s) refers to cache for %s(%s)\n",
-			  iter->ofnode->full_name,
-			  cache_type_string(iter),
-			  cache->ofnode->full_name,
-			  cache_type_string(cache));
-}
-
-static void release_cache(struct cache *cache)
-{
-	if (!cache)
-		return;
-
-	pr_debug("freeing L%d %s cache for %s\n", cache->level,
-		 cache_type_string(cache), cache->ofnode->full_name);
-
-	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 %s(%s)\n",
-			  cpu, next->ofnode->full_name,
-			  cache_type_string(next));
-		cpumask_set_cpu(cpu, &next->shared_cpu_map);
-		next = next->next_local;
-	}
+	if (type == CACHE_TYPE_UNIFIED)
+		return 0;
+	else
+		return type;
 }
 
-static int cache_size(const struct cache *cache, unsigned int *ret)
+static int cache_size(struct cache_info *this_leaf)
 {
 	const char *propname;
 	const __be32 *cache_size;
+	int ct_idx;
 
-	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;
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	propname = cache_type_info[ct_idx].size_prop;
 
-	if (cache_size(cache, &size))
+	cache_size = of_get_property(this_leaf->of_node, propname, NULL);
+	if (!cache_size) {
+		this_leaf->size = 0;
 		return -ENODEV;
-
-	*ret = size / 1024;
-	return 0;
+	} else {
+		this_leaf->size = of_read_number(cache_size, 1);
+		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)
+static int cache_get_line_size(struct cache_info *this_leaf)
 {
 	const __be32 *line_size;
-	int i, lim;
+	int i, lim, ct_idx;
 
-	lim = ARRAY_SIZE(cache_type_info[cache->type].line_size_props);
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	lim = ARRAY_SIZE(cache_type_info[ct_idx].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);
+		propname = cache_type_info[ct_idx].line_size_props[i];
+		line_size = of_get_property(this_leaf->of_node, propname, NULL);
 		if (line_size)
 			break;
 	}
 
-	if (!line_size)
+	if (!line_size) {
+		this_leaf->coherency_line_size = 0;
 		return -ENODEV;
-
-	*ret = of_read_number(line_size, 1);
-	return 0;
+	} else {
+		this_leaf->coherency_line_size = of_read_number(line_size, 1);
+		return 0;
+	}
 }
 
-static int cache_nr_sets(const struct cache *cache, unsigned int *ret)
+static int cache_nr_sets(struct cache_info *this_leaf)
 {
 	const char *propname;
 	const __be32 *nr_sets;
+	int ct_idx;
 
-	propname = cache_type_info[cache->type].nr_sets_prop;
+	ct_idx = get_cacheinfo_idx(this_leaf->type);
+	propname = cache_type_info[ct_idx].nr_sets_prop;
 
-	nr_sets = of_get_property(cache->ofnode, propname, NULL);
-	if (!nr_sets)
+	nr_sets = of_get_property(this_leaf->of_node, propname, NULL);
+	if (!nr_sets) {
+		this_leaf->number_of_sets = 0;
 		return -ENODEV;
-
-	*ret = of_read_number(nr_sets, 1);
-	return 0;
+	} else {
+		this_leaf->number_of_sets = of_read_number(nr_sets, 1);
+		return 0;
+	}
 }
 
-static int cache_associativity(const struct cache *cache, unsigned int *ret)
+static int cache_associativity(struct cache_info *this_leaf)
 {
-	unsigned int line_size;
-	unsigned int nr_sets;
-	unsigned int size;
-
-	if (cache_nr_sets(cache, &nr_sets))
-		goto err;
+	unsigned int line_size = this_leaf->coherency_line_size;
+	unsigned int nr_sets = this_leaf->number_of_sets;
+	unsigned int size = this_leaf->size;
 
 	/* If the cache is fully associative, there is no need to
 	 * check the other properties.
 	 */
 	if (nr_sets == 1) {
-		*ret = 0;
+		this_leaf->ways_of_associativity = 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)
-		return cache;
-
-	list_for_each_entry(iter, &cache_list, list)
-		if (iter->ofnode == cache->ofnode && iter->next_local == cache)
-			return iter;
-
-	return cache;
-}
-
-/* return the first cache on a local list matching node */
-static struct cache *cache_lookup_by_node(const struct device_node *node)
-{
-	struct cache *cache = NULL;
-	struct cache *iter;
-
-	list_for_each_entry(iter, &cache_list, list) {
-		if (iter->ofnode != node)
-			continue;
-		cache = cache_find_first_sibling(iter);
-		break;
+	if (!(nr_sets > 0 && size > 0 && line_size > 0)) {
+		this_leaf->ways_of_associativity = 0;
+		return -ENODEV;
+	} else {
+		this_leaf->ways_of_associativity = (size / nr_sets) / line_size;
+		return 0;
 	}
-
-	return cache;
 }
 
 static bool cache_node_is_unified(const struct device_node *np)
@@ -324,520 +160,74 @@ static bool cache_node_is_unified(const struct device_node *np)
 	return of_get_property(np, "cache-unified", NULL);
 }
 
-static struct cache *cache_do_one_devnode_unified(struct device_node *node,
-						  int level)
-{
-	struct cache *cache;
-
-	pr_debug("creating L%d ucache for %s\n", level, node->full_name);
-
-	cache = new_cache(CACHE_TYPE_UNIFIED, level, node);
-
-	return cache;
-}
-
-static struct cache *cache_do_one_devnode_split(struct device_node *node,
-						int level)
+static void ci_leaf_init(struct cache_info *this_leaf,
+				enum cache_type type, unsigned int level)
 {
-	struct cache *dcache, *icache;
-
-	pr_debug("creating L%d dcache and icache for %s\n", level,
-		 node->full_name);
-
-	dcache = new_cache(CACHE_TYPE_DATA, level, node);
-	icache = new_cache(CACHE_TYPE_INSTRUCTION, level, node);
-
-	if (!dcache || !icache)
-		goto err;
-
-	dcache->next_local = icache;
-
-	return dcache;
-err:
-	release_cache(dcache);
-	release_cache(icache);
-	return NULL;
+	this_leaf->level = level;
+	this_leaf->type = type;
+	cache_size(this_leaf);
+	cache_get_line_size(this_leaf);
+	cache_nr_sets(this_leaf);
+	cache_associativity(this_leaf);
 }
 
-static struct cache *cache_do_one_devnode(struct device_node *node, int level)
+int init_cache_level(unsigned int cpu)
 {
-	struct cache *cache;
-
-	if (cache_node_is_unified(node))
-		cache = cache_do_one_devnode_unified(node, level);
-	else
-		cache = cache_do_one_devnode_split(node, level);
-
-	return cache;
-}
+	struct device_node *np;
+	struct device *cpu_dev = get_cpu_device(cpu);
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	unsigned int level = 0, leaves = 0;
 
-static struct cache *cache_lookup_or_instantiate(struct device_node *node,
-						 int level)
-{
-	struct cache *cache;
-
-	cache = cache_lookup_by_node(node);
-
-	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, 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;
-}
-
-static void do_subsidiary_caches_debugcheck(struct cache *cache)
-{
-	WARN_ON_ONCE(cache->level != 1);
-	WARN_ON_ONCE(strcmp(cache->ofnode->type, "cpu"));
-}
-
-static void do_subsidiary_caches(struct cache *cache)
-{
-	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;
-
-		level++;
-		subcache = cache_lookup_or_instantiate(subcache_node, level);
-		of_node_put(subcache_node);
-		if (!subcache)
-			break;
-
-		link_cache_lists(cache, subcache);
-		cache = subcache;
-	}
-}
-
-static struct cache *cache_chain_instantiate(unsigned int cpu_id)
-{
-	struct device_node *cpu_node;
-	struct cache *cpu_cache = NULL;
-
-	pr_debug("creating cache object(s) for CPU %i\n", cpu_id);
-
-	cpu_node = of_get_cpu_node(cpu_id, NULL);
-	WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
-	if (!cpu_node)
-		goto out;
-
-	cpu_cache = cache_lookup_or_instantiate(cpu_node, 1);
-	if (!cpu_cache)
-		goto out;
-
-	do_subsidiary_caches(cpu_cache);
-
-	cache_cpu_set(cpu_cache, cpu_id);
-out:
-	of_node_put(cpu_node);
-
-	return cpu_cache;
-}
-
-static struct cache_dir *cacheinfo_create_cache_dir(unsigned int cpu_id)
-{
-	struct cache_dir *cache_dir;
-	struct device *dev;
-	struct kobject *kobj = NULL;
-
-	dev = get_cpu_device(cpu_id);
-	WARN_ONCE(!dev, "no dev for CPU %i\n", cpu_id);
-	if (!dev)
-		goto err;
-
-	kobj = kobject_create_and_add("cache", &dev->kobj);
-	if (!kobj)
-		goto err;
-
-	cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL);
-	if (!cache_dir)
-		goto err;
-
-	cache_dir->kobj = kobj;
-
-	WARN_ON_ONCE(per_cpu(cache_dir_pcpu, cpu_id) != NULL);
-
-	per_cpu(cache_dir_pcpu, cpu_id) = cache_dir;
-
-	return cache_dir;
-err:
-	kobject_put(kobj);
-	return NULL;
-}
-
-static void cache_index_release(struct kobject *kobj)
-{
-	struct cache_index_dir *index;
-
-	index = kobj_to_cache_index_dir(kobj);
-
-	pr_debug("freeing index directory for L%d %s cache\n",
-		 index->cache->level, cache_type_string(index->cache));
-
-	kfree(index);
-}
-
-static ssize_t cache_index_show(struct kobject *k, struct attribute *attr, char *buf)
-{
-	struct kobj_attribute *kobj_attr;
-
-	kobj_attr = container_of(attr, struct kobj_attribute, attr);
-
-	return kobj_attr->show(k, kobj_attr, buf);
-}
-
-static struct cache *index_kobj_to_cache(struct kobject *k)
-{
-	struct cache_index_dir *index;
-
-	index = kobj_to_cache_index_dir(k);
-
-	return index->cache;
-}
-
-static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int size_kb;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_size_kb(cache, &size_kb))
+	if (!cpu_dev) {
+		pr_err("No cpu device for CPU %d\n", cpu);
 		return -ENODEV;
-
-	return sprintf(buf, "%uK\n", size_kb);
-}
-
-static struct kobj_attribute cache_size_attr =
-	__ATTR(size, 0444, size_show, NULL);
-
-
-static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int line_size;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_get_line_size(cache, &line_size))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", line_size);
-}
-
-static struct kobj_attribute cache_line_size_attr =
-	__ATTR(coherency_line_size, 0444, line_size_show, NULL);
-
-static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int nr_sets;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_nr_sets(cache, &nr_sets))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", nr_sets);
-}
-
-static struct kobj_attribute cache_nr_sets_attr =
-	__ATTR(number_of_sets, 0444, nr_sets_show, NULL);
-
-static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	unsigned int associativity;
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	if (cache_associativity(cache, &associativity))
-		return -ENODEV;
-
-	return sprintf(buf, "%u\n", associativity);
-}
-
-static struct kobj_attribute cache_assoc_attr =
-	__ATTR(ways_of_associativity, 0444, associativity_show, NULL);
-
-static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache *cache;
-
-	cache = index_kobj_to_cache(k);
-
-	return sprintf(buf, "%s\n", cache_type_string(cache));
-}
-
-static struct kobj_attribute cache_type_attr =
-	__ATTR(type, 0444, type_show, NULL);
-
-static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache_index_dir *index;
-	struct cache *cache;
-
-	index = kobj_to_cache_index_dir(k);
-	cache = index->cache;
-
-	return sprintf(buf, "%d\n", cache->level);
-}
-
-static struct kobj_attribute cache_level_attr =
-	__ATTR(level, 0444, level_show, NULL);
-
-static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
-{
-	struct cache_index_dir *index;
-	struct cache *cache;
-	int len;
-	int n = 0;
-
-	index = kobj_to_cache_index_dir(k);
-	cache = index->cache;
-	len = PAGE_SIZE - 2;
-
-	if (len > 1) {
-		n = cpumask_scnprintf(buf, len, &cache->shared_cpu_map);
-		buf[n++] = '\n';
-		buf[n] = '\0';
 	}
-	return n;
-}
-
-static struct kobj_attribute cache_shared_cpu_map_attr =
-	__ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
-
-/* Attributes which should always be created -- the kobject/sysfs core
- * does this automatically via kobj_type->default_attrs.  This is the
- * minimum data required to uniquely identify a cache.
- */
-static struct attribute *cache_index_default_attrs[] = {
-	&cache_type_attr.attr,
-	&cache_level_attr.attr,
-	&cache_shared_cpu_map_attr.attr,
-	NULL,
-};
-
-/* Attributes which should be created if the cache device node has the
- * right properties -- see cacheinfo_create_index_opt_attrs
- */
-static struct kobj_attribute *cache_index_opt_attrs[] = {
-	&cache_size_attr,
-	&cache_line_size_attr,
-	&cache_nr_sets_attr,
-	&cache_assoc_attr,
-};
-
-static const struct sysfs_ops cache_index_ops = {
-	.show = cache_index_show,
-};
-
-static struct kobj_type cache_index_type = {
-	.release = cache_index_release,
-	.sysfs_ops = &cache_index_ops,
-	.default_attrs = cache_index_default_attrs,
-};
-
-static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir)
-{
-	const char *cache_name;
-	const char *cache_type;
-	struct cache *cache;
-	char *buf;
-	int i;
-
-	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!buf)
-		return;
-
-	cache = dir->cache;
-	cache_name = cache->ofnode->full_name;
-	cache_type = cache_type_string(cache);
-
-	/* We don't want to create an attribute that can't provide a
-	 * meaningful value.  Check the return value of each optional
-	 * attribute's ->show method before registering the
-	 * attribute.
-	 */
-	for (i = 0; i < ARRAY_SIZE(cache_index_opt_attrs); i++) {
-		struct kobj_attribute *attr;
-		ssize_t rc;
-
-		attr = cache_index_opt_attrs[i];
-
-		rc = attr->show(&dir->kobj, attr, buf);
-		if (rc <= 0) {
-			pr_debug("not creating %s attribute for "
-				 "%s(%s) (rc = %zd)\n",
-				 attr->attr.name, cache_name,
-				 cache_type, rc);
-			continue;
-		}
-		if (sysfs_create_file(&dir->kobj, &attr->attr))
-			pr_debug("could not create %s attribute for %s(%s)\n",
-				 attr->attr.name, cache_name, cache_type);
+	np = cpu_dev->of_node;
+	if (!np) {
+		pr_err("Failed to find cpu%d device node\n", cpu);
+		return -ENOENT;
 	}
 
-	kfree(buf);
-}
-
-static void cacheinfo_create_index_dir(struct cache *cache, int index,
-				       struct cache_dir *cache_dir)
-{
-	struct cache_index_dir *index_dir;
-	int rc;
-
-	index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL);
-	if (!index_dir)
-		goto err;
-
-	index_dir->cache = cache;
-
-	rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
-				  cache_dir->kobj, "index%d", index);
-	if (rc)
-		goto err;
-
-	index_dir->next = cache_dir->index;
-	cache_dir->index = index_dir;
-
-	cacheinfo_create_index_opt_attrs(index_dir);
-
-	return;
-err:
-	kfree(index_dir);
-}
-
-static void cacheinfo_sysfs_populate(unsigned int cpu_id,
-				     struct cache *cache_list)
-{
-	struct cache_dir *cache_dir;
-	struct cache *cache;
-	int index = 0;
-
-	cache_dir = cacheinfo_create_cache_dir(cpu_id);
-	if (!cache_dir)
-		return;
-
-	cache = cache_list;
-	while (cache) {
-		cacheinfo_create_index_dir(cache, index, cache_dir);
-		index++;
-		cache = cache->next_local;
+	while (np) {
+		leaves += cache_node_is_unified(np) ? 1 : 2;
+		level++;
+		of_node_put(np);
+		np = of_find_next_cache_node(np);
 	}
-}
-
-void cacheinfo_cpu_online(unsigned int cpu_id)
-{
-	struct cache *cache;
-
-	cache = cache_chain_instantiate(cpu_id);
-	if (!cache)
-		return;
-
-	cacheinfo_sysfs_populate(cpu_id, cache);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU /* functions needed for cpu offline */
+	this_cpu_ci->num_levels = level;
+	this_cpu_ci->num_leaves = leaves;
 
-static struct cache *cache_lookup_by_cpu(unsigned int cpu_id)
-{
-	struct device_node *cpu_node;
-	struct cache *cache;
-
-	cpu_node = of_get_cpu_node(cpu_id, NULL);
-	WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
-	if (!cpu_node)
-		return NULL;
-
-	cache = cache_lookup_by_node(cpu_node);
-	of_node_put(cpu_node);
-
-	return cache;
+	return 0;
 }
 
-static void remove_index_dirs(struct cache_dir *cache_dir)
+int populate_cache_leaves(unsigned int cpu)
 {
-	struct cache_index_dir *index;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+	struct device *cpu_dev = get_cpu_device(cpu);
+	struct device_node *np;
+	unsigned int level, idx;
 
-	index = cache_dir->index;
-
-	while (index) {
-		struct cache_index_dir *next;
-
-		next = index->next;
-		kobject_put(&index->kobj);
-		index = next;
+	np = of_node_get(cpu_dev->of_node);
+	if (!np) {
+		pr_err("Failed to find cpu%d device node\n", cpu);
+		return -ENOENT;
 	}
-}
 
-static void remove_cache_dir(struct cache_dir *cache_dir)
-{
-	remove_index_dirs(cache_dir);
-
-	kobject_put(cache_dir->kobj);
-
-	kfree(cache_dir);
-}
-
-static void cache_cpu_clear(struct cache *cache, int cpu)
-{
-	while (cache) {
-		struct cache *next = cache->next_local;
-
-		WARN_ONCE(!cpumask_test_cpu(cpu, &cache->shared_cpu_map),
-			  "CPU %i not accounted in %s(%s)\n",
-			  cpu, cache->ofnode->full_name,
-			  cache_type_string(cache));
-
-		cpumask_clear_cpu(cpu, &cache->shared_cpu_map);
-
-		/* Release the cache object if all the cpus using it
-		 * are offline */
-		if (cpumask_empty(&cache->shared_cpu_map))
-			release_cache(cache);
-
-		cache = next;
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		this_leaf->of_node = np;
+		if (cache_node_is_unified(np)) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_UNIFIED, level);
+		} else {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		}
+		np = of_find_next_cache_node(np);
 	}
+	return 0;
 }
 
-void cacheinfo_cpu_offline(unsigned int cpu_id)
-{
-	struct cache_dir *cache_dir;
-	struct cache *cache;
-
-	/* Prevent userspace from seeing inconsistent state - remove
-	 * the sysfs hierarchy first */
-	cache_dir = per_cpu(cache_dir_pcpu, cpu_id);
-
-	/* careful, sysfs population may have failed */
-	if (cache_dir)
-		remove_cache_dir(cache_dir);
-
-	per_cpu(cache_dir_pcpu, cpu_id) = NULL;
-
-	/* clear the CPU's bit in its cache chain, possibly freeing
-	 * cache objects */
-	cache = cache_lookup_by_cpu(cpu_id);
-	if (cache)
-		cache_cpu_clear(cache, cpu_id);
-}
-#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/powerpc/kernel/cacheinfo.h b/arch/powerpc/kernel/cacheinfo.h
deleted file mode 100644
index a7b74d3..0000000
--- a/arch/powerpc/kernel/cacheinfo.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _PPC_CACHEINFO_H
-#define _PPC_CACHEINFO_H
-
-/* These are just hooks for sysfs.c to use. */
-extern void cacheinfo_cpu_online(unsigned int cpu_id);
-extern void cacheinfo_cpu_offline(unsigned int cpu_id);
-
-#endif /* _PPC_CACHEINFO_H */
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index d4a43e6..935929b 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -19,8 +19,6 @@
 #include <asm/pmc.h>
 #include <asm/firmware.h>
 
-#include "cacheinfo.h"
-
 #ifdef CONFIG_PPC64
 #include <asm/paca.h>
 #include <asm/lppaca.h>
@@ -732,7 +730,6 @@ static void register_cpu_online(unsigned int cpu)
 		device_create_file(s, &dev_attr_altivec_idle_wait_time);
 	}
 #endif
-	cacheinfo_cpu_online(cpu);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -813,7 +810,6 @@ static void unregister_cpu_online(unsigned int cpu)
 		device_remove_file(s, &dev_attr_altivec_idle_wait_time);
 	}
 #endif
-	cacheinfo_cpu_offline(cpu);
 }
 
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 6/8] ARM64: kernel: add support for cpu cache information
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49   ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: sudeep.holla, Catalin Marinas, Will Deacon, Lorenzo Pieralisi,
	linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM64.

On ARMv8, the cache hierarchy can be identified through Cache Level ID
(CLIDR) register while the cache geometry is provided by Cache Size ID
(CCSIDR) register.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used for the same purpose.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
 arch/arm64/kernel/Makefile    |   2 +-
 arch/arm64/kernel/cacheinfo.c | 134 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/kernel/cacheinfo.c

diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b..7d94a51 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -9,7 +9,7 @@ AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)
 arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\
 			   entry-fpsimd.o process.o ptrace.o setup.o signal.o	\
 			   sys.o stacktrace.o time.o traps.o io.o vdso.o	\
-			   hyp-stub.o psci.o cpu_ops.o insn.o
+			   hyp-stub.o psci.o cpu_ops.o insn.o cacheinfo.o
 
 arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
 					   sys_compat.o
diff --git a/arch/arm64/kernel/cacheinfo.c b/arch/arm64/kernel/cacheinfo.c
new file mode 100644
index 0000000..5d8b840
--- /dev/null
+++ b/arch/arm64/kernel/cacheinfo.c
@@ -0,0 +1,134 @@
+/*
+ *  ARM64 cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/processor.h>
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrs     %0, clidr_el1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile("msr csselr_el1, %x0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((level - 1) << 1 | is_instr_cache);
+
+	this_leaf->level = level;
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 6/8] ARM64: kernel: add support for cpu cache information
@ 2014-02-07 16:49   ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM64.

On ARMv8, the cache hierarchy can be identified through Cache Level ID
(CLIDR) register while the cache geometry is provided by Cache Size ID
(CCSIDR) register.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used for the same purpose.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm64/kernel/Makefile    |   2 +-
 arch/arm64/kernel/cacheinfo.c | 134 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/kernel/cacheinfo.c

diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b..7d94a51 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -9,7 +9,7 @@ AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)
 arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\
 			   entry-fpsimd.o process.o ptrace.o setup.o signal.o	\
 			   sys.o stacktrace.o time.o traps.o io.o vdso.o	\
-			   hyp-stub.o psci.o cpu_ops.o insn.o
+			   hyp-stub.o psci.o cpu_ops.o insn.o cacheinfo.o
 
 arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
 					   sys_compat.o
diff --git a/arch/arm64/kernel/cacheinfo.c b/arch/arm64/kernel/cacheinfo.c
new file mode 100644
index 0000000..5d8b840
--- /dev/null
+++ b/arch/arm64/kernel/cacheinfo.c
@@ -0,0 +1,134 @@
+/*
+ *  ARM64 cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/processor.h>
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrs     %0, clidr_el1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile("msr csselr_el1, %x0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((level - 1) << 1 | is_instr_cache);
+
+	this_leaf->level = level;
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 7/8] ARM: kernel: add support for cpu cache information
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49   ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: sudeep.holla, Russell King, Will Deacon, Nicolas Pitre,
	Lorenzo Pieralisi, linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM platforms.

On ARMv7, the cache hierarchy can be identified through Cache Level ID
register(CLIDR) while the cache geometry is provided by Cache Size ID
register(CCSIDR).

On architecture versions before ARMv7, CLIDR and CCSIDR is not
implemented. The cache type register(CTR) provides both cache hierarchy
and geometry if implemented. For implementations that doesn't support
CTR, we need to list the probable value of CTR if it was implemented
along with the cpuid for the sake of simplicity to handle them.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used fo the same purpose.
On non-DT platforms, first level caches are per-cpu while higher level
caches are assumed system-wide.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Nicolas Pitre <nicolas.pitre@linaro.org>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
 arch/arm/kernel/Makefile    |   1 +
 arch/arm/kernel/cacheinfo.c | 228 ++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mm/Kconfig         |  13 +++
 3 files changed, 242 insertions(+)
 create mode 100644 arch/arm/kernel/cacheinfo.c

diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a30fc9b..f86a4ff 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -29,6 +29,7 @@ obj-y		+= entry-v7m.o v7m.o
 else
 obj-y		+= entry-armv.o
 endif
+obj-$(CONFIG_CPU_HAS_CACHE) += cacheinfo.o
 
 obj-$(CONFIG_OC_ETM)		+= etm.o
 obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
new file mode 100644
index 0000000..10045b7
--- /dev/null
+++ b/arch/arm/kernel/cacheinfo.c
@@ -0,0 +1,228 @@
+/*
+ *  ARM cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/cputype.h>
+#include <asm/processor.h>
+
+#if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
+
+#define MAX_CACHE_LEVEL		1	/* Only 1 level supported */
+#define CTR_CTYPE_SHIFT		24
+#define CTR_CTYPE_MASK		(1 << CTR_CTYPE_SHIFT)
+
+struct ctr_info {
+	unsigned int cpuid_id;
+	unsigned int ctr;
+};
+
+static struct ctr_info cache_ctr_list[] = {
+};
+
+static int get_unimplemented_ctr(unsigned int *ctr)
+{
+	int i, cpuid_id = read_cpuid_id();
+	for (i = 0; i < ARRAY_SIZE(cache_ctr_list); i++)
+		if (cache_ctr_list[i].cpuid_id == cpuid_id) {
+			*ctr = cache_ctr_list[i].ctr;
+			return 0;
+		}
+	return -ENOENT;
+}
+
+static unsigned int get_ctr(void)
+{
+	unsigned int ctr;
+	if (get_unimplemented_ctr(&ctr))
+		asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
+	return ctr;
+}
+
+static enum cache_type get_cache_type(int level)
+{
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	return get_ctr() & CTR_CTYPE_MASK ?
+		CACHE_TYPE_SEPARATE : CACHE_TYPE_UNIFIED;
+}
+
+/*
+ *  +---------------------------------+
+ *  | 9  8  7  6 | 5  4  3 | 2 | 1  0 |
+ *  +---------------------------------+
+ *  |    size    |  assoc  | m |  len |
+ *  +---------------------------------+
+ * linelen        = 1 << (len + 3)
+ * multiplier     = 2 + m
+ * nsets          = 1 << (size + 6 - assoc - len)
+ * associativity  = multiplier << (assoc - 1)
+ * cache_size     = multiplier << (size + 8)
+ */
+#define CTR_LINESIZE_MASK	0x3
+#define CTR_MULTIPLIER_SHIFT	2
+#define CTR_MULTIPLIER_MASK	0x1
+#define CTR_ASSOCIAT_SHIFT	3
+#define CTR_ASSOCIAT_MASK	0x7
+#define CTR_SIZE_SHIFT		6
+#define CTR_SIZE_MASK		0xF
+#define CTR_DCACHE_SHIFT	12
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	unsigned int size, multiplier, assoc, len, tmp = get_ctr();
+
+	if (type == CACHE_TYPE_DATA)
+		tmp >>= CTR_DCACHE_SHIFT;
+
+	len = tmp & CTR_LINESIZE_MASK;
+	size = (tmp >> CTR_SIZE_SHIFT) & CTR_SIZE_MASK;
+	assoc = (tmp >> CTR_ASSOCIAT_SHIFT) & CTR_ASSOCIAT_MASK;
+	multiplier = ((tmp >> CTR_MULTIPLIER_SHIFT) & CTR_MULTIPLIER_MASK) + 2;
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size = 1 << (len + 3);
+	this_leaf->number_of_sets = 1 << (size + 6 - assoc - len);
+	this_leaf->ways_of_associativity = multiplier << (assoc - 1);
+	this_leaf->size = multiplier << (size + 8);
+}
+
+#else /* ARMv7 */
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrc p15, 1, %0, c0, c0, 1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((this_leaf->level - 1) << 1 | is_instr_cache);
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+#endif
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	this_leaf->level = level;
+	__ci_leaf_init(type, this_leaf);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 1f8fed9..c4abb89 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -495,30 +495,42 @@ config CPU_PABRT_V7
 # The cache model
 config CPU_CACHE_V4
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WB
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V6
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V7
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_NOP
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIVT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIPT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_FA
 	bool
+	select CPU_HAS_CACHE
+
+config CPU_HAS_CACHE
+	bool
 
 if MMU
 # The copy-page model
@@ -846,6 +858,7 @@ config DMA_CACHE_RWFO
 
 config OUTER_CACHE
 	bool
+	select CPU_HAS_CACHE
 
 config OUTER_CACHE_SYNC
 	bool
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 7/8] ARM: kernel: add support for cpu cache information
@ 2014-02-07 16:49   ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM platforms.

On ARMv7, the cache hierarchy can be identified through Cache Level ID
register(CLIDR) while the cache geometry is provided by Cache Size ID
register(CCSIDR).

On architecture versions before ARMv7, CLIDR and CCSIDR is not
implemented. The cache type register(CTR) provides both cache hierarchy
and geometry if implemented. For implementations that doesn't support
CTR, we need to list the probable value of CTR if it was implemented
along with the cpuid for the sake of simplicity to handle them.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used fo the same purpose.
On non-DT platforms, first level caches are per-cpu while higher level
caches are assumed system-wide.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Nicolas Pitre <nicolas.pitre@linaro.org>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm/kernel/Makefile    |   1 +
 arch/arm/kernel/cacheinfo.c | 228 ++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mm/Kconfig         |  13 +++
 3 files changed, 242 insertions(+)
 create mode 100644 arch/arm/kernel/cacheinfo.c

diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a30fc9b..f86a4ff 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -29,6 +29,7 @@ obj-y		+= entry-v7m.o v7m.o
 else
 obj-y		+= entry-armv.o
 endif
+obj-$(CONFIG_CPU_HAS_CACHE) += cacheinfo.o
 
 obj-$(CONFIG_OC_ETM)		+= etm.o
 obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
new file mode 100644
index 0000000..10045b7
--- /dev/null
+++ b/arch/arm/kernel/cacheinfo.c
@@ -0,0 +1,228 @@
+/*
+ *  ARM cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/cputype.h>
+#include <asm/processor.h>
+
+#if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
+
+#define MAX_CACHE_LEVEL		1	/* Only 1 level supported */
+#define CTR_CTYPE_SHIFT		24
+#define CTR_CTYPE_MASK		(1 << CTR_CTYPE_SHIFT)
+
+struct ctr_info {
+	unsigned int cpuid_id;
+	unsigned int ctr;
+};
+
+static struct ctr_info cache_ctr_list[] = {
+};
+
+static int get_unimplemented_ctr(unsigned int *ctr)
+{
+	int i, cpuid_id = read_cpuid_id();
+	for (i = 0; i < ARRAY_SIZE(cache_ctr_list); i++)
+		if (cache_ctr_list[i].cpuid_id == cpuid_id) {
+			*ctr = cache_ctr_list[i].ctr;
+			return 0;
+		}
+	return -ENOENT;
+}
+
+static unsigned int get_ctr(void)
+{
+	unsigned int ctr;
+	if (get_unimplemented_ctr(&ctr))
+		asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
+	return ctr;
+}
+
+static enum cache_type get_cache_type(int level)
+{
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	return get_ctr() & CTR_CTYPE_MASK ?
+		CACHE_TYPE_SEPARATE : CACHE_TYPE_UNIFIED;
+}
+
+/*
+ *  +---------------------------------+
+ *  | 9  8  7  6 | 5  4  3 | 2 | 1  0 |
+ *  +---------------------------------+
+ *  |    size    |  assoc  | m |  len |
+ *  +---------------------------------+
+ * linelen        = 1 << (len + 3)
+ * multiplier     = 2 + m
+ * nsets          = 1 << (size + 6 - assoc - len)
+ * associativity  = multiplier << (assoc - 1)
+ * cache_size     = multiplier << (size + 8)
+ */
+#define CTR_LINESIZE_MASK	0x3
+#define CTR_MULTIPLIER_SHIFT	2
+#define CTR_MULTIPLIER_MASK	0x1
+#define CTR_ASSOCIAT_SHIFT	3
+#define CTR_ASSOCIAT_MASK	0x7
+#define CTR_SIZE_SHIFT		6
+#define CTR_SIZE_MASK		0xF
+#define CTR_DCACHE_SHIFT	12
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	unsigned int size, multiplier, assoc, len, tmp = get_ctr();
+
+	if (type == CACHE_TYPE_DATA)
+		tmp >>= CTR_DCACHE_SHIFT;
+
+	len = tmp & CTR_LINESIZE_MASK;
+	size = (tmp >> CTR_SIZE_SHIFT) & CTR_SIZE_MASK;
+	assoc = (tmp >> CTR_ASSOCIAT_SHIFT) & CTR_ASSOCIAT_MASK;
+	multiplier = ((tmp >> CTR_MULTIPLIER_SHIFT) & CTR_MULTIPLIER_MASK) + 2;
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size = 1 << (len + 3);
+	this_leaf->number_of_sets = 1 << (size + 6 - assoc - len);
+	this_leaf->ways_of_associativity = multiplier << (assoc - 1);
+	this_leaf->size = multiplier << (size + 8);
+}
+
+#else /* ARMv7 */
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrc p15, 1, %0, c0, c0, 1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((this_leaf->level - 1) << 1 | is_instr_cache);
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+#endif
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	this_leaf->level = level;
+	__ci_leaf_init(type, this_leaf);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 1f8fed9..c4abb89 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -495,30 +495,42 @@ config CPU_PABRT_V7
 # The cache model
 config CPU_CACHE_V4
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WB
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V6
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V7
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_NOP
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIVT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIPT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_FA
 	bool
+	select CPU_HAS_CACHE
+
+config CPU_HAS_CACHE
+	bool
 
 if MMU
 # The copy-page model
@@ -846,6 +858,7 @@ config DMA_CACHE_RWFO
 
 config OUTER_CACHE
 	bool
+	select CPU_HAS_CACHE
 
 config OUTER_CACHE_SYNC
 	bool
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 8/8] ARM: kernel: add outer cache support for cacheinfo implementation
  2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
@ 2014-02-07 16:49   ` Sudeep Holla
  2014-02-07 16:49   ` Sudeep Holla
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-kernel
  Cc: sudeep.holla, Russell King, Will Deacon, Lorenzo Pieralisi,
	linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

In order to support outer cache in the cacheinfo infrastructure, a new
function 'get_info' is added to outer_cache_fns. This function is used
to get the outer cache information namely: line size, number of ways of
associativity and number of sets.

This patch adds 'get_info' supports to all L2 cache implementations on
ARM except Marvell's Feroceon L2 cache.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
 arch/arm/include/asm/outercache.h | 13 +++++++++++++
 arch/arm/kernel/cacheinfo.c       | 22 +++++++++++++++++++++-
 arch/arm/mm/cache-l2x0.c          | 14 ++++++++++++++
 arch/arm/mm/cache-tauros2.c       | 35 +++++++++++++++++++++++++++++++++++
 arch/arm/mm/cache-xsc3l2.c        | 15 +++++++++++++++
 5 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index f94784f..1471ff2 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -23,7 +23,14 @@
 
 #include <linux/types.h>
 
+struct outer_cache_info {
+	unsigned int num_ways;
+	unsigned int num_sets;
+	unsigned int line_size;
+};
+
 struct outer_cache_fns {
+	void (*get_info)(struct outer_cache_info *info);
 	void (*inv_range)(unsigned long, unsigned long);
 	void (*clean_range)(unsigned long, unsigned long);
 	void (*flush_range)(unsigned long, unsigned long);
@@ -41,6 +48,11 @@ extern struct outer_cache_fns outer_cache;
 
 #ifdef CONFIG_OUTER_CACHE
 
+static inline void outer_get_info(struct outer_cache_info *info)
+{
+	if (outer_cache.get_info)
+		outer_cache.get_info(info);
+}
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.inv_range)
@@ -83,6 +95,7 @@ static inline void outer_resume(void)
 
 #else
 
+static inline void outer_get_info(struct outer_cache_info *info) { }
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 { }
 static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 10045b7..4463833 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 
 #include <asm/cputype.h>
+#include <asm/outercache.h>
 #include <asm/processor.h>
 
 #if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
@@ -175,11 +176,27 @@ static void __ci_leaf_init(enum cache_type type,
 
 #endif
 
+static void __outer_ci_leaf_init(struct cache_info *this_leaf)
+{
+	struct outer_cache_info info;
+
+	outer_get_info(&info);
+
+	this_leaf->type = CACHE_TYPE_UNIFIED;/* record it as Unified */
+	this_leaf->ways_of_associativity = info.num_ways;
+	this_leaf->number_of_sets = info.num_sets;
+	this_leaf->coherency_line_size = info.line_size;
+	this_leaf->size = info.num_ways * info.num_sets * info.line_size;
+}
+
 static void ci_leaf_init(struct cache_info *this_leaf,
 			 enum cache_type type, unsigned int level)
 {
 	this_leaf->level = level;
-	__ci_leaf_init(type, this_leaf);
+	if (type == CACHE_TYPE_NOCACHE)	/* must be outer cache */
+		__outer_ci_leaf_init(this_leaf);
+	else
+		__ci_leaf_init(type, this_leaf);
 }
 
 int init_cache_level(unsigned int cpu)
@@ -201,6 +218,9 @@ int init_cache_level(unsigned int cpu)
 	this_cpu_ci->num_levels = level - 1;
 	this_cpu_ci->num_leaves = leaves;
 
+	if (IS_ENABLED(CONFIG_OUTER_CACHE) && outer_cache.get_info)
+		this_cpu_ci->num_leaves++, this_cpu_ci->num_levels++;
+
 	return 0;
 }
 
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 7abde2ce..b850771 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -183,6 +183,15 @@ static void l2x0_inv_all(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_getinfo(struct outer_cache_info *info)
+{
+	if (!info)
+		return;
+	info->num_ways = get_count_order(l2x0_way_mask);
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = l2x0_size / (info->num_ways * CACHE_LINE_SIZE);
+}
+
 static void l2x0_inv_range(unsigned long start, unsigned long end)
 {
 	void __iomem *base = l2x0_base;
@@ -416,6 +425,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		outer_cache.flush_all = l2x0_flush_all;
 		outer_cache.inv_all = l2x0_inv_all;
 		outer_cache.disable = l2x0_disable;
+		outer_cache.get_info = l2x0_getinfo;
 	}
 
 	pr_info("%s cache controller enabled\n", type);
@@ -886,6 +896,7 @@ static const struct l2x0_of_data pl310_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -901,6 +912,7 @@ static const struct l2x0_of_data l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -916,6 +928,7 @@ static const struct l2x0_of_data aurora_with_outer_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -948,6 +961,7 @@ static const struct l2x0_of_data bcm_l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c
index 1be0f4e..3a43401 100644
--- a/arch/arm/mm/cache-tauros2.c
+++ b/arch/arm/mm/cache-tauros2.c
@@ -60,6 +60,7 @@ static inline void tauros2_inv_pa(unsigned long addr)
  * noninclusive.
  */
 #define CACHE_LINE_SIZE		32
+#define CACHE_LINE_SHIFT	5
 
 static void tauros2_inv_range(unsigned long start, unsigned long end)
 {
@@ -131,6 +132,38 @@ static void tauros2_resume(void)
 	"mcr	p15, 0, %0, c1, c0, 0 @Enable L2 Cache\n\t"
 	: : "r" (0x0));
 }
+
+/*
+ *  +----------------------------------------+
+ *  | 11 10 9  8 | 7  6  5  4  3 | 2 |  1  0 |
+ *  +----------------------------------------+
+ *  |  way size  | associativity | - |line_sz|
+ *  +----------------------------------------+
+ */
+#define L2CTR_ASSOCIAT_SHIFT	3
+#define L2CTR_ASSOCIAT_MASK	0x1F
+#define L2CTR_WAYSIZE_SHIFT	8
+#define L2CTR_WAYSIZE_MASK	0xF
+#define CACHE_WAY_PER_SET(l2ctr)	\
+	(((l2_ctr) >> L2CTR_ASSOCIAT_SHIFT) & L2CTR_ASSOCIAT_MASK)
+#define CACHE_WAY_SIZE(l2ctr)		\
+	(8192 << (((l2ctr) >> L2CTR_WAYSIZE_SHIFT) & L2CTR_WAYSIZE_MASK))
+#define CACHE_SET_SIZE(l2ctr)	(CACHE_WAY_SIZE(l2ctr) >> CACHE_LINE_SHIFT)
+
+static void tauros2_getinfo(struct outer_cache_info *info)
+{
+	unsigned int l2_ctr;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2_ctr));
+
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_ways = CACHE_WAY_PER_SET(l2_ctr);
+	info->num_sets = CACHE_SET_SIZE(l2_ctr);
+}
+
 #endif
 
 static inline u32 __init read_extra_features(void)
@@ -226,6 +259,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
@@ -253,6 +287,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
diff --git a/arch/arm/mm/cache-xsc3l2.c b/arch/arm/mm/cache-xsc3l2.c
index 6c3edeb..353c642 100644
--- a/arch/arm/mm/cache-xsc3l2.c
+++ b/arch/arm/mm/cache-xsc3l2.c
@@ -201,6 +201,20 @@ static void xsc3_l2_flush_range(unsigned long start, unsigned long end)
 	dsb();
 }
 
+static void xsc3_l2_getinfo(struct outer_cache_info *info)
+{
+	unsigned long l2ctype;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+	info->num_ways = CACHE_WAY_PER_SET;
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = CACHE_SET_SIZE(l2ctype);
+}
+
 static int __init xsc3_l2_init(void)
 {
 	if (!cpu_is_xsc3() || !xsc3_l2_present())
@@ -213,6 +227,7 @@ static int __init xsc3_l2_init(void)
 		outer_cache.inv_range = xsc3_l2_inv_range;
 		outer_cache.clean_range = xsc3_l2_clean_range;
 		outer_cache.flush_range = xsc3_l2_flush_range;
+		outer_cache.get_info    = xsc3_l2_getinfo;
 	}
 
 	return 0;
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH RFC/RFT v2 8/8] ARM: kernel: add outer cache support for cacheinfo implementation
@ 2014-02-07 16:49   ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel

From: Sudeep Holla <sudeep.holla@arm.com>

In order to support outer cache in the cacheinfo infrastructure, a new
function 'get_info' is added to outer_cache_fns. This function is used
to get the outer cache information namely: line size, number of ways of
associativity and number of sets.

This patch adds 'get_info' supports to all L2 cache implementations on
ARM except Marvell's Feroceon L2 cache.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm/include/asm/outercache.h | 13 +++++++++++++
 arch/arm/kernel/cacheinfo.c       | 22 +++++++++++++++++++++-
 arch/arm/mm/cache-l2x0.c          | 14 ++++++++++++++
 arch/arm/mm/cache-tauros2.c       | 35 +++++++++++++++++++++++++++++++++++
 arch/arm/mm/cache-xsc3l2.c        | 15 +++++++++++++++
 5 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index f94784f..1471ff2 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -23,7 +23,14 @@
 
 #include <linux/types.h>
 
+struct outer_cache_info {
+	unsigned int num_ways;
+	unsigned int num_sets;
+	unsigned int line_size;
+};
+
 struct outer_cache_fns {
+	void (*get_info)(struct outer_cache_info *info);
 	void (*inv_range)(unsigned long, unsigned long);
 	void (*clean_range)(unsigned long, unsigned long);
 	void (*flush_range)(unsigned long, unsigned long);
@@ -41,6 +48,11 @@ extern struct outer_cache_fns outer_cache;
 
 #ifdef CONFIG_OUTER_CACHE
 
+static inline void outer_get_info(struct outer_cache_info *info)
+{
+	if (outer_cache.get_info)
+		outer_cache.get_info(info);
+}
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.inv_range)
@@ -83,6 +95,7 @@ static inline void outer_resume(void)
 
 #else
 
+static inline void outer_get_info(struct outer_cache_info *info) { }
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 { }
 static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 10045b7..4463833 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 
 #include <asm/cputype.h>
+#include <asm/outercache.h>
 #include <asm/processor.h>
 
 #if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
@@ -175,11 +176,27 @@ static void __ci_leaf_init(enum cache_type type,
 
 #endif
 
+static void __outer_ci_leaf_init(struct cache_info *this_leaf)
+{
+	struct outer_cache_info info;
+
+	outer_get_info(&info);
+
+	this_leaf->type = CACHE_TYPE_UNIFIED;/* record it as Unified */
+	this_leaf->ways_of_associativity = info.num_ways;
+	this_leaf->number_of_sets = info.num_sets;
+	this_leaf->coherency_line_size = info.line_size;
+	this_leaf->size = info.num_ways * info.num_sets * info.line_size;
+}
+
 static void ci_leaf_init(struct cache_info *this_leaf,
 			 enum cache_type type, unsigned int level)
 {
 	this_leaf->level = level;
-	__ci_leaf_init(type, this_leaf);
+	if (type == CACHE_TYPE_NOCACHE)	/* must be outer cache */
+		__outer_ci_leaf_init(this_leaf);
+	else
+		__ci_leaf_init(type, this_leaf);
 }
 
 int init_cache_level(unsigned int cpu)
@@ -201,6 +218,9 @@ int init_cache_level(unsigned int cpu)
 	this_cpu_ci->num_levels = level - 1;
 	this_cpu_ci->num_leaves = leaves;
 
+	if (IS_ENABLED(CONFIG_OUTER_CACHE) && outer_cache.get_info)
+		this_cpu_ci->num_leaves++, this_cpu_ci->num_levels++;
+
 	return 0;
 }
 
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 7abde2ce..b850771 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -183,6 +183,15 @@ static void l2x0_inv_all(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_getinfo(struct outer_cache_info *info)
+{
+	if (!info)
+		return;
+	info->num_ways = get_count_order(l2x0_way_mask);
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = l2x0_size / (info->num_ways * CACHE_LINE_SIZE);
+}
+
 static void l2x0_inv_range(unsigned long start, unsigned long end)
 {
 	void __iomem *base = l2x0_base;
@@ -416,6 +425,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		outer_cache.flush_all = l2x0_flush_all;
 		outer_cache.inv_all = l2x0_inv_all;
 		outer_cache.disable = l2x0_disable;
+		outer_cache.get_info = l2x0_getinfo;
 	}
 
 	pr_info("%s cache controller enabled\n", type);
@@ -886,6 +896,7 @@ static const struct l2x0_of_data pl310_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -901,6 +912,7 @@ static const struct l2x0_of_data l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -916,6 +928,7 @@ static const struct l2x0_of_data aurora_with_outer_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -948,6 +961,7 @@ static const struct l2x0_of_data bcm_l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c
index 1be0f4e..3a43401 100644
--- a/arch/arm/mm/cache-tauros2.c
+++ b/arch/arm/mm/cache-tauros2.c
@@ -60,6 +60,7 @@ static inline void tauros2_inv_pa(unsigned long addr)
  * noninclusive.
  */
 #define CACHE_LINE_SIZE		32
+#define CACHE_LINE_SHIFT	5
 
 static void tauros2_inv_range(unsigned long start, unsigned long end)
 {
@@ -131,6 +132,38 @@ static void tauros2_resume(void)
 	"mcr	p15, 0, %0, c1, c0, 0 @Enable L2 Cache\n\t"
 	: : "r" (0x0));
 }
+
+/*
+ *  +----------------------------------------+
+ *  | 11 10 9  8 | 7  6  5  4  3 | 2 |  1  0 |
+ *  +----------------------------------------+
+ *  |  way size  | associativity | - |line_sz|
+ *  +----------------------------------------+
+ */
+#define L2CTR_ASSOCIAT_SHIFT	3
+#define L2CTR_ASSOCIAT_MASK	0x1F
+#define L2CTR_WAYSIZE_SHIFT	8
+#define L2CTR_WAYSIZE_MASK	0xF
+#define CACHE_WAY_PER_SET(l2ctr)	\
+	(((l2_ctr) >> L2CTR_ASSOCIAT_SHIFT) & L2CTR_ASSOCIAT_MASK)
+#define CACHE_WAY_SIZE(l2ctr)		\
+	(8192 << (((l2ctr) >> L2CTR_WAYSIZE_SHIFT) & L2CTR_WAYSIZE_MASK))
+#define CACHE_SET_SIZE(l2ctr)	(CACHE_WAY_SIZE(l2ctr) >> CACHE_LINE_SHIFT)
+
+static void tauros2_getinfo(struct outer_cache_info *info)
+{
+	unsigned int l2_ctr;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2_ctr));
+
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_ways = CACHE_WAY_PER_SET(l2_ctr);
+	info->num_sets = CACHE_SET_SIZE(l2_ctr);
+}
+
 #endif
 
 static inline u32 __init read_extra_features(void)
@@ -226,6 +259,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
@@ -253,6 +287,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
diff --git a/arch/arm/mm/cache-xsc3l2.c b/arch/arm/mm/cache-xsc3l2.c
index 6c3edeb..353c642 100644
--- a/arch/arm/mm/cache-xsc3l2.c
+++ b/arch/arm/mm/cache-xsc3l2.c
@@ -201,6 +201,20 @@ static void xsc3_l2_flush_range(unsigned long start, unsigned long end)
 	dsb();
 }
 
+static void xsc3_l2_getinfo(struct outer_cache_info *info)
+{
+	unsigned long l2ctype;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+	info->num_ways = CACHE_WAY_PER_SET;
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = CACHE_SET_SIZE(l2ctype);
+}
+
 static int __init xsc3_l2_init(void)
 {
 	if (!cpu_is_xsc3() || !xsc3_l2_present())
@@ -213,6 +227,7 @@ static int __init xsc3_l2_init(void)
 		outer_cache.inv_range = xsc3_l2_inv_range;
 		outer_cache.clean_range = xsc3_l2_clean_range;
 		outer_cache.flush_range = xsc3_l2_flush_range;
+		outer_cache.get_info    = xsc3_l2_getinfo;
 	}
 
 	return 0;
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
@ 2014-02-07 19:29   ` Greg Kroah-Hartman
  2014-02-10 18:09     ` Sudeep Holla
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2014-02-07 19:29 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Rob Herring, linux-doc

On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
> From: Sudeep Holla <sudeep.holla@arm.com>
> 
> This patch adds initial support for providing processor cache information
> to userspace through sysfs interface. This is based on already existing
> implementations(x86, ia64, s390 and powerpc) and hence the interface is
> intended to be fully compatible.
> 
> The main purpose of this generic support is to avoid further code
> duplication to support new architectures and also to unify all the existing
> different implementations.
> 
> This implementation maintains the hierarchy of cache objects which reflects
> the system's cache topology. Cache objects are instantiated as needed as
> CPUs come online. The cache objects are replicated per-cpu even if they are
> shared. A per-cpu array of cache information maintained is used mainly for
> sysfs-related book keeping.

I thought I asked that you not use "raw" kobjects for this, instead
using 'struct device' or just an attribute group?

Using a kobject means that userspace tools that use libudev have no
chance to see these attributes at all, which is not good, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 3/8] s390: " Sudeep Holla
@ 2014-02-10  9:50   ` Heiko Carstens
  2014-02-10 11:34     ` Sudeep Holla
  2014-02-10 11:30   ` Heiko Carstens
  1 sibling, 1 reply; 28+ messages in thread
From: Heiko Carstens @ 2014-02-10  9:50 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Martin Schwidefsky, linux390, linux-s390

On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
> From: Sudeep Holla <sudeep.holla@arm.com>
> 
> This patch removes the redundant sysfs cacheinfo code by making use of
> the newly introduced generic cacheinfo infrastructure.
> 
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
> Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
> Cc: linux390@de.ibm.com
> Cc: linux-s390@vger.kernel.org
> ---

[...]

> diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
> index 2461202..1f875db 100644
> --- a/arch/s390/kernel/processor.c
> +++ b/arch/s390/kernel/processor.c
> @@ -58,7 +58,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>  			if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
>  				seq_printf(m, "%s ", hwcap_str[i]);
>  		seq_puts(m, "\n");
> -		show_cacheinfo(m);
>  	}
>  	get_online_cpus();
>  	if (cpu_online(n)) {

You can't remove that. It's a user space visible change.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-07 16:49 ` [PATCH RFC/RFT v2 3/8] s390: " Sudeep Holla
  2014-02-10  9:50   ` Heiko Carstens
@ 2014-02-10 11:30   ` Heiko Carstens
  2014-02-10 11:36     ` Sudeep Holla
  2014-02-17 18:36     ` Sudeep Holla
  1 sibling, 2 replies; 28+ messages in thread
From: Heiko Carstens @ 2014-02-10 11:30 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Martin Schwidefsky, linux390, linux-s390

On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
> From: Sudeep Holla <sudeep.holla@arm.com>
> 
> This patch removes the redundant sysfs cacheinfo code by making use of
> the newly introduced generic cacheinfo infrastructure.
> 
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

[...]

> -static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
> -{
> -	struct cache_index_dir *index;
> -	int len;
> +	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);
> +	if (private)
> +		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
> +	else /* System wide */
> +		cpumask_copy(&this_leaf->shared_cpu_map, cpu_online_mask);

FWIW, this also seems to be wrong: on s390 we only expose cpu private
caches via sysfs. Shared caches are not exposed (and simply saying they are
system wide would be wrong).
Please have a look at these two commits which should describe why things
are as they are on s390:

881730ad365130f64b5c70c40904b04eb3b79de3
 "s390/cache: expose cpu cache topology via sysfs"
6668022c7bde3fdc96d3d257294a7216c7a46829
 "s390/cache: add cpu cache information to /proc/cpuinfo"


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-10  9:50   ` Heiko Carstens
@ 2014-02-10 11:34     ` Sudeep Holla
  2014-02-10 11:59       ` Heiko Carstens
  0 siblings, 1 reply; 28+ messages in thread
From: Sudeep Holla @ 2014-02-10 11:34 UTC (permalink / raw)
  To: Heiko Carstens
  Cc: Sudeep.Holla, linux-kernel, Martin Schwidefsky, linux390, linux-s390

On 10/02/14 09:50, Heiko Carstens wrote:
> On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
>> From: Sudeep Holla <sudeep.holla@arm.com>
>>
>> This patch removes the redundant sysfs cacheinfo code by making use of
>> the newly introduced generic cacheinfo infrastructure.
>>
>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
>> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
>> Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
>> Cc: linux390@de.ibm.com
>> Cc: linux-s390@vger.kernel.org
>> ---
> 
> [...]
> 
>> diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
>> index 2461202..1f875db 100644
>> --- a/arch/s390/kernel/processor.c
>> +++ b/arch/s390/kernel/processor.c
>> @@ -58,7 +58,6 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>>  			if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
>>  				seq_printf(m, "%s ", hwcap_str[i]);
>>  		seq_puts(m, "\n");
>> -		show_cacheinfo(m);
>>  	}
>>  	get_online_cpus();
>>  	if (cpu_online(n)) {
> 
> You can't remove that. It's a user space visible change.
> 
Correct, I wanted to add show_cacheinfo back as its user ABI already, missed it.

Is it ok if I use cpu0 cacheinfo or to be safer as cpu0 can be offline, use
the cacheinfo of the cpu executing this ?

Regards,
Sudeep



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-10 11:30   ` Heiko Carstens
@ 2014-02-10 11:36     ` Sudeep Holla
  2014-02-17 18:36     ` Sudeep Holla
  1 sibling, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-10 11:36 UTC (permalink / raw)
  To: Heiko Carstens
  Cc: Sudeep.Holla, linux-kernel, Martin Schwidefsky, linux390, linux-s390

On 10/02/14 11:30, Heiko Carstens wrote:
> On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
>> From: Sudeep Holla <sudeep.holla@arm.com>
>>
>> This patch removes the redundant sysfs cacheinfo code by making use of
>> the newly introduced generic cacheinfo infrastructure.
>>
>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> 
> [...]
> 
>> -static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
>> -{
>> -	struct cache_index_dir *index;
>> -	int len;
>> +	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);
>> +	if (private)
>> +		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
>> +	else /* System wide */
>> +		cpumask_copy(&this_leaf->shared_cpu_map, cpu_online_mask);
> 
> FWIW, this also seems to be wrong: on s390 we only expose cpu private
> caches via sysfs. Shared caches are not exposed (and simply saying they are
> system wide would be wrong).
> Please have a look at these two commits which should describe why things
> are as they are on s390:
> 
> 881730ad365130f64b5c70c40904b04eb3b79de3
>  "s390/cache: expose cpu cache topology via sysfs"
> 6668022c7bde3fdc96d3d257294a7216c7a46829
>  "s390/cache: add cpu cache information to /proc/cpuinfo"
> 
Thanks for the review, will have a look at these commits and update accordingly.

Regards,
Sudeep



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-10 11:34     ` Sudeep Holla
@ 2014-02-10 11:59       ` Heiko Carstens
  0 siblings, 0 replies; 28+ messages in thread
From: Heiko Carstens @ 2014-02-10 11:59 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Martin Schwidefsky, linux390, linux-s390

On Mon, Feb 10, 2014 at 11:34:55AM +0000, Sudeep Holla wrote:
> On 10/02/14 09:50, Heiko Carstens wrote:
> > On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
> >> -		show_cacheinfo(m);
> >>  	}
> >>  	get_online_cpus();
> >>  	if (cpu_online(n)) {
> > 
> > You can't remove that. It's a user space visible change.
> > 
> Correct, I wanted to add show_cacheinfo back as its user ABI already, missed it.
> 
> Is it ok if I use cpu0 cacheinfo or to be safer as cpu0 can be offline, use
> the cacheinfo of the cpu executing this ?

Any online cpu would do, as the information is completely symmetrical.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-07 19:29   ` Greg Kroah-Hartman
@ 2014-02-10 18:09     ` Sudeep Holla
  2014-02-11  0:13       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 28+ messages in thread
From: Sudeep Holla @ 2014-02-10 18:09 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Sudeep.Holla, linux-kernel, Rob Herring, linux-doc

On 07/02/14 19:29, Greg Kroah-Hartman wrote:
> On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
>> From: Sudeep Holla <sudeep.holla@arm.com>
>>
>> This patch adds initial support for providing processor cache information
>> to userspace through sysfs interface. This is based on already existing
>> implementations(x86, ia64, s390 and powerpc) and hence the interface is
>> intended to be fully compatible.
>>
>> The main purpose of this generic support is to avoid further code
>> duplication to support new architectures and also to unify all the existing
>> different implementations.
>>
>> This implementation maintains the hierarchy of cache objects which reflects
>> the system's cache topology. Cache objects are instantiated as needed as
>> CPUs come online. The cache objects are replicated per-cpu even if they are
>> shared. A per-cpu array of cache information maintained is used mainly for
>> sysfs-related book keeping.
> 
> I thought I asked that you not use "raw" kobjects for this, instead
> using 'struct device' or just an attribute group?
> 

Correct, sorry I should have mentioned here instead of cover letter that it's
not yet done. Since the changes involved other architectures, I posted v2 to get
early feedback and testing. More over it's one place to fix now instead of 4
unlike before.

Just adding cache as a device as you suggested won't suffice here. As we need to
track multiple cache indices for each cpu, devices are needed for each cache
index. I tried using device_create_with_groups which provides all we need in one
api for cache indices but since cpu devices are not associated with any class,
it fails if class is NULL. Any suggestions ?

--->8(something like this)
        struct device *dev = get_cpu_device(cpu);
        struct device *cache_dev =
			device_create(dev->class, dev, 0, NULL, "cache");
	
	/* then for each cache call index device_create_with_groups */

> Using a kobject means that userspace tools that use libudev have no
> chance to see these attributes at all, which is not good, right?
> 

Yes I understand that, I am looking at fixing it. Looks like none of the current
implementations cares about uevent notifications :(

Regards,
Sudeep


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-10 18:09     ` Sudeep Holla
@ 2014-02-11  0:13       ` Greg Kroah-Hartman
  2014-02-13 15:55         ` Sudeep Holla
  0 siblings, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2014-02-11  0:13 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Rob Herring, linux-doc

On Mon, Feb 10, 2014 at 06:09:58PM +0000, Sudeep Holla wrote:
> On 07/02/14 19:29, Greg Kroah-Hartman wrote:
> > On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
> >> From: Sudeep Holla <sudeep.holla@arm.com>
> >>
> >> This patch adds initial support for providing processor cache information
> >> to userspace through sysfs interface. This is based on already existing
> >> implementations(x86, ia64, s390 and powerpc) and hence the interface is
> >> intended to be fully compatible.
> >>
> >> The main purpose of this generic support is to avoid further code
> >> duplication to support new architectures and also to unify all the existing
> >> different implementations.
> >>
> >> This implementation maintains the hierarchy of cache objects which reflects
> >> the system's cache topology. Cache objects are instantiated as needed as
> >> CPUs come online. The cache objects are replicated per-cpu even if they are
> >> shared. A per-cpu array of cache information maintained is used mainly for
> >> sysfs-related book keeping.
> > 
> > I thought I asked that you not use "raw" kobjects for this, instead
> > using 'struct device' or just an attribute group?
> > 
> 
> Correct, sorry I should have mentioned here instead of cover letter that it's
> not yet done. Since the changes involved other architectures, I posted v2 to get
> early feedback and testing. More over it's one place to fix now instead of 4
> unlike before.

Ok, I'll wait to review it after you do the device conversion.

> Just adding cache as a device as you suggested won't suffice here. As we need to
> track multiple cache indices for each cpu, devices are needed for each cache
> index. I tried using device_create_with_groups which provides all we need in one
> api for cache indices but since cpu devices are not associated with any class,
> it fails if class is NULL. Any suggestions ?

Make the cpu devices be part of a class?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-11  0:13       ` Greg Kroah-Hartman
@ 2014-02-13 15:55         ` Sudeep Holla
  2014-02-17 18:26           ` Sudeep Holla
  2014-02-18 21:53           ` Greg Kroah-Hartman
  0 siblings, 2 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-13 15:55 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Sudeep.Holla, linux-kernel, Rob Herring, linux-doc

Hi Greg,

On 11/02/14 00:13, Greg Kroah-Hartman wrote:
> On Mon, Feb 10, 2014 at 06:09:58PM +0000, Sudeep Holla wrote:
>> On 07/02/14 19:29, Greg Kroah-Hartman wrote:
>>> On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
>>>> From: Sudeep Holla <sudeep.holla@arm.com>
>>>>
>>>> This patch adds initial support for providing processor cache information
>>>> to userspace through sysfs interface. This is based on already existing
>>>> implementations(x86, ia64, s390 and powerpc) and hence the interface is
>>>> intended to be fully compatible.
>>>>
>>>> The main purpose of this generic support is to avoid further code
>>>> duplication to support new architectures and also to unify all the existing
>>>> different implementations.
>>>>
>>>> This implementation maintains the hierarchy of cache objects which reflects
>>>> the system's cache topology. Cache objects are instantiated as needed as
>>>> CPUs come online. The cache objects are replicated per-cpu even if they are
>>>> shared. A per-cpu array of cache information maintained is used mainly for
>>>> sysfs-related book keeping.
>>>
>>> I thought I asked that you not use "raw" kobjects for this, instead
>>> using 'struct device' or just an attribute group?
>>>
>>
>> Correct, sorry I should have mentioned here instead of cover letter that it's
>> not yet done. Since the changes involved other architectures, I posted v2 to get
>> early feedback and testing. More over it's one place to fix now instead of 4
>> unlike before.
> 
> Ok, I'll wait to review it after you do the device conversion.
> 
>> Just adding cache as a device as you suggested won't suffice here. As we need to
>> track multiple cache indices for each cpu, devices are needed for each cache
>> index. I tried using device_create_with_groups which provides all we need in one
>> api for cache indices but since cpu devices are not associated with any class,
>> it fails if class is NULL. Any suggestions ?
> 
> Make the cpu devices be part of a class?

I was able to convert these to use struct device instead of raw kobjects. But it
requires some changes in order to retain the existing sysfs path mainly due to
the fact that cpus are using legacy subsys_system_register and introducing
cpu_class conflicts with cpu bus. The base changes in the driver core is as
below. Is this acceptable ? or any other alternate suggestions ?

--->8
 drivers/base/bus.c  | 2 ++
 drivers/base/core.c | 8 ++++++++
 drivers/base/cpu.c  | 7 +++++++
 include/linux/cpu.h | 2 ++
 4 files changed, 19 insertions(+)

diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 59dc808..c33bfdb 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -518,10 +518,12 @@ int bus_add_device(struct device *dev)
 						&dev->kobj, dev_name(dev));
 		if (error)
 			goto out_id;
+		if (!dev->class) {
 			error = sysfs_create_link(&dev->kobj,
 				&dev->bus->p->subsys.kobj, "subsystem");
 			if (error)
 				goto out_subsys;
+		}
 		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
 	}
 	return 0;
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2b56717..ef81984 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -10,6 +10,7 @@
  *
  */

+#include <linux/cpu.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -759,6 +760,8 @@ static struct kobject *get_device_parent(struct device *dev,
 			return &block_class.p->subsys.kobj;
 		}
 #endif
+		if (dev->class == cpu_class)
+			return &parent->kobj;

 		/*
 		 * If we have no parent, we live in "virtual".
@@ -843,6 +846,8 @@ static int device_add_class_symlinks(struct device *dev)
 	if (sysfs_deprecated && dev->class == &block_class)
 		return 0;
 #endif
+	if (dev->class == cpu_class)
+		return 0;

 	/* link in the class directory pointing to the device */
 	error = sysfs_create_link(&dev->class->p->subsys.kobj,
@@ -873,6 +878,9 @@ static void device_remove_class_symlinks(struct device *dev)
 	if (sysfs_deprecated && dev->class == &block_class)
 		return;
 #endif
+	if (dev->class == cpu_class)
+		return;
+
 	sysfs_delete_link(&dev->class->p->subsys.kobj, &dev->kobj, dev_name(dev));
 }

diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index f48370d..28386de 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -286,6 +286,7 @@ static void cpu_device_release(struct device *dev)
 	 */
 }

+struct class *cpu_class;
 /*
  * register_cpu - Setup a sysfs device for a CPU.
  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
@@ -302,6 +303,8 @@ int register_cpu(struct cpu *cpu, int num)
 	memset(&cpu->dev, 0x00, sizeof(struct device));
 	cpu->dev.id = num;
 	cpu->dev.bus = &cpu_subsys;
+	cpu->dev.parent = cpu_subsys.dev_root;
+	cpu->dev.class = cpu_class;
 	cpu->dev.release = cpu_device_release;
 	cpu->dev.offline_disabled = !cpu->hotpluggable;
 	cpu->dev.offline = !cpu_online(num);
@@ -387,5 +390,9 @@ void __init cpu_dev_init(void)
 	if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
 		panic("Failed to register CPU subsystem");

+	cpu_class = class_create(THIS_MODULE, "cpu");
+	if (IS_ERR(cpu_class))
+		panic("Failed to register CPU class");
+
 	cpu_dev_register_generic();
 }
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 03e235ad..d1279a5 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -39,6 +39,8 @@ extern void cpu_remove_dev_attr(struct device_attribute *attr);
 extern int cpu_add_dev_attr_group(struct attribute_group *attrs);
 extern void cpu_remove_dev_attr_group(struct attribute_group *attrs);

+extern struct class *cpu_class;
+
 #ifdef CONFIG_HOTPLUG_CPU
 extern void unregister_cpu(struct cpu *cpu);
 extern ssize_t arch_cpu_probe(const char *, size_t);
-- 
1.8.3.2



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-13 15:55         ` Sudeep Holla
@ 2014-02-17 18:26           ` Sudeep Holla
  2014-02-18 21:53           ` Greg Kroah-Hartman
  1 sibling, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-17 18:26 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Sudeep Holla, linux-kernel, Rob Herring, linux-doc

Hi Greg,

On 13/02/14 15:55, Sudeep Holla wrote:
> Hi Greg,
> 
> On 11/02/14 00:13, Greg Kroah-Hartman wrote:
>> On Mon, Feb 10, 2014 at 06:09:58PM +0000, Sudeep Holla wrote:
>>> On 07/02/14 19:29, Greg Kroah-Hartman wrote:
>>>> On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
>>>>> From: Sudeep Holla <sudeep.holla@arm.com>
>>>>>
>>>>> This patch adds initial support for providing processor cache information
>>>>> to userspace through sysfs interface. This is based on already existing
>>>>> implementations(x86, ia64, s390 and powerpc) and hence the interface is
>>>>> intended to be fully compatible.
>>>>>
>>>>> The main purpose of this generic support is to avoid further code
>>>>> duplication to support new architectures and also to unify all the existing
>>>>> different implementations.
>>>>>
>>>>> This implementation maintains the hierarchy of cache objects which reflects
>>>>> the system's cache topology. Cache objects are instantiated as needed as
>>>>> CPUs come online. The cache objects are replicated per-cpu even if they are
>>>>> shared. A per-cpu array of cache information maintained is used mainly for
>>>>> sysfs-related book keeping.
>>>>
>>>> I thought I asked that you not use "raw" kobjects for this, instead
>>>> using 'struct device' or just an attribute group?
>>>>
>>>
>>> Correct, sorry I should have mentioned here instead of cover letter that it's
>>> not yet done. Since the changes involved other architectures, I posted v2 to get
>>> early feedback and testing. More over it's one place to fix now instead of 4
>>> unlike before.
>>
>> Ok, I'll wait to review it after you do the device conversion.
>>
>>> Just adding cache as a device as you suggested won't suffice here. As we need to
>>> track multiple cache indices for each cpu, devices are needed for each cache
>>> index. I tried using device_create_with_groups which provides all we need in one
>>> api for cache indices but since cpu devices are not associated with any class,
>>> it fails if class is NULL. Any suggestions ?
>>
>> Make the cpu devices be part of a class?
> 
> I was able to convert these to use struct device instead of raw kobjects. But it
> requires some changes in order to retain the existing sysfs path mainly due to
> the fact that cpus are using legacy subsys_system_register and introducing
> cpu_class conflicts with cpu bus. The base changes in the driver core is as
> below. Is this acceptable ? or any other alternate suggestions ?
>

Any feedback on this ? If this is acceptable I can post my changes converting
raw kobjects to devices attributes along with this change in a separate patch.

Regards,
Sudeep



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-10 11:30   ` Heiko Carstens
  2014-02-10 11:36     ` Sudeep Holla
@ 2014-02-17 18:36     ` Sudeep Holla
  2014-02-18  9:45       ` Heiko Carstens
  1 sibling, 1 reply; 28+ messages in thread
From: Sudeep Holla @ 2014-02-17 18:36 UTC (permalink / raw)
  To: Heiko Carstens
  Cc: Sudeep.Holla, linux-kernel, Martin Schwidefsky, linux390, linux-s390

Hi Heiko,

On 10/02/14 11:30, Heiko Carstens wrote:
> On Fri, Feb 07, 2014 at 04:49:18PM +0000, Sudeep Holla wrote:
>> From: Sudeep Holla <sudeep.holla@arm.com>
>>
>> This patch removes the redundant sysfs cacheinfo code by making use of
>> the newly introduced generic cacheinfo infrastructure.
>>
>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> 
> [...]
> 
>> -static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
>> -{
>> -	struct cache_index_dir *index;
>> -	int len;
>> +	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);
>> +	if (private)
>> +		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
>> +	else /* System wide */
>> +		cpumask_copy(&this_leaf->shared_cpu_map, cpu_online_mask);
> 
> FWIW, this also seems to be wrong: on s390 we only expose cpu private
> caches via sysfs. Shared caches are not exposed (and simply saying they are
> system wide would be wrong).
> Please have a look at these two commits which should describe why things
> are as they are on s390:
> 
> 881730ad365130f64b5c70c40904b04eb3b79de3
>  "s390/cache: expose cpu cache topology via sysfs"
> 6668022c7bde3fdc96d3d257294a7216c7a46829
>  "s390/cache: add cpu cache information to /proc/cpuinfo"
> 
>

I need your help to get few things clarified.

IIUC, I see that shared caches are not exposed via sysfs but there are exposed
through /proc/cpuinfo, right ? If yes, based on your above statement, shared
cpus are may not be system-wide, then how can /proc/cpuinfo show shared cache info.

Regards,
Sudeep


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 3/8] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
  2014-02-17 18:36     ` Sudeep Holla
@ 2014-02-18  9:45       ` Heiko Carstens
  0 siblings, 0 replies; 28+ messages in thread
From: Heiko Carstens @ 2014-02-18  9:45 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Martin Schwidefsky, linux390, linux-s390

Hi Sudeep,

> > Please have a look at these two commits which should describe why things
> > are as they are on s390:
> > 
> > 881730ad365130f64b5c70c40904b04eb3b79de3
> >  "s390/cache: expose cpu cache topology via sysfs"
> > 6668022c7bde3fdc96d3d257294a7216c7a46829
> >  "s390/cache: add cpu cache information to /proc/cpuinfo"
> > 
>
> I need your help to get few things clarified.
> 
> IIUC, I see that shared caches are not exposed via sysfs but there are exposed
> through /proc/cpuinfo, right ?

Yes.

> If yes, based on your above statement, shared
> cpus are may not be system-wide, then how can /proc/cpuinfo show shared
> cache info.

/proc/cpuinfo shows the information of the underlying hardware but we
cannot tell which second level (virtual) cpus share which caches.
There simply is no such interface available.

Also, if there would be such an interface, it would be only valid until
the hypervisor decides to schedule a virtual cpu on a different physical
cpu; which in turn would mean that we would have to update the cpu maps
all the time.

In order to avoid all those games, we decided to only expose cpu private
caches via sysfs, so we obviously have static cpu maps.

However since the physical cache information may be of interest for a
guest operating system there is the second interface /proc/cpuinfo
which provides this.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-13 15:55         ` Sudeep Holla
  2014-02-17 18:26           ` Sudeep Holla
@ 2014-02-18 21:53           ` Greg Kroah-Hartman
  2014-02-19 16:04             ` Sudeep Holla
  1 sibling, 1 reply; 28+ messages in thread
From: Greg Kroah-Hartman @ 2014-02-18 21:53 UTC (permalink / raw)
  To: Sudeep Holla; +Cc: linux-kernel, Rob Herring, linux-doc

On Thu, Feb 13, 2014 at 03:55:47PM +0000, Sudeep Holla wrote:
> Hi Greg,
> 
> On 11/02/14 00:13, Greg Kroah-Hartman wrote:
> > On Mon, Feb 10, 2014 at 06:09:58PM +0000, Sudeep Holla wrote:
> >> On 07/02/14 19:29, Greg Kroah-Hartman wrote:
> >>> On Fri, Feb 07, 2014 at 04:49:16PM +0000, Sudeep Holla wrote:
> >>>> From: Sudeep Holla <sudeep.holla@arm.com>
> >>>>
> >>>> This patch adds initial support for providing processor cache information
> >>>> to userspace through sysfs interface. This is based on already existing
> >>>> implementations(x86, ia64, s390 and powerpc) and hence the interface is
> >>>> intended to be fully compatible.
> >>>>
> >>>> The main purpose of this generic support is to avoid further code
> >>>> duplication to support new architectures and also to unify all the existing
> >>>> different implementations.
> >>>>
> >>>> This implementation maintains the hierarchy of cache objects which reflects
> >>>> the system's cache topology. Cache objects are instantiated as needed as
> >>>> CPUs come online. The cache objects are replicated per-cpu even if they are
> >>>> shared. A per-cpu array of cache information maintained is used mainly for
> >>>> sysfs-related book keeping.
> >>>
> >>> I thought I asked that you not use "raw" kobjects for this, instead
> >>> using 'struct device' or just an attribute group?
> >>>
> >>
> >> Correct, sorry I should have mentioned here instead of cover letter that it's
> >> not yet done. Since the changes involved other architectures, I posted v2 to get
> >> early feedback and testing. More over it's one place to fix now instead of 4
> >> unlike before.
> > 
> > Ok, I'll wait to review it after you do the device conversion.
> > 
> >> Just adding cache as a device as you suggested won't suffice here. As we need to
> >> track multiple cache indices for each cpu, devices are needed for each cache
> >> index. I tried using device_create_with_groups which provides all we need in one
> >> api for cache indices but since cpu devices are not associated with any class,
> >> it fails if class is NULL. Any suggestions ?
> > 
> > Make the cpu devices be part of a class?
> 
> I was able to convert these to use struct device instead of raw kobjects. But it
> requires some changes in order to retain the existing sysfs path mainly due to
> the fact that cpus are using legacy subsys_system_register and introducing
> cpu_class conflicts with cpu bus. The base changes in the driver core is as
> below. Is this acceptable ? or any other alternate suggestions ?
> 
> --->8
>  drivers/base/bus.c  | 2 ++
>  drivers/base/core.c | 8 ++++++++
>  drivers/base/cpu.c  | 7 +++++++
>  include/linux/cpu.h | 2 ++
>  4 files changed, 19 insertions(+)
> 
> diff --git a/drivers/base/bus.c b/drivers/base/bus.c
> index 59dc808..c33bfdb 100644
> --- a/drivers/base/bus.c
> +++ b/drivers/base/bus.c
> @@ -518,10 +518,12 @@ int bus_add_device(struct device *dev)
>  						&dev->kobj, dev_name(dev));
>  		if (error)
>  			goto out_id;
> +		if (!dev->class) {
>  			error = sysfs_create_link(&dev->kobj,
>  				&dev->bus->p->subsys.kobj, "subsystem");
>  			if (error)
>  				goto out_subsys;
> +		}
>  		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
>  	}
>  	return 0;

This change worries me, does it cause anything else to happen in sysfs
that looks differently from what it does today?

> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 2b56717..ef81984 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -10,6 +10,7 @@
>   *
>   */
> 
> +#include <linux/cpu.h>
>  #include <linux/device.h>
>  #include <linux/err.h>
>  #include <linux/init.h>
> @@ -759,6 +760,8 @@ static struct kobject *get_device_parent(struct device *dev,
>  			return &block_class.p->subsys.kobj;
>  		}
>  #endif
> +		if (dev->class == cpu_class)
> +			return &parent->kobj;

I don't think this is ok :)

Oh wait, we do this for disk classes today, ick, ok, nevermind, but
comment it really well please.

>  		/*
>  		 * If we have no parent, we live in "virtual".
> @@ -843,6 +846,8 @@ static int device_add_class_symlinks(struct device *dev)
>  	if (sysfs_deprecated && dev->class == &block_class)
>  		return 0;
>  #endif
> +	if (dev->class == cpu_class)
> +		return 0;

Same here.

>  	/* link in the class directory pointing to the device */
>  	error = sysfs_create_link(&dev->class->p->subsys.kobj,
> @@ -873,6 +878,9 @@ static void device_remove_class_symlinks(struct device *dev)
>  	if (sysfs_deprecated && dev->class == &block_class)
>  		return;
>  #endif
> +	if (dev->class == cpu_class)
> +		return;

And here.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs
  2014-02-18 21:53           ` Greg Kroah-Hartman
@ 2014-02-19 16:04             ` Sudeep Holla
  0 siblings, 0 replies; 28+ messages in thread
From: Sudeep Holla @ 2014-02-19 16:04 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: Sudeep.Holla, linux-kernel, Rob Herring, linux-doc

Hi Greg,

On 18/02/14 21:53, Greg Kroah-Hartman wrote:
> On Thu, Feb 13, 2014 at 03:55:47PM +0000, Sudeep Holla wrote:
>> Hi Greg,
>> On 11/02/14 00:13, Greg Kroah-Hartman wrote:
[...]
>>> Make the cpu devices be part of a class?
>>
>> I was able to convert these to use struct device instead of raw kobjects. But it
>> requires some changes in order to retain the existing sysfs path mainly due to
>> the fact that cpus are using legacy subsys_system_register and introducing
>> cpu_class conflicts with cpu bus. The base changes in the driver core is as
>> below. Is this acceptable ? or any other alternate suggestions ?
>>
>> --->8
>>  drivers/base/bus.c  | 2 ++
>>  drivers/base/core.c | 8 ++++++++
>>  drivers/base/cpu.c  | 7 +++++++
>>  include/linux/cpu.h | 2 ++
>>  4 files changed, 19 insertions(+)
>>
>> diff --git a/drivers/base/bus.c b/drivers/base/bus.c
>> index 59dc808..c33bfdb 100644
>> --- a/drivers/base/bus.c
>> +++ b/drivers/base/bus.c
>> @@ -518,10 +518,12 @@ int bus_add_device(struct device *dev)
>>  						&dev->kobj, dev_name(dev));
>>  		if (error)
>>  			goto out_id;
>> +		if (!dev->class) {
>>  			error = sysfs_create_link(&dev->kobj,
>>  				&dev->bus->p->subsys.kobj, "subsystem");
>>  			if (error)
>>  				goto out_subsys;
>> +		}
>>  		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
>>  	}
>>  	return 0;
> 
> This change worries me, does it cause anything else to happen in sysfs
> that looks differently from what it does today?
> 
Yes, I found that out 2 days back and have now fixed it, the above change is
removed. It's now handled in device_{add,remove}_class_symlinks correctly.

>> diff --git a/drivers/base/core.c b/drivers/base/core.c
>> index 2b56717..ef81984 100644
>> --- a/drivers/base/core.c
>> +++ b/drivers/base/core.c
>> @@ -10,6 +10,7 @@
>>   *
>>   */
>>
>> +#include <linux/cpu.h>
>>  #include <linux/device.h>
>>  #include <linux/err.h>
>>  #include <linux/init.h>
>> @@ -759,6 +760,8 @@ static struct kobject *get_device_parent(struct device *dev,
>>  			return &block_class.p->subsys.kobj;
>>  		}
>>  #endif
>> +		if (dev->class == cpu_class)
>> +			return &parent->kobj;
> 
> I don't think this is ok :)
> 
> Oh wait, we do this for disk classes today, ick, ok, nevermind, but
> comment it really well please.
> 

Yes I was initially worried but I don't see any other way to retain the sysfs
paths as is. I have commented it well enough now in the patch IMO. I will send
v3 series soon.

Regards,
Sudeep


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2014-02-19 16:04 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-07 16:49 [PATCH RFC/RFT v2 0/8] drivers: cacheinfo support Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 1/8] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
2014-02-07 19:29   ` Greg Kroah-Hartman
2014-02-10 18:09     ` Sudeep Holla
2014-02-11  0:13       ` Greg Kroah-Hartman
2014-02-13 15:55         ` Sudeep Holla
2014-02-17 18:26           ` Sudeep Holla
2014-02-18 21:53           ` Greg Kroah-Hartman
2014-02-19 16:04             ` Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 2/8] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure Sudeep Holla
2014-02-07 16:49   ` Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 3/8] s390: " Sudeep Holla
2014-02-10  9:50   ` Heiko Carstens
2014-02-10 11:34     ` Sudeep Holla
2014-02-10 11:59       ` Heiko Carstens
2014-02-10 11:30   ` Heiko Carstens
2014-02-10 11:36     ` Sudeep Holla
2014-02-17 18:36     ` Sudeep Holla
2014-02-18  9:45       ` Heiko Carstens
2014-02-07 16:49 ` [PATCH RFC/RFT v2 4/8] x86: " Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 5/8] powerpc: " Sudeep Holla
2014-02-07 16:49   ` Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 6/8] ARM64: kernel: add support for cpu cache information Sudeep Holla
2014-02-07 16:49   ` Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 7/8] ARM: " Sudeep Holla
2014-02-07 16:49   ` Sudeep Holla
2014-02-07 16:49 ` [PATCH RFC/RFT v2 8/8] ARM: kernel: add outer cache support for cacheinfo implementation Sudeep Holla
2014-02-07 16:49   ` Sudeep Holla

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.