* [PATCH v5 01/11] cpumask: factor out show_cpumap into separate helper function
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 02/11] topology: replace custom attribute macros with standard DEVICE_ATTR* Sudeep Holla
` (10 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, x86, linux-acpi, linux-pci
Many sysfs *_show function use cpu{list,mask}_scnprintf to copy cpumap
to the buffer aligned to PAGE_SIZE, append '\n' and '\0' to return null
terminated buffer with newline.
This patch creates a new helper function cpumap_print_to_pagebuf in
cpumask.h using newly added bitmap_print_to_pagebuf and consolidates
most of those sysfs functions using the new helper function.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Suggested-by: Stephen Boyd <sboyd@codeaurora.org>
Tested-by: Stephen Boyd <sboyd@codeaurora.org>
Acked-by: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: x86@kernel.org
Cc: linux-acpi@vger.kernel.org
Cc: linux-pci@vger.kernel.org
---
arch/x86/kernel/cpu/perf_event_amd_iommu.c | 5 +---
arch/x86/kernel/cpu/perf_event_amd_uncore.c | 6 +----
arch/x86/kernel/cpu/perf_event_intel_rapl.c | 6 +----
arch/x86/kernel/cpu/perf_event_intel_uncore.c | 6 +----
drivers/acpi/acpi_pad.c | 8 +++---
drivers/base/cpu.c | 5 +---
drivers/base/node.c | 14 +++-------
drivers/base/topology.c | 22 ++-------------
drivers/pci/pci-sysfs.c | 39 +++++++--------------------
include/linux/bitmap.h | 3 +++
include/linux/cpumask.h | 17 ++++++++++++
lib/bitmap.c | 29 ++++++++++++++++++++
12 files changed, 73 insertions(+), 87 deletions(-)
diff --git a/arch/x86/kernel/cpu/perf_event_amd_iommu.c b/arch/x86/kernel/cpu/perf_event_amd_iommu.c
index 639d1289b1ba..97242a9242bd 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_iommu.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_iommu.c
@@ -130,10 +130,7 @@ static ssize_t _iommu_cpumask_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &iommu_cpumask);
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(true, buf, &iommu_cpumask);
}
static DEVICE_ATTR(cpumask, S_IRUGO, _iommu_cpumask_show, NULL);
diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
index 30790d798e6b..cc6cedb8f25d 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
@@ -219,7 +219,6 @@ static ssize_t amd_uncore_attr_show_cpumask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- int n;
cpumask_t *active_mask;
struct pmu *pmu = dev_get_drvdata(dev);
@@ -230,10 +229,7 @@ static ssize_t amd_uncore_attr_show_cpumask(struct device *dev,
else
return 0;
- n = cpulist_scnprintf(buf, PAGE_SIZE - 2, active_mask);
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(true, buf, active_mask);
}
static DEVICE_ATTR(cpumask, S_IRUGO, amd_uncore_attr_show_cpumask, NULL);
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
index 619f7699487a..3256cb87834e 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
@@ -365,11 +365,7 @@ static void rapl_pmu_event_read(struct perf_event *event)
static ssize_t rapl_get_attr_cpumask(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &rapl_cpu_mask);
-
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(true, buf, &rapl_cpu_mask);
}
static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL);
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index 0939f86f543d..3c8ddf0b89ad 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -3617,11 +3617,7 @@ static int uncore_pmu_event_init(struct perf_event *event)
static ssize_t uncore_get_attr_cpumask(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &uncore_cpu_mask);
-
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(true, buf, &uncore_cpu_mask);
}
static DEVICE_ATTR(cpumask, S_IRUGO, uncore_get_attr_cpumask, NULL);
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index f148a0580e04..c7b105c0e1d3 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/drivers/acpi/acpi_pad.c
@@ -350,12 +350,10 @@ static ssize_t acpi_pad_idlecpus_store(struct device *dev,
static ssize_t acpi_pad_idlecpus_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int n = 0;
- n = cpumask_scnprintf(buf, PAGE_SIZE-2, to_cpumask(pad_busy_cpus_bits));
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(false, buf,
+ to_cpumask(pad_busy_cpus_bits));
}
+
static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR,
acpi_pad_idlecpus_show,
acpi_pad_idlecpus_store);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 006b1bc5297d..4d8a56406fbb 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -207,11 +207,8 @@ static ssize_t show_cpus_attr(struct device *dev,
char *buf)
{
struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
- int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map));
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
+ return cpumap_print_to_pagebuf(true, buf, *ca->map);
}
#define _CPU_ATTR(name, map) \
diff --git a/drivers/base/node.c b/drivers/base/node.c
index c6d3ae05f1ca..e68c134551ea 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -25,32 +25,26 @@ static struct bus_type node_subsys = {
};
-static ssize_t node_read_cpumap(struct device *dev, int type, char *buf)
+static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf)
{
struct node *node_dev = to_node(dev);
const struct cpumask *mask = cpumask_of_node(node_dev->dev.id);
- int len;
/* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */
BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1));
- len = type?
- cpulist_scnprintf(buf, PAGE_SIZE-2, mask) :
- cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
- buf[len++] = '\n';
- buf[len] = '\0';
- return len;
+ return cpumap_print_to_pagebuf(list, buf, mask);
}
static inline ssize_t node_read_cpumask(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return node_read_cpumap(dev, 0, buf);
+ return node_read_cpumap(dev, false, buf);
}
static inline ssize_t node_read_cpulist(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return node_read_cpumap(dev, 1, buf);
+ return node_read_cpumap(dev, true, buf);
}
static DEVICE_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL);
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index be7c1fb7c0c9..f7c353843ddf 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -42,29 +42,11 @@ static ssize_t show_##name(struct device *dev, \
return sprintf(buf, "%d\n", topology_##name(dev->id)); \
}
-#if defined(topology_thread_cpumask) || defined(topology_core_cpumask) || \
- defined(topology_book_cpumask)
-static ssize_t show_cpumap(int type, const struct cpumask *mask, char *buf)
-{
- ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
- int n = 0;
-
- if (len > 1) {
- n = type?
- cpulist_scnprintf(buf, len-2, mask) :
- cpumask_scnprintf(buf, len-2, mask);
- buf[n++] = '\n';
- buf[n] = '\0';
- }
- return n;
-}
-#endif
-
#define define_siblings_show_map(name) \
static ssize_t show_##name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
- return show_cpumap(0, topology_##name(dev->id), buf); \
+ return cpumap_print_to_pagebuf(false, buf, topology_##name(dev->id));\
}
#define define_siblings_show_list(name) \
@@ -72,7 +54,7 @@ static ssize_t show_##name##_list(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- return show_cpumap(1, topology_##name(dev->id), buf); \
+ return cpumap_print_to_pagebuf(true, buf, topology_##name(dev->id));\
}
#define define_siblings_show_func(name) \
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 9ff0a901ecf7..b8c0ffb36ae7 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -77,11 +77,10 @@ static ssize_t broken_parity_status_store(struct device *dev,
}
static DEVICE_ATTR_RW(broken_parity_status);
-static ssize_t pci_dev_show_local_cpu(struct device *dev, int type,
+static ssize_t pci_dev_show_local_cpu(struct device *dev, bool list,
struct device_attribute *attr, char *buf)
{
const struct cpumask *mask;
- int len;
#ifdef CONFIG_NUMA
mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
@@ -89,59 +88,41 @@ static ssize_t pci_dev_show_local_cpu(struct device *dev, int type,
#else
mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
#endif
- len = type ?
- cpumask_scnprintf(buf, PAGE_SIZE-2, mask) :
- cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
-
- buf[len++] = '\n';
- buf[len] = '\0';
- return len;
+ return cpumap_print_to_pagebuf(list, buf, mask);
}
static ssize_t local_cpus_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return pci_dev_show_local_cpu(dev, 1, attr, buf);
+ return pci_dev_show_local_cpu(dev, false, attr, buf);
}
static DEVICE_ATTR_RO(local_cpus);
static ssize_t local_cpulist_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return pci_dev_show_local_cpu(dev, 0, attr, buf);
+ return pci_dev_show_local_cpu(dev, true, attr, buf);
}
static DEVICE_ATTR_RO(local_cpulist);
/*
* PCI Bus Class Devices
*/
-static ssize_t pci_bus_show_cpuaffinity(struct device *dev, int type,
- struct device_attribute *attr,
- char *buf)
-{
- int ret;
- const struct cpumask *cpumask;
-
- cpumask = cpumask_of_pcibus(to_pci_bus(dev));
- ret = type ?
- cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask) :
- cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask);
- buf[ret++] = '\n';
- buf[ret] = '\0';
- return ret;
-}
-
static ssize_t cpuaffinity_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return pci_bus_show_cpuaffinity(dev, 0, attr, buf);
+ const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev));
+
+ return cpumap_print_to_pagebuf(false, buf, cpumask);
}
static DEVICE_ATTR_RO(cpuaffinity);
static ssize_t cpulistaffinity_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return pci_bus_show_cpuaffinity(dev, 1, attr, buf);
+ const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev));
+
+ return cpumap_print_to_pagebuf(true, buf, cpumask);
}
static DEVICE_ATTR_RO(cpulistaffinity);
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index e1c8d080c427..9d5c3224a1e2 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -60,6 +60,7 @@
* bitmap_find_free_region(bitmap, bits, order) Find and allocate bit region
* bitmap_release_region(bitmap, pos, order) Free specified bit region
* bitmap_allocate_region(bitmap, pos, order) Allocate specified bit region
+ * bitmap_print_to_pagebuf(list, buf, mask, nbits) Print bitmap src as list/hex
*/
/*
@@ -145,6 +146,8 @@ extern void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int o
extern int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order);
extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
extern int bitmap_ord_to_pos(const unsigned long *bitmap, int n, int bits);
+extern int bitmap_print_to_pagebuf(bool list, char *buf,
+ const unsigned long *maskp, int nmaskbits);
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
#define BITMAP_LAST_WORD_MASK(nbits) \
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 2997af6d2ccd..d8a6f8a1d308 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -792,6 +792,23 @@ static inline const struct cpumask *get_cpu_mask(unsigned int cpu)
}
#endif /* NR_CPUS > BITS_PER_LONG */
+/**
+ * cpumap_print_to_pagebuf - copies the cpumask into the buffer either
+ * as comma-separated list of cpus or hex values of cpumask
+ * @list: indicates whether the cpumap must be list
+ * @mask: the cpumask to copy
+ * @buf: the buffer to copy into
+ *
+ * Returns the length of the (null-terminated) @buf string, zero if
+ * nothing is copied.
+ */
+static inline ssize_t
+cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask)
+{
+ return bitmap_print_to_pagebuf(list, buf, cpumask_bits(mask),
+ nr_cpumask_bits);
+}
+
/*
*
* From here down, all obsolete. Use cpumask_ variants!
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 1e031f2c9aba..0a9f39388c63 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -12,6 +12,8 @@
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/bug.h>
+
+#include <asm/page.h>
#include <asm/uaccess.h>
/*
@@ -580,6 +582,33 @@ int bitmap_scnlistprintf(char *buf, unsigned int buflen,
EXPORT_SYMBOL(bitmap_scnlistprintf);
/**
+ * bitmap_print_to_pagebuf - convert bitmap to list or hex format ASCII string
+ * @list: indicates whether the bitmap must be list
+ * @buf: page aligned buffer into which string is placed
+ * @maskp: pointer to bitmap to convert
+ * @nmaskbits: size of bitmap, in bits
+ *
+ * Output format is a comma-separated list of decimal numbers and
+ * ranges if list is specified or hex digits grouped into comma-separated
+ * sets of 8 digits/set. Returns the number of characters written to buf.
+ */
+int bitmap_print_to_pagebuf(bool list, char *buf, const unsigned long *maskp,
+ int nmaskbits)
+{
+ ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf - 2;
+ int n = 0;
+
+ if (len > 1) {
+ n = list ? bitmap_scnlistprintf(buf, len, maskp, nmaskbits) :
+ bitmap_scnprintf(buf, len, maskp, nmaskbits);
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ }
+ return n;
+}
+EXPORT_SYMBOL(bitmap_print_to_pagebuf);
+
+/**
* __bitmap_parselist - convert list format ASCII string to bitmap
* @buf: read nul-terminated user string from this buffer
* @buflen: buffer size in bytes. If string is smaller than this
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 02/11] topology: replace custom attribute macros with standard DEVICE_ATTR*
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 01/11] cpumask: factor out show_cpumap into separate helper function Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 03/11] drivers: base: add cpu_device_create to support per-cpu devices Sudeep Holla
` (9 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML; +Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman
Currently couple of custom macros are defined to declare the
device attributes. However there are already standard macros
defined in device.h that suffice the need and these custom
macros can be removed.
This patch replaces custom attribute macros with standard
DEVICE_ATTR_RO attribute
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/base/topology.c | 53 ++++++++++++++++++++++---------------------------
1 file changed, 24 insertions(+), 29 deletions(-)
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index f7c353843ddf..6491f45200a7 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -29,57 +29,52 @@
#include <linux/hardirq.h>
#include <linux/topology.h>
-#define define_one_ro_named(_name, _func) \
- static DEVICE_ATTR(_name, 0444, _func, NULL)
-
-#define define_one_ro(_name) \
- static DEVICE_ATTR(_name, 0444, show_##_name, NULL)
-
#define define_id_show_func(name) \
-static ssize_t show_##name(struct device *dev, \
+static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
return sprintf(buf, "%d\n", topology_##name(dev->id)); \
}
-#define define_siblings_show_map(name) \
-static ssize_t show_##name(struct device *dev, \
+#define define_siblings_show_map(name, mask) \
+static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
- return cpumap_print_to_pagebuf(false, buf, topology_##name(dev->id));\
+ return cpumap_print_to_pagebuf(false, buf, topology_##mask(dev->id));\
}
-#define define_siblings_show_list(name) \
-static ssize_t show_##name##_list(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
+#define define_siblings_show_list(name, mask) \
+static ssize_t name##_list_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- return cpumap_print_to_pagebuf(true, buf, topology_##name(dev->id));\
+ return cpumap_print_to_pagebuf(true, buf, topology_##mask(dev->id));\
}
-#define define_siblings_show_func(name) \
- define_siblings_show_map(name); define_siblings_show_list(name)
+#define define_siblings_show_func(name, mask) \
+ define_siblings_show_map(name, mask); \
+ define_siblings_show_list(name, mask)
define_id_show_func(physical_package_id);
-define_one_ro(physical_package_id);
+static DEVICE_ATTR_RO(physical_package_id);
define_id_show_func(core_id);
-define_one_ro(core_id);
+static DEVICE_ATTR_RO(core_id);
-define_siblings_show_func(thread_cpumask);
-define_one_ro_named(thread_siblings, show_thread_cpumask);
-define_one_ro_named(thread_siblings_list, show_thread_cpumask_list);
+define_siblings_show_func(thread_siblings, thread_cpumask);
+static DEVICE_ATTR_RO(thread_siblings);
+static DEVICE_ATTR_RO(thread_siblings_list);
-define_siblings_show_func(core_cpumask);
-define_one_ro_named(core_siblings, show_core_cpumask);
-define_one_ro_named(core_siblings_list, show_core_cpumask_list);
+define_siblings_show_func(core_siblings, core_cpumask);
+static DEVICE_ATTR_RO(core_siblings);
+static DEVICE_ATTR_RO(core_siblings_list);
#ifdef CONFIG_SCHED_BOOK
define_id_show_func(book_id);
-define_one_ro(book_id);
-define_siblings_show_func(book_cpumask);
-define_one_ro_named(book_siblings, show_book_cpumask);
-define_one_ro_named(book_siblings_list, show_book_cpumask_list);
+static DEVICE_ATTR_RO(book_id);
+define_siblings_show_func(book_siblings, book_cpumask);
+static DEVICE_ATTR_RO(book_siblings);
+static DEVICE_ATTR_RO(book_siblings_list);
#endif
static struct attribute *default_attrs[] = {
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 03/11] drivers: base: add cpu_device_create to support per-cpu devices
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 01/11] cpumask: factor out show_cpumap into separate helper function Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 02/11] topology: replace custom attribute macros with standard DEVICE_ATTR* Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-11-07 19:22 ` Greg Kroah-Hartman
2014-09-30 13:48 ` [PATCH v5 04/11] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
` (8 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, David Herrmann, Kay Sievers
This patch adds a new function to create per-cpu devices.
This helps in:
1. reusing the device infrastructure to create any cpu related
attributes and corresponding sysfs instead of creating and
dealing with raw kobjects directly
2. retaining the legacy path(/sys/devices/system/cpu/..) to support
existing sysfs ABI
3. avoiding to create links in the bus directory pointing to the
device as there would be per-cpu instance of these devices with
the same name since dev->bus is not populated to cpu_sysbus on
purpose
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Tested-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Kay Sievers <kay@vrfy.org>
---
drivers/base/cpu.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/cpu.h | 4 ++++
2 files changed, 58 insertions(+)
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4d8a56406fbb..f829a4c71749 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -363,6 +363,60 @@ struct device *get_cpu_device(unsigned cpu)
}
EXPORT_SYMBOL_GPL(get_cpu_device);
+static void device_create_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+static struct device *
+__cpu_device_create(struct device *parent, void *drvdata,
+ const struct attribute_group **groups,
+ const char *fmt, va_list args)
+{
+ struct device *dev = NULL;
+ int retval = -ENODEV;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ device_initialize(dev);
+ dev->parent = parent;
+ dev->groups = groups;
+ dev->release = device_create_release;
+ dev_set_drvdata(dev, drvdata);
+
+ retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
+ if (retval)
+ goto error;
+
+ retval = device_add(dev);
+ if (retval)
+ goto error;
+
+ return dev;
+
+error:
+ put_device(dev);
+ return ERR_PTR(retval);
+}
+
+struct device *cpu_device_create(struct device *parent, void *drvdata,
+ const struct attribute_group **groups,
+ const char *fmt, ...)
+{
+ va_list vargs;
+ struct device *dev;
+
+ va_start(vargs, fmt);
+ dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs);
+ va_end(vargs);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(cpu_device_create);
+
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
#endif
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 95978ad7fcdd..bb790a5621c0 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -19,6 +19,7 @@
struct device;
struct device_node;
+struct attribute_group;
struct cpu {
int node_id; /* The node which contains the CPU */
@@ -39,6 +40,9 @@ 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 device *cpu_device_create(struct device *parent, void *drvdata,
+ const struct attribute_group **groups,
+ const char *fmt, ...);
#ifdef CONFIG_HOTPLUG_CPU
extern void unregister_cpu(struct cpu *cpu);
extern ssize_t arch_cpu_probe(const char *, size_t);
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH v5 03/11] drivers: base: add cpu_device_create to support per-cpu devices
2014-09-30 13:48 ` [PATCH v5 03/11] drivers: base: add cpu_device_create to support per-cpu devices Sudeep Holla
@ 2014-11-07 19:22 ` Greg Kroah-Hartman
0 siblings, 0 replies; 19+ messages in thread
From: Greg Kroah-Hartman @ 2014-11-07 19:22 UTC (permalink / raw)
To: Sudeep Holla
Cc: LKML, Heiko Carstens, Lorenzo Pieralisi, David Herrmann,
Kay Sievers
On Tue, Sep 30, 2014 at 02:48:24PM +0100, Sudeep Holla wrote:
> This patch adds a new function to create per-cpu devices.
> This helps in:
> 1. reusing the device infrastructure to create any cpu related
> attributes and corresponding sysfs instead of creating and
> dealing with raw kobjects directly
> 2. retaining the legacy path(/sys/devices/system/cpu/..) to support
> existing sysfs ABI
> 3. avoiding to create links in the bus directory pointing to the
> device as there would be per-cpu instance of these devices with
> the same name since dev->bus is not populated to cpu_sysbus on
> purpose
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> Tested-by: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: David Herrmann <dh.herrmann@gmail.com>
> Cc: Kay Sievers <kay@vrfy.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v5 04/11] drivers: base: support cpu cache information interface to userspace via sysfs
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (2 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 03/11] drivers: base: add cpu_device_create to support per-cpu devices Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 05/11] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure Sudeep Holla
` (7 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, linux-api, linux390, linux-arm-kernel,
linux-ia64, linuxppc-dev, linux-s390, x86
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 devices are instantiated as needed as
CPUs come online. The cache information is 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>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Tested-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-api@vger.kernel.org
Cc: linux390@de.ibm.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-ia64@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: x86@kernel.org
---
Documentation/ABI/testing/sysfs-devices-system-cpu | 47 ++
drivers/base/Makefile | 2 +-
drivers/base/cacheinfo.c | 541 +++++++++++++++++++++
include/linux/cacheinfo.h | 100 ++++
4 files changed, 689 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 acb9bfc89b48..99983e67c13c 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -224,3 +224,50 @@ 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: July 2014(documented, existed before August 2008)
+Contact: Sudeep Holla <sudeep.holla@arm.com>
+ Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description: Parameters for the CPU cache attributes
+
+ allocation_policy:
+ - 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
+ - ReadWriteAllocate: both writeallocate and readallocate
+
+ attributes: LEGACY used only on IA64 and is same as write_policy
+
+ coherency_line_size: the minimum amount of data in bytes that gets
+ transferred from memory to cache
+
+ 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 logical 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
+
+ write_policy:
+ - 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
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 4aab26ec0292..f901bc1cffc8 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 000000000000..8ed4ea1f3716
--- /dev/null
+++ b/drivers/base/cacheinfo.c
@@ -0,0 +1,541 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#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/of.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/sysfs.h>
+
+/* pointer to per cpu cacheinfo */
+static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo);
+#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, 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 cacheinfo *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 cacheinfo *this_leaf,
+ struct cacheinfo *sib_leaf)
+{
+ return sib_leaf->of_node == this_leaf->of_node;
+}
+#else
+static inline int cache_setup_of_node(unsigned int cpu) { return 0; }
+static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
+ struct cacheinfo *sib_leaf)
+{
+ /*
+ * For non-DT systems, assume unique level 1 cache, system-wide
+ * shared caches for all other levels. This will be used only if
+ * arch specific code has not populated shared_cpu_map
+ */
+ return !(this_leaf->level == 1);
+}
+#endif
+
+static int cache_shared_cpu_map_setup(unsigned int cpu)
+{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *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;
+ /* skip if shared_cpu_map is already populated */
+ if (!cpumask_empty(&this_leaf->shared_cpu_map))
+ continue;
+
+ 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;
+}
+
+static void cache_shared_cpu_map_remove(unsigned int cpu)
+{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *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);
+ }
+}
+
+static void free_cache_attributes(unsigned int cpu)
+{
+ cache_shared_cpu_map_remove(cpu);
+
+ kfree(per_cpu_cacheinfo(cpu));
+ per_cpu_cacheinfo(cpu) = NULL;
+}
+
+int __weak init_cache_level(unsigned int cpu)
+{
+ return -ENOENT;
+}
+
+int __weak populate_cache_leaves(unsigned int cpu)
+{
+ return -ENOENT;
+}
+
+static int detect_cache_attributes(unsigned int cpu)
+{
+ int ret;
+
+ if (init_cache_level(cpu))
+ return -ENOENT;
+
+ per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
+ sizeof(struct cacheinfo), 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 only if they are not populated already
+ */
+ ret = cache_shared_cpu_map_setup(cpu);
+ if (ret)
+ goto free_ci;
+ return 0;
+
+free_ci:
+ free_cache_attributes(cpu);
+ return ret;
+}
+
+/* pointer to cpuX/cache device */
+static DEFINE_PER_CPU(struct device *, ci_cache_dev);
+#define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu))
+
+static cpumask_t cache_dev_map;
+
+/* pointer to array of devices for cpuX/cache/indexY */
+static DEFINE_PER_CPU(struct device **, ci_index_dev);
+#define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu))
+#define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx])
+
+#define show_one(file_name, object) \
+static ssize_t file_name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \
+ return sprintf(buf, "%u\n", this_leaf->object); \
+}
+
+show_one(level, level);
+show_one(coherency_line_size, coherency_line_size);
+show_one(number_of_sets, number_of_sets);
+show_one(physical_line_partition, physical_line_partition);
+show_one(ways_of_associativity, ways_of_associativity);
+
+static ssize_t size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%uK\n", this_leaf->size >> 10);
+}
+
+static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf)
+{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ const struct cpumask *mask = &this_leaf->shared_cpu_map;
+
+ return cpumap_print_to_pagebuf(list, buf, mask);
+}
+
+static ssize_t shared_cpu_map_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return shared_cpumap_show_func(dev, false, buf);
+}
+
+static ssize_t shared_cpu_list_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return shared_cpumap_show_func(dev, true, buf);
+}
+
+static ssize_t type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+
+ 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 -EINVAL;
+ }
+}
+
+static ssize_t allocation_policy_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ unsigned int ci_attr = this_leaf->attributes;
+ int n = 0;
+
+ if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE))
+ n = sprintf(buf, "ReadWriteAllocate\n");
+ else if (ci_attr & CACHE_READ_ALLOCATE)
+ n = sprintf(buf, "ReadAllocate\n");
+ else if (ci_attr & CACHE_WRITE_ALLOCATE)
+ n = sprintf(buf, "WriteAllocate\n");
+ return n;
+}
+
+static ssize_t write_policy_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ unsigned int ci_attr = this_leaf->attributes;
+ int n = 0;
+
+ if (ci_attr & CACHE_WRITE_THROUGH)
+ n = sprintf(buf, "WriteThrough\n");
+ else if (ci_attr & CACHE_WRITE_BACK)
+ n = sprintf(buf, "WriteBack\n");
+ return n;
+}
+
+static DEVICE_ATTR_RO(level);
+static DEVICE_ATTR_RO(type);
+static DEVICE_ATTR_RO(coherency_line_size);
+static DEVICE_ATTR_RO(ways_of_associativity);
+static DEVICE_ATTR_RO(number_of_sets);
+static DEVICE_ATTR_RO(size);
+static DEVICE_ATTR_RO(allocation_policy);
+static DEVICE_ATTR_RO(write_policy);
+static DEVICE_ATTR_RO(shared_cpu_map);
+static DEVICE_ATTR_RO(shared_cpu_list);
+static DEVICE_ATTR_RO(physical_line_partition);
+
+static struct attribute *cache_default_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_level.attr,
+ &dev_attr_shared_cpu_map.attr,
+ &dev_attr_shared_cpu_list.attr,
+ &dev_attr_coherency_line_size.attr,
+ &dev_attr_ways_of_associativity.attr,
+ &dev_attr_number_of_sets.attr,
+ &dev_attr_size.attr,
+ &dev_attr_allocation_policy.attr,
+ &dev_attr_write_policy.attr,
+ &dev_attr_physical_line_partition.attr,
+ NULL
+};
+
+static umode_t
+cache_default_attrs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int unused)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ const struct cpumask *mask = &this_leaf->shared_cpu_map;
+ umode_t mode = attr->mode;
+
+ if ((attr == &dev_attr_type.attr) && this_leaf->type)
+ return mode;
+ if ((attr == &dev_attr_level.attr) && this_leaf->level)
+ return mode;
+ if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask))
+ return mode;
+ if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask))
+ return mode;
+ if ((attr == &dev_attr_coherency_line_size.attr) &&
+ this_leaf->coherency_line_size)
+ return mode;
+ if ((attr == &dev_attr_ways_of_associativity.attr) &&
+ this_leaf->size) /* allow 0 = full associativity */
+ return mode;
+ if ((attr == &dev_attr_number_of_sets.attr) &&
+ this_leaf->number_of_sets)
+ return mode;
+ if ((attr == &dev_attr_size.attr) && this_leaf->size)
+ return mode;
+ if ((attr == &dev_attr_write_policy.attr) &&
+ (this_leaf->attributes & CACHE_WRITE_POLICY_MASK))
+ return mode;
+ if ((attr == &dev_attr_allocation_policy.attr) &&
+ (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK))
+ return mode;
+ if ((attr == &dev_attr_physical_line_partition.attr) &&
+ this_leaf->physical_line_partition)
+ return mode;
+
+ return 0;
+}
+
+static const struct attribute_group cache_default_group = {
+ .attrs = cache_default_attrs,
+ .is_visible = cache_default_attrs_is_visible,
+};
+
+static const struct attribute_group *cache_default_groups[] = {
+ &cache_default_group,
+ NULL,
+};
+
+static const struct attribute_group *cache_private_groups[] = {
+ &cache_default_group,
+ NULL, /* Place holder for private group */
+ NULL,
+};
+
+const struct attribute_group *
+__weak cache_get_priv_group(struct cacheinfo *this_leaf)
+{
+ return NULL;
+}
+
+static const struct attribute_group **
+cache_get_attribute_groups(struct cacheinfo *this_leaf)
+{
+ const struct attribute_group *priv_group =
+ cache_get_priv_group(this_leaf);
+
+ if (!priv_group)
+ return cache_default_groups;
+
+ if (!cache_private_groups[1])
+ cache_private_groups[1] = priv_group;
+
+ return cache_private_groups;
+}
+
+/* Add/Remove cache interface for CPU device */
+static void cpu_cache_sysfs_exit(unsigned int cpu)
+{
+ int i;
+ struct device *ci_dev;
+
+ if (per_cpu_index_dev(cpu)) {
+ for (i = 0; i < cache_leaves(cpu); i++) {
+ ci_dev = per_cache_index_dev(cpu, i);
+ if (!ci_dev)
+ continue;
+ device_unregister(ci_dev);
+ }
+ kfree(per_cpu_index_dev(cpu));
+ per_cpu_index_dev(cpu) = NULL;
+ }
+ device_unregister(per_cpu_cache_dev(cpu));
+ per_cpu_cache_dev(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_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache");
+ if (IS_ERR(per_cpu_cache_dev(cpu)))
+ return PTR_ERR(per_cpu_cache_dev(cpu));
+
+ /* Allocate all required memory */
+ per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu),
+ sizeof(struct device *), GFP_KERNEL);
+ if (unlikely(per_cpu_index_dev(cpu) == NULL))
+ goto err_out;
+
+ return 0;
+
+err_out:
+ cpu_cache_sysfs_exit(cpu);
+ return -ENOMEM;
+}
+
+static int cache_add_dev(unsigned int cpu)
+{
+ unsigned int i;
+ int rc;
+ struct device *ci_dev, *parent;
+ struct cacheinfo *this_leaf;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ const struct attribute_group **cache_groups;
+
+ rc = cpu_cache_sysfs_init(cpu);
+ if (unlikely(rc < 0))
+ return rc;
+
+ parent = per_cpu_cache_dev(cpu);
+ for (i = 0; i < cache_leaves(cpu); i++) {
+ this_leaf = this_cpu_ci->info_list + i;
+ if (this_leaf->disable_sysfs)
+ continue;
+ cache_groups = cache_get_attribute_groups(this_leaf);
+ ci_dev = cpu_device_create(parent, this_leaf, cache_groups,
+ "index%1u", i);
+ if (IS_ERR(ci_dev)) {
+ rc = PTR_ERR(ci_dev);
+ goto err;
+ }
+ per_cache_index_dev(cpu, i) = ci_dev;
+ }
+ cpumask_set_cpu(cpu, &cache_dev_map);
+
+ return 0;
+err:
+ cpu_cache_sysfs_exit(cpu);
+ return rc;
+}
+
+static void cache_remove_dev(unsigned int cpu)
+{
+ if (!cpumask_test_cpu(cpu, &cache_dev_map))
+ return;
+ cpumask_clear_cpu(cpu, &cache_dev_map);
+
+ 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 & ~CPU_TASKS_FROZEN) {
+ case CPU_ONLINE:
+ rc = detect_cache_attributes(cpu);
+ if (!rc)
+ rc = cache_add_dev(cpu);
+ break;
+ case CPU_DEAD:
+ cache_remove_dev(cpu);
+ if (per_cpu_cacheinfo(cpu))
+ free_cache_attributes(cpu);
+ break;
+ }
+ return notifier_from_errno(rc);
+}
+
+static int __init cacheinfo_sysfs_init(void)
+{
+ int cpu, rc = 0;
+
+ cpu_notifier_register_begin();
+
+ for_each_online_cpu(cpu) {
+ rc = detect_cache_attributes(cpu);
+ if (rc) {
+ pr_err("error detecting cacheinfo..cpu%d\n", cpu);
+ goto out;
+ }
+ rc = cache_add_dev(cpu);
+ if (rc) {
+ free_cache_attributes(cpu);
+ pr_err("error populating cacheinfo..cpu%d\n", cpu);
+ goto out;
+ }
+ }
+ __hotcpu_notifier(cacheinfo_cpu_callback, 0);
+
+out:
+ cpu_notifier_register_done();
+ return rc;
+}
+
+device_initcall(cacheinfo_sysfs_init);
diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h
new file mode 100644
index 000000000000..3daf5ed392c9
--- /dev/null
+++ b/include/linux/cacheinfo.h
@@ -0,0 +1,100 @@
+#ifndef _LINUX_CACHEINFO_H
+#define _LINUX_CACHEINFO_H
+
+#include <linux/bitops.h>
+#include <linux/cpumask.h>
+#include <linux/smp.h>
+
+struct device_node;
+struct attribute;
+
+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 cacheinfo - represent a cache leaf node
+ * @type: type of the cache - data, inst or unified
+ * @level: represents the hierarcy in the multi-level cache
+ * @coherency_line_size: size of each cache line usually representing
+ * the minimum amount of data that gets transferred from memory
+ * @number_of_sets: total number of sets, a set is a collection of cache
+ * lines sharing the same index
+ * @ways_of_associativity: number of ways in which a particular memory
+ * block can be placed in the cache
+ * @physical_line_partition: number of physical cache lines sharing the
+ * same cachetag
+ * @size: Total size of the cache
+ * @shared_cpu_map: logical cpumask representing all the cpus sharing
+ * this cache node
+ * @attributes: bitfield representing various cache attributes
+ * @of_node: if devicetree is used, this represents either the cpu node in
+ * case there's no explicit cache node or the cache node itself in the
+ * device tree
+ * @disable_sysfs: indicates whether this node is visible to the user via
+ * sysfs or not
+ * @priv: pointer to any private data structure specific to particular
+ * cache design
+ *
+ * While @of_node, @disable_sysfs and @priv are used for internal book
+ * keeping, the remaining members form the core properties of the cache
+ */
+struct cacheinfo {
+ enum cache_type type;
+ unsigned int level;
+ unsigned int coherency_line_size;
+ unsigned int number_of_sets;
+ unsigned int ways_of_associativity;
+ unsigned int physical_line_partition;
+ unsigned int size;
+ cpumask_t shared_cpu_map;
+ unsigned int attributes;
+#define CACHE_WRITE_THROUGH BIT(0)
+#define CACHE_WRITE_BACK BIT(1)
+#define CACHE_WRITE_POLICY_MASK \
+ (CACHE_WRITE_THROUGH | CACHE_WRITE_BACK)
+#define CACHE_READ_ALLOCATE BIT(2)
+#define CACHE_WRITE_ALLOCATE BIT(3)
+#define CACHE_ALLOCATE_POLICY_MASK \
+ (CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE)
+
+ struct device_node *of_node;
+ bool disable_sysfs;
+ void *priv;
+};
+
+struct cpu_cacheinfo {
+ struct cacheinfo *info_list;
+ unsigned int num_levels;
+ unsigned int num_leaves;
+};
+
+/*
+ * Helpers to make sure "func" is executed on the cpu whose cache
+ * attributes are being detected
+ */
+#define DEFINE_SMP_CALL_CACHE_FUNCTION(func) \
+static inline void _##func(void *ret) \
+{ \
+ int cpu = smp_processor_id(); \
+ *(int *)ret = __##func(cpu); \
+} \
+ \
+int func(unsigned int cpu) \
+{ \
+ int ret; \
+ smp_call_function_single(cpu, _##func, &ret, true); \
+ return ret; \
+}
+
+struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu);
+int init_cache_level(unsigned int cpu);
+int populate_cache_leaves(unsigned int cpu);
+
+const struct attribute_group *cache_get_priv_group(struct cacheinfo *this_leaf);
+
+#endif /* _LINUX_CACHEINFO_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 05/11] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (3 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 04/11] drivers: base: support cpu cache information interface to userspace via sysfs Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 06/11] s390: " Sudeep Holla
` (6 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Tony Luck, Fenghua Yu, linux-ia64
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 | 421 ++++++++++++--------------------------------
1 file changed, 111 insertions(+), 310 deletions(-)
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index f295f9abba4b..3d6749cb53ec 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 cacheinfo *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,305 +133,141 @@ 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);
+ ia64_pal_cache_shared_info(this_leaf->level, type, i,
+ &csi) == PAL_STATUS_SUCCESS);
+ return 0;
}
-static ssize_t show_ways_of_associativity(struct cache_info *this_leaf,
- char *buf)
+static int cache_shared_cpu_map_setup(unsigned int cpu)
{
- return sprintf(buf, "%u\n", this_leaf->cci.pcci_assoc);
+ unsigned int idx;
+ int ret = 0;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *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 ssize_t show_attributes(struct cache_info *this_leaf, char *buf)
+#else
+static int cache_shared_cpu_map_setup(unsigned int cpu)
{
- return sprintf(buf,
- "%s\n",
- cache_mattrib[this_leaf->cci.pcci_cache_attr]);
-}
+ int idx;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *this_leaf = this_cpu_ci->info_list;
-static ssize_t show_size(struct cache_info *this_leaf, char *buf)
-{
- return sprintf(buf, "%uK\n", this_leaf->cci.pcci_cache_size / 1024);
+ for (idx = 0; idx < this_cpu_ci->num_leaves; idx++, this_leaf++)
+ cpu_set(cpu, this_leaf->shared_cpu_map);
+ return 0;
}
+#endif
-static ssize_t show_number_of_sets(struct cache_info *this_leaf, char *buf)
+static ssize_t attributes_show(struct device *dev,
+ struct device_attribute *attr, 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);
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ unsigned int ci_attr = this_leaf->attributes;
+ int n = 0;
+
+ if (ci_attr & CACHE_WRITE_THROUGH)
+ n = sprintf(buf, "WriteThrough\n");
+ else if (ci_attr & CACHE_WRITE_BACK)
+ n = sprintf(buf, "WriteBack\n");
+ return n;
}
-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 DEVICE_ATTR_RO(attributes);
-static ssize_t show_type(struct cache_info *this_leaf, char *buf)
+static umode_t
+cache_private_attrs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int unused)
{
- int type = this_leaf->type + this_leaf->cci.pcci_unified;
- return sprintf(buf, "%s\n", cache_types[type]);
-}
+ struct device *dev = kobj_to_dev(kobj);
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
-static ssize_t show_level(struct cache_info *this_leaf, char *buf)
-{
- return sprintf(buf, "%u\n", this_leaf->level);
+ if ((attr == &dev_attr_attributes.attr) && this_leaf->attributes)
+ return attr->mode;
+ 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,
+static struct attribute *cache_priv_attrs[] = {
+ &dev_attr_attributes.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)
-{
- 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;
- 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 attribute_group cache_private_group = {
+ .attrs = cache_priv_attrs,
+ .is_visible = cache_private_attrs_is_visible,
};
-static struct kobj_type cache_ktype_percpu_entry = {
- .sysfs_ops = &cache_sysfs_ops,
-};
-
-static void cpu_cache_sysfs_exit(unsigned int cpu)
+const struct attribute_group *
+cache_get_priv_group(struct cacheinfo *this_leaf)
{
- 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)
-{
- 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));
-
- return 0;
+ return &cache_private_group;
}
-/* Add cache interface for CPU device */
-static int cache_add_dev(struct device *sys_dev)
+static void ci_leaf_init(struct cacheinfo *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;
-
- 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;
+ unsigned number_of_sets = cci->pcci_cache_size;
+
+ number_of_sets /= cci->pcci_assoc;
+ number_of_sets /= 1 << cci->pcci_line_size;
+ 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 =
-{
- .notifier_call = cache_cpu_callback
-};
-
-static int __init cache_sysfs_init(void)
+int populate_cache_leaves(unsigned int cpu)
{
- int i;
-
- cpu_notifier_register_begin();
-
- 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 cacheinfo *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);
-
- cpu_notifier_register_done();
-
- return 0;
+ return cache_shared_cpu_map_setup(cpu);
}
-
-device_initcall(cache_sysfs_init);
-
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 06/11] s390: move cacheinfo sysfs to generic cacheinfo infrastructure
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (4 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 05/11] ia64: move cacheinfo sysfs to generic cacheinfo infrastructure Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 07/11] x86: " Sudeep Holla
` (5 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Martin Schwidefsky, linux390, linux-s390
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/kernel/cache.c | 388 +++++++++++------------------------------------
1 file changed, 92 insertions(+), 296 deletions(-)
diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c
index c0b03c28d157..fe21f074cf9f 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 {
@@ -70,39 +44,59 @@ struct cache_info {
};
#define CACHE_MAX_LEVEL 8
-
union cache_topology {
struct cache_info ci[CACHE_MAX_LEVEL];
unsigned long long raw;
};
static const char * const cache_type_string[] = {
- "Data",
+ "",
"Instruction",
+ "Data",
+ "",
"Unified",
};
-static struct cache_dir *cache_dir_cpu[NR_CPUS];
-static LIST_HEAD(cache_list);
+static const enum cache_type cache_type_map[] = {
+ [CTYPE_SEPARATE] = CACHE_TYPE_SEPARATE,
+ [CTYPE_DATA] = CACHE_TYPE_DATA,
+ [CTYPE_INSTRUCTION] = CACHE_TYPE_INST,
+ [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED,
+};
void show_cacheinfo(struct seq_file *m)
{
- struct cache *cache;
- int index = 0;
+ int cpu = smp_processor_id(), idx;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *cache;
- list_for_each_entry(cache, &cache_list, list) {
- seq_printf(m, "cache%-11d: ", index);
+ for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) {
+ cache = this_cpu_ci->info_list + idx;
+ seq_printf(m, "cache%-11d: ", idx);
seq_printf(m, "level=%d ", cache->level);
seq_printf(m, "type=%s ", cache_type_string[cache->type]);
- seq_printf(m, "scope=%s ", cache->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_printf(m, "scope=%s ",
+ cache->disable_sysfs ? "Shared" : "Private");
+ seq_printf(m, "size=%dK ", cache->size >> 10);
+ seq_printf(m, "line_size=%u ", cache->coherency_line_size);
+ seq_printf(m, "associativity=%d", cache->ways_of_associativity);
seq_puts(m, "\n");
- index++;
}
}
+static inline enum cache_type get_cache_type(struct cache_info *ci, int level)
+{
+ if (level >= CACHE_MAX_LEVEL)
+ return CACHE_TYPE_NOCACHE;
+
+ ci += level;
+
+ if (ci->scope != CACHE_SCOPE_SHARED && ci->scope != CACHE_SCOPE_PRIVATE)
+ return CACHE_TYPE_NOCACHE;
+
+ return cache_type_map[ci->type];
+}
+
static inline unsigned long ecag(int ai, int li, int ti)
{
unsigned long cmd, val;
@@ -113,277 +107,79 @@ 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 cacheinfo *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);
+ 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);
-static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
-{
- struct cache_index_dir *index;
- int len;
-
- 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;
-}
-
-static ssize_t shared_cpu_map_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
-{
- return shared_cpu_map_func(kobj, 0, buf);
+ num_sets = this_leaf->size / this_leaf->coherency_line_size;
+ num_sets /= this_leaf->ways_of_associativity;
+ this_leaf->number_of_sets = num_sets;
+ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
+ if (!private)
+ this_leaf->disable_sysfs = true;
}
-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)
+int init_cache_level(unsigned int cpu)
{
- 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 cacheinfo *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();
-
- cpu_notifier_register_begin();
- for_each_online_cpu(cpu)
- cache_add_cpu(cpu);
- __hotcpu_notifier(cache_hotplug, 0);
- cpu_notifier_register_done();
return 0;
}
-device_initcall(cache_init);
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 07/11] x86: move cacheinfo sysfs to generic cacheinfo infrastructure
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (5 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 06/11] s390: " Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 08/11] powerpc: " Sudeep Holla
` (4 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Thomas Gleixner, Ingo Molnar, x86
This patch removes the redundant sysfs cacheinfo code by making use of
the newly introduced generic cacheinfo infrastructure.
The private pointer provided by the cacheinfo 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 | 709 +++++++++-------------------------
1 file changed, 189 insertions(+), 520 deletions(-)
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index c7035073dfc1..5746aea7e9b8 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,11 +157,6 @@ 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;
/* AMD doesn't have CPUID4. Emulate it here to report the same
@@ -220,6 +213,13 @@ static const unsigned short assocs[] = {
static const unsigned char levels[] = { 1, 1, 2, 3 };
static const unsigned char types[] = { 1, 2, 3, 3 };
+static const enum cache_type cache_type_map[] = {
+ [CTYPE_NULL] = CACHE_TYPE_NOCACHE,
+ [CTYPE_DATA] = CACHE_TYPE_DATA,
+ [CTYPE_INST] = CACHE_TYPE_INST,
+ [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED,
+};
+
static void
amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
union _cpuid4_leaf_ebx *ebx,
@@ -291,14 +291,8 @@ 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)
+
/*
* L3 cache descriptors
*/
@@ -325,20 +319,6 @@ static void amd_calc_l3_indices(struct amd_northbridge *nb)
l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1;
}
-static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index)
-{
- int node;
-
- /* only for L3, and not in virtualized environments */
- if (index < 3)
- return;
-
- 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)
- amd_calc_l3_indices(this_leaf->nb);
-}
-
/*
* check whether a slot used for disabling an L3 index is occupied.
* @l3: L3 cache descriptor
@@ -359,15 +339,13 @@ 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 cacheinfo *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))
- 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,9 +354,10 @@ 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, \
- unsigned int cpu) \
+cache_disable_##slot##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
{ \
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \
return show_cache_disable(this_leaf, buf, slot); \
}
SHOW_CACHE_DISABLE(0)
@@ -446,25 +425,23 @@ 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,
- const char *buf, size_t count,
- unsigned int slot)
+static ssize_t store_cache_disable(struct cacheinfo *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))
- return -EINVAL;
-
- cpu = cpumask_first(to_cpumask(this_leaf->shared_cpu_map));
+ cpu = cpumask_first(&this_leaf->shared_cpu_map);
if (kstrtoul(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,41 +453,36 @@ 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, \
- const char *buf, size_t count, \
- unsigned int cpu) \
+cache_disable_##slot##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
{ \
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev); \
return store_cache_disable(this_leaf, buf, count, slot); \
}
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)
+static ssize_t subcaches_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
- return -EINVAL;
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ int cpu = cpumask_first(&this_leaf->shared_cpu_map);
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,
- unsigned int cpu)
+static ssize_t subcaches_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ int cpu = cpumask_first(&this_leaf->shared_cpu_map);
unsigned long val;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (!this_leaf->base.nb || !amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
- return -EINVAL;
-
if (kstrtoul(buf, 16, &val) < 0)
return -EINVAL;
@@ -520,9 +492,88 @@ 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);
+static DEVICE_ATTR_RW(cache_disable_0);
+static DEVICE_ATTR_RW(cache_disable_1);
+static DEVICE_ATTR_RW(subcaches);
+
+static umode_t
+cache_private_attrs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int unused)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct cacheinfo *this_leaf = dev_get_drvdata(dev);
+ umode_t mode = attr->mode;
+
+ if (!this_leaf->priv)
+ return 0;
+
+ if ((attr == &dev_attr_subcaches.attr) &&
+ amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+ return mode;
+
+ if ((attr == &dev_attr_cache_disable_0.attr ||
+ attr == &dev_attr_cache_disable_1.attr) &&
+ amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
+ return mode;
+
+ return 0;
+}
+
+static struct attribute_group cache_private_group = {
+ .is_visible = cache_private_attrs_is_visible,
+};
+
+static void init_amd_l3_attrs(void)
+{
+ int n = 1;
+ static struct attribute **amd_l3_attrs;
+
+ if (amd_l3_attrs) /* already initialized */
+ return;
+
+ 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 = kcalloc(n, sizeof(*amd_l3_attrs), GFP_KERNEL);
+ if (!amd_l3_attrs)
+ return;
+
+ n = 0;
+ if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {
+ amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr;
+ amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr;
+ }
+ if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
+ amd_l3_attrs[n++] = &dev_attr_subcaches.attr;
+
+ cache_private_group.attrs = amd_l3_attrs;
+}
+
+const struct attribute_group *
+cache_get_priv_group(struct cacheinfo *this_leaf)
+{
+ if (!this_leaf->priv || !cache_private_group.attrs)
+ return NULL;
+ return &cache_private_group;
+}
+
+static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index)
+{
+ int node;
+
+ /* only for L3, and not in virtualized environments */
+ if (index < 3)
+ return;
+ 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) {
+ amd_calc_l3_indices(this_leaf->nb);
+ init_amd_l3_attrs();
+ }
+}
#else
#define amd_init_l3_cache(x, y)
#endif /* CONFIG_AMD_NB && CONFIG_SYSFS */
@@ -546,7 +597,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 +626,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 +677,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:
@@ -747,55 +798,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 cacheinfo *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
@@ -804,459 +846,86 @@ 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 cacheinfo *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();
+ return;
- /* Do cpuid and store the results */
- for (j = 0; j < num_cache_leaves; j++) {
- struct _cpuid4_info *this_leaf = CPUID4_INFO_IDX(cpu, j);
+ index_msb = get_count_order(num_threads_sharing);
- *retval = cpuid4_cache_lookup_regs(j, &this_leaf->base);
- if (unlikely(*retval < 0)) {
- int i;
+ 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);
- for (i = 0; i < j; i++)
- cache_remove_shared_cpu_map(cpu, i);
- break;
+ 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 cacheinfo *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 = cache_type_map[base->eax.split.type];
+ this_leaf->coherency_line_size =
+ base->ebx.split.coherency_line_size + 1;
+ this_leaf->ways_of_associativity =
+ base->ebx.split.ways_of_associativity + 1;
+ this_leaf->size = base->size;
+ this_leaf->number_of_sets = base->ecx.split.number_of_sets + 1;
+ this_leaf->physical_line_partition =
+ base->ebx.split.physical_line_partition + 1;
+ this_leaf->priv = base->nb;
}
-static inline ssize_t show_shared_cpu_map(struct _cpuid4_info *leaf, char *buf,
- unsigned int cpu)
+static 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;
-}
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
-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)
+ if (!num_cache_leaves)
return -ENOENT;
-
- 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;
-
+ if (!this_cpu_ci)
+ return -EINVAL;
+ 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)
+static 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;
- }
-
- for (i = 0; i < num_cache_leaves; i++) {
- this_object = INDEX_KOBJECT_PTR(cpu, i);
- this_object->cpu = cpu;
- this_object->index = i;
+ unsigned int idx, ret;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+ struct _cpuid4_info_regs id4_regs = {};
- 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, err = 0;
-
- if (num_cache_leaves == 0)
- return 0;
-
- cpu_notifier_register_begin();
- for_each_online_cpu(i) {
- struct device *dev = get_cpu_device(i);
-
- err = cache_add_dev(dev);
- if (err)
- goto out;
- }
- __register_hotcpu_notifier(&cacheinfo_cpu_notifier);
-
-out:
- cpu_notifier_register_done();
- return err;
-}
-
-device_initcall(cache_sysfs_init);
-
-#endif
+DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
+DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves)
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 08/11] powerpc: move cacheinfo sysfs to generic cacheinfo infrastructure
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (6 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 07/11] x86: " Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 09/11] ARM64: kernel: add support for cpu cache information Sudeep Holla
` (3 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Benjamin Herrenschmidt, Paul Mackerras,
linuxppc-dev
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 | 812 +++++-----------------------------------
arch/powerpc/kernel/cacheinfo.h | 8 -
arch/powerpc/kernel/sysfs.c | 12 +-
3 files changed, 90 insertions(+), 742 deletions(-)
delete mode 100644 arch/powerpc/kernel/cacheinfo.h
diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c
index 40198d50b4c2..6845eb7fcc18 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-*
@@ -92,231 +59,82 @@ static const struct cache_type_info cache_type_info[] = {
},
};
-/* 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;
+ return type;
}
-static int cache_size(const struct cache *cache, unsigned int *ret)
+static void cache_size(struct cacheinfo *this_leaf)
{
const char *propname;
const __be32 *cache_size;
+ int ct_idx;
- propname = cache_type_info[cache->type].size_prop;
+ ct_idx = get_cacheinfo_idx(this_leaf->type);
+ propname = cache_type_info[ct_idx].size_prop;
- cache_size = of_get_property(cache->ofnode, propname, NULL);
+ cache_size = of_get_property(this_leaf->of_node, propname, NULL);
if (!cache_size)
- return -ENODEV;
-
- *ret = of_read_number(cache_size, 1);
- return 0;
-}
-
-static int cache_size_kb(const struct cache *cache, unsigned int *ret)
-{
- unsigned int size;
-
- if (cache_size(cache, &size))
- return -ENODEV;
-
- *ret = size / 1024;
- return 0;
+ this_leaf->size = 0;
+ else
+ this_leaf->size = of_read_number(cache_size, 1);
}
/* 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 void cache_get_line_size(struct cacheinfo *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)
- return -ENODEV;
-
- *ret = of_read_number(line_size, 1);
- return 0;
+ this_leaf->coherency_line_size = 0;
+ else
+ this_leaf->coherency_line_size = of_read_number(line_size, 1);
}
-static int cache_nr_sets(const struct cache *cache, unsigned int *ret)
+static void cache_nr_sets(struct cacheinfo *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);
+ nr_sets = of_get_property(this_leaf->of_node, propname, NULL);
if (!nr_sets)
- return -ENODEV;
-
- *ret = of_read_number(nr_sets, 1);
- return 0;
+ this_leaf->number_of_sets = 0;
+ else
+ this_leaf->number_of_sets = of_read_number(nr_sets, 1);
}
-static int cache_associativity(const struct cache *cache, unsigned int *ret)
+static void cache_associativity(struct cacheinfo *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;
- 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;
- }
-
- return cache;
+ if ((nr_sets == 1) || !(nr_sets > 0 && size > 0 && line_size > 0))
+ this_leaf->ways_of_associativity = 0;
+ else
+ this_leaf->ways_of_associativity = (size / nr_sets) / line_size;
}
static bool cache_node_is_unified(const struct device_node *np)
@@ -324,526 +142,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)
-{
- 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;
-}
-
-static struct cache *cache_do_one_devnode(struct device_node *node, int level)
-{
- 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;
-}
-
-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)
+static void ci_leaf_init(struct cacheinfo *this_leaf,
+ enum cache_type type, unsigned int level)
{
- 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;
+ 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_dir *cacheinfo_create_cache_dir(unsigned int cpu_id)
+int init_cache_level(unsigned int cpu)
{
- 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;
+ 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;
- 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))
- 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))
+ if (!cpu_dev) {
+ pr_err("No cpu device for CPU %d\n", cpu);
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);
}
-}
+ this_cpu_ci->num_levels = level;
+ this_cpu_ci->num_leaves = leaves;
-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);
-}
-
-/* functions needed to remove cache entry for cpu offline or suspend/resume */
-
-#if (defined(CONFIG_PPC_PSERIES) && defined(CONFIG_SUSPEND)) || \
- defined(CONFIG_HOTPLUG_CPU)
-
-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;
-
- index = cache_dir->index;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+ struct device *cpu_dev = get_cpu_device(cpu);
+ struct device_node *np;
+ unsigned int level, idx;
- 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);
- /* Remove cache dir from sysfs */
- kobject_del(cache_dir->kobj);
-
- 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_PPC_PSERIES && CONFIG_SUSPEND) || CONFIG_HOTPLUG_CPU */
diff --git a/arch/powerpc/kernel/cacheinfo.h b/arch/powerpc/kernel/cacheinfo.h
deleted file mode 100644
index a7b74d36acd7..000000000000
--- 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 67fd2fd2620a..6e9c5a8141bb 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>
@@ -743,7 +741,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
@@ -824,7 +821,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
@@ -988,8 +984,7 @@ static int __init topology_init(void)
int cpu;
register_nodes();
-
- cpu_notifier_register_begin();
+ register_cpu_notifier(&sysfs_cpu_nb);
for_each_possible_cpu(cpu) {
struct cpu *c = &per_cpu(cpu_devices, cpu);
@@ -1013,11 +1008,6 @@ static int __init topology_init(void)
if (cpu_online(cpu))
register_cpu_online(cpu);
}
-
- __register_cpu_notifier(&sysfs_cpu_nb);
-
- cpu_notifier_register_done();
-
#ifdef CONFIG_PPC64
sysfs_create_dscr_default();
#endif /* CONFIG_PPC64 */
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 09/11] ARM64: kernel: add support for cpu cache information
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (7 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 08/11] powerpc: " Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 10/11] ARM: " Sudeep Holla
` (2 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Catalin Marinas, Will Deacon, Mark Rutland,
linux-arm-kernel
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: Mark Rutland <mark.rutland@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
arch/arm64/include/asm/cachetype.h | 29 +++++++--
arch/arm64/kernel/Makefile | 2 +-
arch/arm64/kernel/cacheinfo.c | 128 +++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/cpuinfo.c | 12 ----
4 files changed, 152 insertions(+), 19 deletions(-)
create mode 100644 arch/arm64/kernel/cacheinfo.c
diff --git a/arch/arm64/include/asm/cachetype.h b/arch/arm64/include/asm/cachetype.h
index 4c631a0a3609..da2fc9e3cedd 100644
--- a/arch/arm64/include/asm/cachetype.h
+++ b/arch/arm64/include/asm/cachetype.h
@@ -39,24 +39,41 @@
extern unsigned long __icache_flags;
+/*
+ * 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_EL1_WRITE_THROUGH BIT(31)
+#define CCSIDR_EL1_WRITE_BACK BIT(30)
+#define CCSIDR_EL1_READ_ALLOCATE BIT(29)
+#define CCSIDR_EL1_WRITE_ALLOCATE BIT(28)
#define CCSIDR_EL1_LINESIZE_MASK 0x7
#define CCSIDR_EL1_LINESIZE(x) ((x) & CCSIDR_EL1_LINESIZE_MASK)
-
+#define CCSIDR_EL1_ASSOCIATIVITY_SHIFT 3
+#define CCSIDR_EL1_ASSOCIATIVITY_MASK 0x3ff
+#define CCSIDR_EL1_ASSOCIATIVITY(x) \
+ (((x) >> CCSIDR_EL1_ASSOCIATIVITY_SHIFT) & CCSIDR_EL1_ASSOCIATIVITY_MASK)
#define CCSIDR_EL1_NUMSETS_SHIFT 13
-#define CCSIDR_EL1_NUMSETS_MASK (0x7fff << CCSIDR_EL1_NUMSETS_SHIFT)
+#define CCSIDR_EL1_NUMSETS_MASK 0x7fff
#define CCSIDR_EL1_NUMSETS(x) \
- (((x) & CCSIDR_EL1_NUMSETS_MASK) >> CCSIDR_EL1_NUMSETS_SHIFT)
+ (((x) >> CCSIDR_EL1_NUMSETS_SHIFT) & CCSIDR_EL1_NUMSETS_MASK)
+
+#define CACHE_LINESIZE(x) (16 << CCSIDR_EL1_LINESIZE(x))
+#define CACHE_NUMSETS(x) (CCSIDR_EL1_NUMSETS(x) + 1)
+#define CACHE_ASSOCIATIVITY(x) (CCSIDR_EL1_ASSOCIATIVITY(x) + 1)
-extern u64 __attribute_const__ icache_get_ccsidr(void);
+extern u64 __attribute_const__ cache_get_ccsidr(u64 csselr);
+/* Helpers for Level 1 Instruction cache csselr = 1L */
static inline int icache_get_linesize(void)
{
- return 16 << CCSIDR_EL1_LINESIZE(icache_get_ccsidr());
+ return CACHE_LINESIZE(cache_get_ccsidr(1L));
}
static inline int icache_get_numsets(void)
{
- return 1 + CCSIDR_EL1_NUMSETS(icache_get_ccsidr());
+ return CACHE_NUMSETS(cache_get_ccsidr(1L));
}
/*
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index df7ef8768fc2..285cd88c1e37 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -15,7 +15,7 @@ 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 return_address.o \
- cpuinfo.o
+ cpuinfo.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 000000000000..497b0e557bd5
--- /dev/null
+++ b/arch/arm64/kernel/cacheinfo.c
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/cachetype.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)
+{
+ u64 clidr;
+
+ if (level > MAX_CACHE_LEVEL)
+ return CACHE_TYPE_NOCACHE;
+ asm volatile ("mrs %x0, clidr_el1" : "=r" (clidr));
+ return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * Cache Size Selection Register(CSSELR) selects which Cache Size ID
+ * Register(CCSIDR) is accessible by specifying the required cache
+ * level and the cache type. We need to ensure that no one else changes
+ * CSSELR by calling this in non-preemtible context
+ */
+u64 __attribute_const__ cache_get_ccsidr(u64 csselr)
+{
+ u64 ccsidr;
+
+ WARN_ON(preemptible());
+
+ /* Put value into CSSELR */
+ asm volatile("msr csselr_el1, %x0" : : "r" (csselr));
+ isb();
+ /* Read result out of CCSIDR */
+ asm volatile("mrs %x0, ccsidr_el1" : "=r" (ccsidr));
+
+ return ccsidr;
+}
+
+static void ci_leaf_init(struct cacheinfo *this_leaf,
+ enum cache_type type, unsigned int level)
+{
+ bool is_icache = type & CACHE_TYPE_INST;
+ u64 tmp = cache_get_ccsidr((level - 1) << 1 | is_icache);
+
+ this_leaf->level = level;
+ this_leaf->type = type;
+ this_leaf->coherency_line_size = CACHE_LINESIZE(tmp);
+ this_leaf->number_of_sets = CACHE_NUMSETS(tmp);
+ this_leaf->ways_of_associativity = CACHE_ASSOCIATIVITY(tmp);
+ this_leaf->size = this_leaf->number_of_sets *
+ this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+ this_leaf->attributes =
+ ((tmp & CCSIDR_EL1_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+ ((tmp & CCSIDR_EL1_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+ ((tmp & CCSIDR_EL1_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+ ((tmp & CCSIDR_EL1_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+static int __init_cache_level(unsigned int cpu)
+{
+ unsigned int ctype, level, leaves;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+ for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
+ ctype = get_cache_type(level);
+ if (ctype == CACHE_TYPE_NOCACHE) {
+ level--;
+ break;
+ }
+ /* Separate instruction and data caches */
+ leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+ }
+
+ this_cpu_ci->num_levels = level;
+ this_cpu_ci->num_leaves = leaves;
+ return 0;
+}
+
+static 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 cacheinfo *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++) {
+ 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;
+}
+
+DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
+DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves)
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 504fdaa8367e..8fc00e919b52 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -202,15 +202,3 @@ void __init cpuinfo_store_boot_cpu(void)
boot_cpu_data = *info;
}
-
-u64 __attribute_const__ icache_get_ccsidr(void)
-{
- u64 ccsidr;
-
- WARN_ON(preemptible());
-
- /* Select L1 I-cache and read its size ID register */
- asm("msr csselr_el1, %1; isb; mrs %0, ccsidr_el1"
- : "=r"(ccsidr) : "r"(1L));
- return ccsidr;
-}
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 10/11] ARM: kernel: add support for cpu cache information
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (8 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 09/11] ARM64: kernel: add support for cpu cache information Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-09-30 13:48 ` [PATCH v5 11/11] ARM: kernel: add outer cache support for cacheinfo implementation Sudeep Holla
2014-10-20 18:46 ` [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Russell King, Will Deacon, linux-arm-kernel
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>
Tested-by: Stephen Boyd <sboyd@codeaurora.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/cacheinfo.c | 282 ++++++++++++++++++++++++++++++++++++++++++++
arch/arm/mm/Kconfig | 13 ++
3 files changed, 296 insertions(+)
create mode 100644 arch/arm/kernel/cacheinfo.c
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 38ddd9f83d0e..2c5ff0efb670 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 000000000000..8e4f677741d8
--- /dev/null
+++ b/arch/arm/kernel/cacheinfo.c
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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>
+#include <asm/system_info.h>
+
+#define cache_is_armv7() \
+ (cpu_architecture() >= CPU_ARCH_ARMv7 && !armv6_extended())
+#define MAX_CACHE_LEVEL (cache_is_armv7() ? 7 : 1)
+
+#define CTR_CTYPE_SHIFT 24
+#define CTR_CTYPE_MASK (1 << CTR_CTYPE_SHIFT)
+
+struct ctr_info {
+ unsigned int cpuid_part;
+ unsigned int ctr;
+};
+
+/*
+ * Cache Type Register
+ * +---------------------------------+
+ * | 31 29 | 28 25 |24| 23 12 | 11 0 |
+ * +---------------------------------+
+ * | 0 0 0 | Ctype | S| Dsize | Isize|
+ * +---------------------------------+
+ * The table below encodes only Dsize and Isize
+ */
+static struct ctr_info cache_ctr_list[] = {
+ {0x4400a100, 0x0016A16A }, /* SA-110: 32kB D$, 32kB I$ */
+ {0x4400a110, 0x0012A16A }, /* SA-1100: 16kB D$, 32kB I$ */
+ {0x6900b110, 0x0012A16A }, /* SA-1110: 16kB D$, 32kB I$ */
+};
+
+/*
+ * List of CPUs reported as ARMv7 but don't implement CLIDR,
+ * CSSELR and CCSIDR. Cache information is still available from CTR
+ */
+static int armv6_ext_cpuid_part[] = {
+ 0x4100b020, /* ARM11MP */
+ 0x4100b760, /* ARM1176 */
+};
+
+static bool armv6_extended(void)
+{
+ int i, cpuid_part = read_cpuid_part();
+
+ for (i = 0; i < ARRAY_SIZE(armv6_ext_cpuid_part); i++)
+ if (armv6_ext_cpuid_part[i] == cpuid_part)
+ return true;
+ return false;
+}
+
+static int get_unimplemented_ctr(unsigned int *ctr)
+{
+ int i, cpuid_part = read_cpuid_part();
+
+ for (i = 0; i < ARRAY_SIZE(cache_ctr_list); i++)
+ if (cache_ctr_list[i].cpuid_part == cpuid_part) {
+ *ctr = cache_ctr_list[i].ctr;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+static unsigned int get_ctr(void)
+{
+ unsigned int ctr;
+
+ if (get_unimplemented_ctr(&ctr))
+ ctr = read_cpuid_cachetype();
+ 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 cacheinfo *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);
+}
+
+/* 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 __armv7_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_LINESIZE(x) ((x) & CCSIDR_LINESIZE_MASK)
+#define CCSIDR_ASSOCIATIVITY_SHIFT 3
+#define CCSIDR_ASSOCIATIVITY_MASK 0x3ff
+#define CCSIDR_ASSOCIATIVITY(x) \
+ (((x) >> CCSIDR_ASSOCIATIVITY_SHIFT) & CCSIDR_ASSOCIATIVITY_MASK)
+#define CCSIDR_NUMSETS_SHIFT 13
+#define CCSIDR_NUMSETS_MASK 0x7fff
+#define CCSIDR_NUMSETS(x) \
+ (((x) >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK)
+
+#define CACHE_LINESIZE(x) (16 << CCSIDR_LINESIZE(x))
+#define CACHE_NUMSETS(x) (CCSIDR_NUMSETS(x) + 1)
+#define CACHE_ASSOCIATIVITY(x) (CCSIDR_ASSOCIATIVITY(x) + 1)
+
+/*
+ * Cache Size Selection Register(CSSELR) selects which Cache Size ID
+ * Register(CCSIDR) is accessible by specifying the required cache
+ * level and the cache type. We need to ensure that no one else changes
+ * CSSELR by calling this in non-preemtible context
+ */
+static u32 __attribute_const__ cache_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 __armv7_ci_leaf_init(enum cache_type type,
+ struct cacheinfo *this_leaf)
+{
+ bool is_icache = type & CACHE_TYPE_INST;
+ u32 tmp = cache_get_ccsidr((this_leaf->level - 1) << 1 | is_icache);
+
+ this_leaf->type = type;
+ this_leaf->coherency_line_size = CACHE_LINESIZE(tmp);
+ this_leaf->number_of_sets = CACHE_NUMSETS(tmp);
+ this_leaf->ways_of_associativity = CACHE_ASSOCIATIVITY(tmp);
+ 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);
+}
+
+static inline enum cache_type get_cache_type(int level)
+{
+ if (cache_is_armv7())
+ return __armv7_get_cache_type(level);
+ return __get_cache_type(level);
+}
+
+static void ci_leaf_init(struct cacheinfo *this_leaf,
+ enum cache_type type, unsigned int level)
+{
+ this_leaf->level = level;
+ if (cache_is_armv7())
+ __armv7_ci_leaf_init(type, this_leaf);
+ else
+ __ci_leaf_init(type, this_leaf);
+}
+
+static int __init_cache_level(unsigned int cpu)
+{
+ unsigned int ctype, level, leaves;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+ for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
+ ctype = get_cache_type(level);
+ if (ctype == CACHE_TYPE_NOCACHE) {
+ level--;
+ break;
+ }
+ /* Separate instruction and data caches */
+ leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+ }
+
+ this_cpu_ci->num_levels = level;
+ this_cpu_ci->num_leaves = leaves;
+
+ return 0;
+}
+
+static 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 cacheinfo *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++) {
+ 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;
+}
+
+DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
+DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves)
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index ae69809a9e47..9cfbf2fa8bc4 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -494,30 +494,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
@@ -845,6 +857,7 @@ config DMA_CACHE_RWFO
config OUTER_CACHE
bool
+ select CPU_HAS_CACHE
config OUTER_CACHE_SYNC
bool
--
1.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH v5 11/11] ARM: kernel: add outer cache support for cacheinfo implementation
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (9 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 10/11] ARM: " Sudeep Holla
@ 2014-09-30 13:48 ` Sudeep Holla
2014-10-20 18:46 ` [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
11 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2014-09-30 13:48 UTC (permalink / raw)
To: LKML
Cc: Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi,
Greg Kroah-Hartman, Russell King, Will Deacon, linux-arm-kernel
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: linux-arm-kernel@lists.infradead.org
---
arch/arm/include/asm/outercache.h | 9 +++++++++
arch/arm/kernel/cacheinfo.c | 14 +++++++++++++-
arch/arm/mm/cache-l2x0.c | 35 ++++++++++++++++++++++++++++++++++-
arch/arm/mm/cache-tauros2.c | 36 ++++++++++++++++++++++++++++++++++++
arch/arm/mm/cache-xsc3l2.c | 17 +++++++++++++++++
5 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 891a56b35bcf..e063d8c87077 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -23,7 +23,10 @@
#include <linux/types.h>
+struct cacheinfo;
+
struct outer_cache_fns {
+ void (*get_info)(struct cacheinfo *);
void (*inv_range)(unsigned long, unsigned long);
void (*clean_range)(unsigned long, unsigned long);
void (*flush_range)(unsigned long, unsigned long);
@@ -112,6 +115,11 @@ static inline void outer_resume(void)
outer_cache.resume();
}
+static inline void outer_get_info(struct cacheinfo *this_leaf)
+{
+ if (outer_cache.get_info)
+ outer_cache.get_info(this_leaf);
+}
#else
static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
@@ -123,6 +131,7 @@ static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
static inline void outer_flush_all(void) { }
static inline void outer_disable(void) { }
static inline void outer_resume(void) { }
+static inline void outer_get_info(struct cacheinfo *this_leaf) { }
#endif
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 8e4f677741d8..2107e1d67914 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -24,6 +24,7 @@
#include <linux/of.h>
#include <asm/cputype.h>
+#include <asm/outercache.h>
#include <asm/processor.h>
#include <asm/system_info.h>
@@ -227,11 +228,19 @@ static inline enum cache_type get_cache_type(int level)
return __get_cache_type(level);
}
+static inline void __outer_ci_leaf_init(struct cacheinfo *this_leaf)
+{
+ outer_get_info(this_leaf);
+ BUG_ON(this_leaf->type == CACHE_TYPE_SEPARATE);
+}
+
static void ci_leaf_init(struct cacheinfo *this_leaf,
enum cache_type type, unsigned int level)
{
this_leaf->level = level;
- if (cache_is_armv7())
+ if (type == CACHE_TYPE_NOCACHE) /* must be outer cache */
+ __outer_ci_leaf_init(this_leaf);
+ else if (cache_is_armv7())
__armv7_ci_leaf_init(type, this_leaf);
else
__ci_leaf_init(type, this_leaf);
@@ -255,6 +264,9 @@ static int __init_cache_level(unsigned int cpu)
this_cpu_ci->num_levels = level;
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 5f2c988a06ac..4114b1944807 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/cpu.h>
+#include <linux/cacheinfo.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/smp.h>
@@ -105,6 +106,22 @@ static inline void l2c_unlock(void __iomem *base, unsigned num)
}
}
+static void __l2x0_getinfo(struct cacheinfo *this_leaf)
+{
+ unsigned int assoc = get_count_order(l2x0_way_mask);
+
+ this_leaf->size = l2x0_size;
+ this_leaf->coherency_line_size = CACHE_LINE_SIZE;
+ this_leaf->ways_of_associativity = assoc;
+ this_leaf->number_of_sets = l2x0_size / (assoc * CACHE_LINE_SIZE);
+}
+
+static void l2x0_getinfo(struct cacheinfo *this_leaf)
+{
+ this_leaf->type = CACHE_TYPE_UNIFIED;
+ __l2x0_getinfo(this_leaf);
+}
+
/*
* Enable the L2 cache controller. This function must only be
* called when the cache controller is known to be disabled.
@@ -309,6 +326,7 @@ static const struct l2c_init_data l2c210_data __initconst = {
.disable = l2c_disable,
.sync = l2c210_sync,
.resume = l2c210_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -466,6 +484,7 @@ static const struct l2c_init_data l2c220_data = {
.disable = l2c_disable,
.sync = l2c220_sync,
.resume = l2c210_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -814,6 +833,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
.disable = l2c310_disable,
.sync = l2c210_sync,
.resume = l2c310_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -894,7 +914,6 @@ static void __init __l2c_init(const struct l2c_init_data *data,
data->enable(l2x0_base, aux, data->num_lock);
outer_cache = fns;
-
/*
* It is strange to save the register state before initialisation,
* but hey, this is what the DT implementations decided to do.
@@ -994,6 +1013,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = {
.disable = l2c_disable,
.sync = l2c210_sync,
.resume = l2c210_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1012,6 +1032,7 @@ static const struct l2c_init_data of_l2c220_data __initconst = {
.disable = l2c_disable,
.sync = l2c220_sync,
.resume = l2c210_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1065,6 +1086,7 @@ static const struct l2c_init_data of_l2c310_data __initconst = {
.disable = l2c310_disable,
.sync = l2c210_sync,
.resume = l2c310_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1092,6 +1114,7 @@ static const struct l2c_init_data of_l2c310_coherent_data __initconst = {
.flush_all = l2c210_flush_all,
.disable = l2c310_disable,
.resume = l2c310_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1255,6 +1278,12 @@ static void __init aurora_of_parse(const struct device_node *np,
*aux_mask &= ~mask;
}
+static void aurora_no_outer_data_getinfo(struct cacheinfo *this_leaf)
+{
+ this_leaf->type = CACHE_TYPE_INST;
+ __l2x0_getinfo(this_leaf);
+}
+
static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
.type = "Aurora",
.way_size_0 = SZ_4K,
@@ -1271,6 +1300,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
.disable = l2x0_disable,
.sync = l2x0_cache_sync,
.resume = aurora_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1284,6 +1314,7 @@ static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
.save = aurora_save,
.outer_cache = {
.resume = aurora_resume,
+ .get_info = aurora_no_outer_data_getinfo,
},
};
@@ -1439,6 +1470,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
.disable = l2c310_disable,
.sync = l2c210_sync,
.resume = l2c310_resume,
+ .get_info = l2x0_getinfo,
},
};
@@ -1475,6 +1507,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = {
/* Tauros3 broadcasts L1 cache operations to L2 */
.outer_cache = {
.resume = tauros3_resume,
+ .get_info = l2x0_getinfo,
},
};
diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c
index b273739e6359..0d4dd8ff6a56 100644
--- a/arch/arm/mm/cache-tauros2.c
+++ b/arch/arm/mm/cache-tauros2.c
@@ -14,6 +14,7 @@
* Document ID MV-S105190-00, Rev 0.7, March 14 2008.
*/
+#include <linux/cacheinfo.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -60,6 +61,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 +133,39 @@ 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 cacheinfo *this_leaf)
+{
+ unsigned int l2_ctr;
+
+ __asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2_ctr));
+
+ this_leaf->type = CACHE_TYPE_UNIFIED;
+ this_leaf->coherency_line_size = CACHE_LINE_SIZE;
+ this_leaf->ways_of_associativity = CACHE_WAY_PER_SET(l2_ctr);
+ this_leaf->number_of_sets = CACHE_SET_SIZE(l2_ctr);
+ this_leaf->size = this_leaf->coherency_line_size *
+ this_leaf->number_of_sets *
+ this_leaf->ways_of_associativity;
+}
+
#endif
static inline u32 __init read_extra_features(void)
@@ -226,6 +261,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 6c3edeb66e74..175bf44eb039 100644
--- a/arch/arm/mm/cache-xsc3l2.c
+++ b/arch/arm/mm/cache-xsc3l2.c
@@ -16,6 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/cacheinfo.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <asm/cp15.h>
@@ -201,6 +202,21 @@ static void xsc3_l2_flush_range(unsigned long start, unsigned long end)
dsb();
}
+static void xsc3_l2_getinfo(struct cacheinfo *this_leaf)
+{
+ unsigned long l2ctype;
+
+ __asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+ this_leaf->type = CACHE_TYPE_UNIFIED;
+ this_leaf->coherency_line_size = CACHE_LINE_SIZE;
+ this_leaf->ways_of_associativity = CACHE_WAY_PER_SET;
+ this_leaf->number_of_sets = CACHE_SET_SIZE(l2ctype);
+ this_leaf->size = this_leaf->coherency_line_size *
+ this_leaf->number_of_sets *
+ this_leaf->ways_of_associativity;
+}
+
static int __init xsc3_l2_init(void)
{
if (!cpu_is_xsc3() || !xsc3_l2_present())
@@ -213,6 +229,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.9.1
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-09-30 13:48 [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
` (10 preceding siblings ...)
2014-09-30 13:48 ` [PATCH v5 11/11] ARM: kernel: add outer cache support for cacheinfo implementation Sudeep Holla
@ 2014-10-20 18:46 ` Sudeep Holla
2014-10-20 18:49 ` Greg Kroah-Hartman
11 siblings, 1 reply; 19+ messages in thread
From: Sudeep Holla @ 2014-10-20 18:46 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: LKML, Sudeep Holla, Heiko Carstens, Lorenzo Pieralisi
Hi Greg,
On 30/09/14 14:48, Sudeep Holla wrote:
> 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 tested on x86.
>
> This series also adds support for ARM and ARM64 architectures based on
> the generic support.
>
> The code can be fetched from:
> git://linux-arm.org/linux-skn cacheinfo
>
I am hoping to target this for v3.19 if possible, it would be good if
you can review and provide feedback.
Regards,
Sudeep
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-10-20 18:46 ` [PATCH v5 00/11] drivers: cacheinfo support Sudeep Holla
@ 2014-10-20 18:49 ` Greg Kroah-Hartman
2014-10-21 9:26 ` Sudeep Holla
0 siblings, 1 reply; 19+ messages in thread
From: Greg Kroah-Hartman @ 2014-10-20 18:49 UTC (permalink / raw)
To: Sudeep Holla; +Cc: LKML, Heiko Carstens, Lorenzo Pieralisi
On Mon, Oct 20, 2014 at 07:46:19PM +0100, Sudeep Holla wrote:
> Hi Greg,
>
> On 30/09/14 14:48, Sudeep Holla wrote:
> >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 tested on x86.
> >
> >This series also adds support for ARM and ARM64 architectures based on
> >the generic support.
> >
> >The code can be fetched from:
> > git://linux-arm.org/linux-skn cacheinfo
> >
>
> I am hoping to target this for v3.19 if possible, it would be good if
> you can review and provide feedback.
3.18-rc1 just came out a few _hours_ ago, and now we can start worrying
about what goes into 3.19. I have 1257 emails in my "to-review" queue
for 3.19, please give me a chance to catch up in a few weeks to get to
this code.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-10-20 18:49 ` Greg Kroah-Hartman
@ 2014-10-21 9:26 ` Sudeep Holla
2014-11-07 19:23 ` Greg Kroah-Hartman
0 siblings, 1 reply; 19+ messages in thread
From: Sudeep Holla @ 2014-10-21 9:26 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: Sudeep Holla, LKML, Heiko Carstens, Lorenzo Pieralisi
Hi Greg,
On 20/10/14 19:49, Greg Kroah-Hartman wrote:
> On Mon, Oct 20, 2014 at 07:46:19PM +0100, Sudeep Holla wrote:
>> Hi Greg,
>>
>> On 30/09/14 14:48, Sudeep Holla wrote:
>>> 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 tested on x86.
>>>
>>> This series also adds support for ARM and ARM64 architectures based on
>>> the generic support.
>>>
>>> The code can be fetched from:
>>> git://linux-arm.org/linux-skn cacheinfo
>>>
>>
>> I am hoping to target this for v3.19 if possible, it would be good if
>> you can review and provide feedback.
>
> 3.18-rc1 just came out a few _hours_ ago, and now we can start worrying
> about what goes into 3.19. I have 1257 emails in my "to-review" queue
> for 3.19, please give me a chance to catch up in a few weeks to get to
> this code.
>
I completely understand and I am sorry if that was a noise.
I just wanted to ensure it's not lost, thanks for letting me know it's
in your list.
Regards,
Sudeep
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-10-21 9:26 ` Sudeep Holla
@ 2014-11-07 19:23 ` Greg Kroah-Hartman
2014-11-07 19:40 ` Sudeep Holla
0 siblings, 1 reply; 19+ messages in thread
From: Greg Kroah-Hartman @ 2014-11-07 19:23 UTC (permalink / raw)
To: Sudeep Holla; +Cc: LKML, Heiko Carstens, Lorenzo Pieralisi
On Tue, Oct 21, 2014 at 10:26:05AM +0100, Sudeep Holla wrote:
> Hi Greg,
>
> On 20/10/14 19:49, Greg Kroah-Hartman wrote:
> >On Mon, Oct 20, 2014 at 07:46:19PM +0100, Sudeep Holla wrote:
> >>Hi Greg,
> >>
> >>On 30/09/14 14:48, Sudeep Holla wrote:
> >>>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 tested on x86.
> >>>
> >>>This series also adds support for ARM and ARM64 architectures based on
> >>>the generic support.
> >>>
> >>>The code can be fetched from:
> >>> git://linux-arm.org/linux-skn cacheinfo
> >>>
> >>
> >>I am hoping to target this for v3.19 if possible, it would be good if
> >>you can review and provide feedback.
> >
> >3.18-rc1 just came out a few _hours_ ago, and now we can start worrying
> >about what goes into 3.19. I have 1257 emails in my "to-review" queue
> >for 3.19, please give me a chance to catch up in a few weeks to get to
> >this code.
> >
>
> I completely understand and I am sorry if that was a noise.
> I just wanted to ensure it's not lost, thanks for letting me know it's
> in your list.
Do you want me to take this through my tree? If so, I will, otherwise,
feel free to add:
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
to the series.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-11-07 19:23 ` Greg Kroah-Hartman
@ 2014-11-07 19:40 ` Sudeep Holla
2014-11-07 19:45 ` Greg Kroah-Hartman
0 siblings, 1 reply; 19+ messages in thread
From: Sudeep Holla @ 2014-11-07 19:40 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: Sudeep Holla, LKML, Heiko Carstens, Lorenzo Pieralisi
On 07/11/14 19:23, Greg Kroah-Hartman wrote:
> On Tue, Oct 21, 2014 at 10:26:05AM +0100, Sudeep Holla wrote:
>> Hi Greg,
>>
>> On 20/10/14 19:49, Greg Kroah-Hartman wrote:
>>> On Mon, Oct 20, 2014 at 07:46:19PM +0100, Sudeep Holla wrote:
>>>> Hi Greg,
>>>>
>>>> On 30/09/14 14:48, Sudeep Holla wrote:
>>>>> 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 tested on x86.
>>>>>
>>>>> This series also adds support for ARM and ARM64 architectures based on
>>>>> the generic support.
>>>>>
>>>>> The code can be fetched from:
>>>>> git://linux-arm.org/linux-skn cacheinfo
>>>>>
>>>>
>>>> I am hoping to target this for v3.19 if possible, it would be good if
>>>> you can review and provide feedback.
>>>
>>> 3.18-rc1 just came out a few _hours_ ago, and now we can start worrying
>>> about what goes into 3.19. I have 1257 emails in my "to-review" queue
>>> for 3.19, please give me a chance to catch up in a few weeks to get to
>>> this code.
>>>
>>
>> I completely understand and I am sorry if that was a noise.
>> I just wanted to ensure it's not lost, thanks for letting me know it's
>> in your list.
>
> Do you want me to take this through my tree? If so, I will, otherwise,
> feel free to add:
I have not got it tested on AMD, IA64 and PPC. Probably they were
waiting for the core driver to settle down. I have only tested on
ARM{32,64} and x86. Heiko Carstens tested on s390 long back some
previous version.
If it fine with you, I would like to break the first 4 patches and
target it for v3.19, and take up the arch-specific patches (i.e.5-11)
after that, so that I can get them tested on all architectures.
>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>
> to the series.
Thanks, I will rebase on v3.18-rc4 and repost first 4 patches on Monday
which you can take it through your tree *iff you agree with me* on above
flow.
Regards,
Sudeep
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH v5 00/11] drivers: cacheinfo support
2014-11-07 19:40 ` Sudeep Holla
@ 2014-11-07 19:45 ` Greg Kroah-Hartman
0 siblings, 0 replies; 19+ messages in thread
From: Greg Kroah-Hartman @ 2014-11-07 19:45 UTC (permalink / raw)
To: Sudeep Holla; +Cc: LKML, Heiko Carstens, Lorenzo Pieralisi
On Fri, Nov 07, 2014 at 07:40:51PM +0000, Sudeep Holla wrote:
>
>
> On 07/11/14 19:23, Greg Kroah-Hartman wrote:
> >On Tue, Oct 21, 2014 at 10:26:05AM +0100, Sudeep Holla wrote:
> >>Hi Greg,
> >>
> >>On 20/10/14 19:49, Greg Kroah-Hartman wrote:
> >>>On Mon, Oct 20, 2014 at 07:46:19PM +0100, Sudeep Holla wrote:
> >>>>Hi Greg,
> >>>>
> >>>>On 30/09/14 14:48, Sudeep Holla wrote:
> >>>>>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 tested on x86.
> >>>>>
> >>>>>This series also adds support for ARM and ARM64 architectures based on
> >>>>>the generic support.
> >>>>>
> >>>>>The code can be fetched from:
> >>>>> git://linux-arm.org/linux-skn cacheinfo
> >>>>>
> >>>>
> >>>>I am hoping to target this for v3.19 if possible, it would be good if
> >>>>you can review and provide feedback.
> >>>
> >>>3.18-rc1 just came out a few _hours_ ago, and now we can start worrying
> >>>about what goes into 3.19. I have 1257 emails in my "to-review" queue
> >>>for 3.19, please give me a chance to catch up in a few weeks to get to
> >>>this code.
> >>>
> >>
> >>I completely understand and I am sorry if that was a noise.
> >>I just wanted to ensure it's not lost, thanks for letting me know it's
> >>in your list.
> >
> >Do you want me to take this through my tree? If so, I will, otherwise,
> >feel free to add:
>
> I have not got it tested on AMD, IA64 and PPC. Probably they were
> waiting for the core driver to settle down. I have only tested on
> ARM{32,64} and x86. Heiko Carstens tested on s390 long back some
> previous version.
>
> If it fine with you, I would like to break the first 4 patches and
> target it for v3.19, and take up the arch-specific patches (i.e.5-11)
> after that, so that I can get them tested on all architectures.
>
> >
> >Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >
> >to the series.
>
> Thanks, I will rebase on v3.18-rc4 and repost first 4 patches on Monday
> which you can take it through your tree *iff you agree with me* on above
> flow.
I've just taken the first 4 patches in my tree now, no need to repost
them. Let's see what breaks with them :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 19+ messages in thread