Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1635778 > unrolled thread
| Started by | Anju T Sudhakar <anju@linux.vnet.ibm.com> |
|---|---|
| First post | 2017-05-04 16:30 +0200 |
| Last post | 2017-05-12 06:40 +0200 |
| Articles | 6 on this page of 26 — 7 participants |
Back to article view | Back to linux.kernel
[PATCH v8 00/10] IMC Instrumentation Support Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 07/10] powerpc/perf: PMU functions for Core IMC and hotplugging Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
Re: [PATCH v8 07/10] powerpc/perf: PMU functions for Core IMC and hotplugging Stewart Smith <stewart@linux.vnet.ibm.com> - 2017-05-17 10:00 +0200
[PATCH v8 10/10] powerpc/perf: Thread imc cpuhotplug support Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Daniel Axtens <dja@axtens.net> - 2017-05-08 16:20 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Madhavan Srinivasan <maddy@linux.vnet.ibm.com> - 2017-05-09 08:20 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Stewart Smith <stewart@linux.vnet.ibm.com> - 2017-05-12 04:20 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Michael Ellerman <mpe@ellerman.id.au> - 2017-05-12 05:40 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Madhavan Srinivasan <maddy@linux.vnet.ibm.com> - 2017-05-12 06:00 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Madhavan Srinivasan <maddy@linux.vnet.ibm.com> - 2017-05-12 05:50 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-09 13:00 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Thomas Gleixner <tglx@linutronix.de> - 2017-05-10 14:20 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Stephen Rothwell <sfr@canb.auug.org.au> - 2017-05-11 01:50 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Thomas Gleixner <tglx@linutronix.de> - 2017-05-11 10:50 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Madhavan Srinivasan <maddy@linux.vnet.ibm.com> - 2017-05-15 12:20 +0200
Re: [PATCH v8 05/10] powerpc/perf: IMC pmu cpumask and cpuhotplug support Thomas Gleixner <tglx@linutronix.de> - 2017-05-15 13:10 +0200
[PATCH v8 06/10] powerpc/powernv: Core IMC events detection Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 08/10] powerpc/powernv: Thread IMC events detection Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 01/10] powerpc/powernv: Data structure and macros definitions for IMC Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 09/10] powerpc/perf: Thread IMC PMU functions Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 04/10] powerpc/perf: Add generic IMC pmu groupand event functions Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 03/10] powerpc/powernv: Detect supported IMC units and its events Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
[PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module Anju T Sudhakar <anju@linux.vnet.ibm.com> - 2017-05-04 16:30 +0200
Re: [PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module Stewart Smith <stewart@linux.vnet.ibm.com> - 2017-05-11 09:50 +0200
Re: [PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module Madhavan Srinivasan <maddy@linux.vnet.ibm.com> - 2017-05-12 06:40 +0200
Page 2 of 2 — ← Prev page 1 [2]
| From | Anju T Sudhakar <anju@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-04 16:30 +0200 |
| Subject | [PATCH v8 09/10] powerpc/perf: Thread IMC PMU functions |
| Message-ID | <tDpVw-4CY-21@gated-at.bofh.it> |
| In reply to | #1635778 |
This patch adds the PMU functions required for event initialization,
read, update, add, del etc. for thread IMC PMU. Thread IMC PMUs are used
for per-task monitoring.
For each CPU, a page of memory is allocated and is kept static i.e.,
these pages will exist till the machine shuts down. The base address of
this page is assigned to the ldbar of that cpu. As soon as we do that,
the thread IMC counters start running for that cpu and the data of these
counters are assigned to the page allocated. But we use this for
per-task monitoring. Whenever we start monitoring a task, the event is
added is onto the task. At that point, we read the initial value of the
event. Whenever, we stop monitoring the task, the final value is taken
and the difference is the event data.
Now, a task can move to a different cpu. Suppose a task X is moving from
cpu A to cpu B. When the task is scheduled out of A, we get an
event_del for A, and hence, the event data is updated. And, we stop
updating the X's event data. As soon as X moves on to B, event_add is
called for B, and we again update the event_data. And this is how it
keeps on updating the event data even when the task is scheduled on to
different cpus.
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/imc-pmu.h | 5 +
arch/powerpc/perf/imc-pmu.c | 209 +++++++++++++++++++++++++++++-
arch/powerpc/platforms/powernv/opal-imc.c | 3 +
3 files changed, 216 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h
index 6260e61..cc04712 100644
--- a/arch/powerpc/include/asm/imc-pmu.h
+++ b/arch/powerpc/include/asm/imc-pmu.h
@@ -42,6 +42,7 @@
* IMC Core engine expects 8K bytes of memory for counter collection.
*/
#define IMC_CORE_COUNTER_MEM 8192
+#define IMC_THREAD_COUNTER_MEM 8192
/*
*Compatbility macros for IMC devices
@@ -51,6 +52,9 @@
#define IMC_DTB_CORE_COMPAT "ibm,imc-counters-core"
#define IMC_DTB_THREAD_COMPAT "ibm,imc-counters-thread"
+#define THREAD_IMC_LDBAR_MASK 0x0003ffffffffe000
+#define THREAD_IMC_ENABLE 0x8000000000000000
+
/*
* Structure to hold per chip specific memory address
* information for nest pmus. Nest Counter data are exported
@@ -110,4 +114,5 @@ extern struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
extern struct imc_pmu *core_imc_pmu;
extern int __init init_imc_pmu(struct imc_events *events,int idx, struct imc_pmu *pmu_ptr);
void core_imc_disable(void);
+void thread_imc_disable(void);
#endif /* PPC_POWERNV_IMC_PMU_DEF_H */
diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
index 9767714..cfd112e 100644
--- a/arch/powerpc/perf/imc-pmu.c
+++ b/arch/powerpc/perf/imc-pmu.c
@@ -38,6 +38,9 @@ static u64 per_core_pdbar_add[IMC_MAX_CHIPS][IMC_MAX_CORES];
static cpumask_t core_imc_cpumask;
struct imc_pmu *core_imc_pmu;
+/* Maintains base address for all the cpus */
+static u64 per_cpu_add[NR_CPUS];
+
/* Needed for sanity check */
extern u64 nest_max_offset;
extern u64 core_max_offset;
@@ -480,6 +483,56 @@ static int core_imc_event_init(struct perf_event *event)
return 0;
}
+static int thread_imc_event_init(struct perf_event *event)
+{
+ struct task_struct *target;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ /* Sampling not supported */
+ if (event->hw.sample_period)
+ return -EINVAL;
+
+ event->hw.idx = -1;
+
+ /* Sanity check for config (event offset) */
+ if (event->attr.config > thread_max_offset)
+ return -EINVAL;
+
+ target = event->hw.target;
+
+ if (!target)
+ return -EINVAL;
+
+ event->pmu->task_ctx_nr = perf_sw_context;
+ return 0;
+}
+
+static void thread_imc_read_counter(struct perf_event *event)
+{
+ u64 *addr, data;
+ int cpu_id = smp_processor_id();
+
+ addr = (u64 *)(per_cpu_add[cpu_id] + event->attr.config);
+ data = __be64_to_cpu(READ_ONCE(*addr));
+ local64_set(&event->hw.prev_count, data);
+}
+
+static void thread_imc_perf_event_update(struct perf_event *event)
+{
+ u64 counter_prev, counter_new, final_count, *addr;
+ int cpu_id = smp_processor_id();
+
+ addr = (u64 *)(per_cpu_add[cpu_id] + event->attr.config);
+ counter_prev = local64_read(&event->hw.prev_count);
+ counter_new = __be64_to_cpu(READ_ONCE(*addr));
+ final_count = counter_new - counter_prev;
+
+ local64_set(&event->hw.prev_count, counter_new);
+ local64_add(final_count, &event->count);
+}
+
static void imc_read_counter(struct perf_event *event)
{
u64 *addr, data;
@@ -720,6 +773,84 @@ static int core_imc_event_add(struct perf_event *event, int flags)
}
+static void thread_imc_event_start(struct perf_event *event, int flags)
+{
+ int rc;
+
+ /*
+ * Core pmu units are enabled only when it is used.
+ * See if this is triggered for the first time.
+ * If yes, take the mutex lock and enable the core counters.
+ * If not, just increment the count in core_events.
+ */
+ if (atomic_inc_return(&core_events) == 1) {
+ mutex_lock(&imc_core_reserve);
+ rc = core_imc_control(IMC_COUNTER_ENABLE);
+ mutex_unlock(&imc_core_reserve);
+ if (rc)
+ pr_err("IMC: Unbale to start the counters\n");
+ }
+ thread_imc_read_counter(event);
+}
+
+static void thread_imc_event_stop(struct perf_event *event, int flags)
+{
+ int rc;
+
+ thread_imc_perf_event_update(event);
+ /*
+ * See if we need to disable the IMC PMU.
+ * If no events are currently in use, then we have to take a
+ * mutex to ensure that we don't race with another task doing
+ * enable or disable the core counters.
+ */
+ if (atomic_dec_return(&core_events) == 0) {
+ mutex_lock(&imc_core_reserve);
+ rc = core_imc_control(IMC_COUNTER_DISABLE);
+ mutex_unlock(&imc_core_reserve);
+ if (rc)
+ pr_err("IMC: Disable counters failed\n");
+
+ }
+}
+
+static void thread_imc_event_del(struct perf_event *event, int flags)
+{
+ thread_imc_perf_event_update(event);
+}
+
+static int thread_imc_event_add(struct perf_event *event, int flags)
+{
+ thread_imc_event_start(event, flags);
+
+ return 0;
+}
+
+static void thread_imc_pmu_start_txn(struct pmu *pmu,
+ unsigned int txn_flags)
+{
+ if (txn_flags & ~PERF_PMU_TXN_ADD)
+ return;
+ perf_pmu_disable(pmu);
+}
+
+static void thread_imc_pmu_cancel_txn(struct pmu *pmu)
+{
+ perf_pmu_enable(pmu);
+}
+
+static int thread_imc_pmu_commit_txn(struct pmu *pmu)
+{
+ perf_pmu_enable(pmu);
+ return 0;
+}
+
+static void thread_imc_pmu_sched_task(struct perf_event_context *ctx,
+ bool sched_in)
+{
+ return;
+}
+
/* update_pmu_ops : Populate the appropriate operations for "pmu" */
static int update_pmu_ops(struct imc_pmu *pmu)
{
@@ -745,7 +876,26 @@ static int update_pmu_ops(struct imc_pmu *pmu)
pmu->pmu.read = imc_perf_event_update;
pmu->attr_groups[IMC_FORMAT_ATTR] = &imc_format_group;
pmu->pmu.attr_groups = pmu->attr_groups;
-
+ if (pmu->domain == IMC_DOMAIN_THREAD) {
+ pmu->pmu.event_init = thread_imc_event_init;
+ pmu->pmu.start = thread_imc_event_start;
+ pmu->pmu.add = thread_imc_event_add;
+ pmu->pmu.del = thread_imc_event_del;
+ pmu->pmu.stop = thread_imc_event_stop;
+ pmu->pmu.read = thread_imc_perf_event_update;
+ pmu->pmu.start_txn = thread_imc_pmu_start_txn;
+ pmu->pmu.cancel_txn = thread_imc_pmu_cancel_txn;
+ pmu->pmu.commit_txn = thread_imc_pmu_commit_txn;
+ pmu->pmu.sched_task = thread_imc_pmu_sched_task;
+
+ /*
+ * Since thread_imc does not have any CPUMASK attr,
+ * this may drop the "events" attr all together.
+ * So swap the IMC_EVENT_ATTR slot with IMC_CPUMASK_ATTR.
+ */
+ pmu->attr_groups[IMC_CPUMASK_ATTR] = pmu->attr_groups[IMC_EVENT_ATTR];
+ pmu->attr_groups[IMC_EVENT_ATTR] = NULL;
+ }
return 0;
}
@@ -806,6 +956,56 @@ static int update_events_in_group(struct imc_events *events,
return 0;
}
+static void thread_imc_ldbar_disable(void *dummy)
+{
+ /* LDBAR spr is a per-thread */
+ mtspr(SPRN_LDBAR, 0);
+}
+
+void thread_imc_disable(void)
+{
+ on_each_cpu(thread_imc_ldbar_disable, NULL, 1);
+}
+
+static void cleanup_thread_imc_memory(void *dummy)
+{
+ int cpu_id = smp_processor_id();
+ u64 addr = per_cpu_add[cpu_id];
+
+ /* Only if the address is non-zero, shall we free it */
+ if (addr)
+ free_pages(addr, 0);
+}
+
+static void cleanup_all_thread_imc_memory(void)
+{
+ on_each_cpu(cleanup_thread_imc_memory, NULL, 1);
+}
+
+/*
+ * Allocates a page of memory for each of the online cpus, and, writes the
+ * physical base address of that page to the LDBAR for that cpu. This starts
+ * the thread IMC counters.
+ */
+static void thread_imc_mem_alloc(void *dummy)
+{
+ u64 ldbar_addr, ldbar_value;
+ int cpu_id = smp_processor_id();
+ int phys_id = topology_physical_package_id(smp_processor_id());
+
+ per_cpu_add[cpu_id] = (u64)alloc_pages_exact_nid(phys_id,
+ (size_t)IMC_THREAD_COUNTER_MEM, GFP_KERNEL | __GFP_ZERO);
+ ldbar_addr = (u64)virt_to_phys((void *)per_cpu_add[cpu_id]);
+ ldbar_value = (ldbar_addr & (u64)THREAD_IMC_LDBAR_MASK) |
+ (u64)THREAD_IMC_ENABLE;
+ mtspr(SPRN_LDBAR, ldbar_value);
+}
+
+void thread_imc_cpu_init(void)
+{
+ on_each_cpu(thread_imc_mem_alloc, NULL, 1);
+}
+
/*
* init_imc_pmu : Setup and register the IMC pmu device.
*
@@ -833,6 +1033,9 @@ int __init init_imc_pmu(struct imc_events *events, int idx,
if (ret)
return ret;
break;
+ case IMC_DOMAIN_THREAD:
+ thread_imc_cpu_init();
+ break;
default:
return -1; /* Unknown domain */
}
@@ -865,5 +1068,9 @@ int __init init_imc_pmu(struct imc_events *events, int idx,
if (pmu_ptr->domain == IMC_DOMAIN_CORE)
cleanup_all_core_imc_memory();
+ /* For thread_imc, we have allocated memory, we need to free it */
+ if (pmu_ptr->domain == IMC_DOMAIN_THREAD)
+ cleanup_all_thread_imc_memory();
+
return ret;
}
diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
index 940f6b9..e36722b 100644
--- a/arch/powerpc/platforms/powernv/opal-imc.c
+++ b/arch/powerpc/platforms/powernv/opal-imc.c
@@ -582,6 +582,9 @@ static void opal_imc_counters_shutdown(struct platform_device *pdev)
{
/* Disable the IMC Core functions */
core_imc_disable();
+
+ /* Disable the IMC Thread functions */
+ thread_imc_disable();
}
static const struct of_device_id opal_imc_match[] = {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Anju T Sudhakar <anju@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-04 16:30 +0200 |
| Subject | [PATCH v8 04/10] powerpc/perf: Add generic IMC pmu groupand event functions |
| Message-ID | <tDpVw-4CY-27@gated-at.bofh.it> |
| In reply to | #1635778 |
From: Hemant Kumar <hemant@linux.vnet.ibm.com>
Device tree IMC driver code parses the IMC units and their events. It
passes the information to IMC pmu code which is placed in powerpc/perf
as "imc-pmu.c".
Patch adds a set of generic imc pmu related event functions to be
used by each imc pmu unit. Add code to setup format attribute and to
register imc pmus. Add a event_init function for nest_imc events.
Since, the IMC counters' data are periodically fed to a memory location,
the functions to read/update, start/stop, add/del can be generic and can
be used by all IMC PMU units.
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/imc-pmu.h | 3 +
arch/powerpc/perf/Makefile | 3 +
arch/powerpc/perf/imc-pmu.c | 269 ++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/opal-imc.c | 10 +-
4 files changed, 283 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/perf/imc-pmu.c
diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h
index d0193c8..6bbe184 100644
--- a/arch/powerpc/include/asm/imc-pmu.h
+++ b/arch/powerpc/include/asm/imc-pmu.h
@@ -92,4 +92,7 @@ struct imc_pmu {
#define IMC_DOMAIN_NEST 1
#define IMC_DOMAIN_UNKNOWN -1
+extern struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
+extern struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
+extern int __init init_imc_pmu(struct imc_events *events,int idx, struct imc_pmu *pmu_ptr);
#endif /* PPC_POWERNV_IMC_PMU_DEF_H */
diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile
index 4d606b9..b29d918 100644
--- a/arch/powerpc/perf/Makefile
+++ b/arch/powerpc/perf/Makefile
@@ -6,6 +6,9 @@ obj-$(CONFIG_PPC_PERF_CTRS) += core-book3s.o bhrb.o
obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \
power5+-pmu.o power6-pmu.o power7-pmu.o \
isa207-common.o power8-pmu.o power9-pmu.o
+
+obj-$(CONFIG_HV_PERF_IMC_CTRS) += imc-pmu.o
+
obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o
obj-$(CONFIG_FSL_EMB_PERF_EVENT) += core-fsl-emb.o
diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
new file mode 100644
index 0000000..f09a37a
--- /dev/null
+++ b/arch/powerpc/perf/imc-pmu.c
@@ -0,0 +1,269 @@
+/*
+ * Nest Performance Monitor counter support.
+ *
+ * Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
+ * (C) 2017 Anju T Sudhakar, IBM Corporation.
+ * (C) 2017 Hemant K Shaw, IBM Corporation.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+#include <asm/opal.h>
+#include <asm/imc-pmu.h>
+#include <asm/cputhreads.h>
+#include <asm/smp.h>
+#include <linux/string.h>
+
+struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
+struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
+
+/* Needed for sanity check */
+extern u64 nest_max_offset;
+
+PMU_FORMAT_ATTR(event, "config:0-20");
+static struct attribute *imc_format_attrs[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group imc_format_group = {
+ .name = "format",
+ .attrs = imc_format_attrs,
+};
+
+static int nest_imc_event_init(struct perf_event *event)
+{
+ int chip_id;
+ u32 config = event->attr.config;
+ struct perchip_nest_info *pcni;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ /* Sampling not supported */
+ if (event->hw.sample_period)
+ return -EINVAL;
+
+ /* unsupported modes and filters */
+ if (event->attr.exclude_user ||
+ event->attr.exclude_kernel ||
+ event->attr.exclude_hv ||
+ event->attr.exclude_idle ||
+ event->attr.exclude_host ||
+ event->attr.exclude_guest)
+ return -EINVAL;
+
+ if (event->cpu < 0)
+ return -EINVAL;
+
+ /* Sanity check for config (event offset) */
+ if (config > nest_max_offset)
+ return -EINVAL;
+
+ chip_id = topology_physical_package_id(event->cpu);
+ pcni = &nest_perchip_info[chip_id];
+
+ /*
+ * Memory for Nest HW counter data could be in multiple pages.
+ * Hence check and pick the right event base page for chip with
+ * "chip_id" and add "config" to it".
+ */
+ event->hw.event_base = pcni->vbase[config/PAGE_SIZE] +
+ (config & ~PAGE_MASK);
+
+ return 0;
+}
+
+static void imc_read_counter(struct perf_event *event)
+{
+ u64 *addr, data;
+
+ /*
+ * In-Memory Collection (IMC) counters are free flowing counters.
+ * So we take a snapshot of the counter value on enable and save it
+ * to calculate the delta at later stage to present the event counter
+ * value.
+ */
+ addr = (u64 *)event->hw.event_base;
+ data = __be64_to_cpu(READ_ONCE(*addr));
+ local64_set(&event->hw.prev_count, data);
+}
+
+static void imc_perf_event_update(struct perf_event *event)
+{
+ u64 counter_prev, counter_new, final_count, *addr;
+
+ addr = (u64 *)event->hw.event_base;
+ counter_prev = local64_read(&event->hw.prev_count);
+ counter_new = __be64_to_cpu(READ_ONCE(*addr));
+ final_count = counter_new - counter_prev;
+
+ /*
+ * Need to update prev_count is that, counter could be
+ * read in a periodic interval from the tool side.
+ */
+ local64_set(&event->hw.prev_count, counter_new);
+ /* Update the delta to the event count */
+ local64_add(final_count, &event->count);
+}
+
+static void imc_event_start(struct perf_event *event, int flags)
+{
+ /*
+ * In Memory Counters are free flowing counters. HW or the microcode
+ * keeps adding to the counter offset in memory. To get event
+ * counter value, we snapshot the value here and we calculate
+ * delta at later point.
+ */
+ imc_read_counter(event);
+}
+
+static void imc_event_stop(struct perf_event *event, int flags)
+{
+ /*
+ * Take a snapshot and calculate the delta and update
+ * the event counter values.
+ */
+ imc_perf_event_update(event);
+}
+
+/*
+ * The wrapper function is provided here, since we will have reserve
+ * and release lock for imc_event_start() in the following patch.
+ * Same in case of imc_event_stop().
+ */
+static void nest_imc_event_start(struct perf_event *event, int flags)
+{
+ imc_event_start(event, flags);
+}
+
+static void nest_imc_event_stop(struct perf_event *event, int flags)
+{
+ imc_event_stop(event, flags);
+}
+
+static int nest_imc_event_add(struct perf_event *event, int flags)
+{
+ if (flags & PERF_EF_START)
+ nest_imc_event_start(event, flags);
+
+ return 0;
+}
+
+/* update_pmu_ops : Populate the appropriate operations for "pmu" */
+static int update_pmu_ops(struct imc_pmu *pmu)
+{
+ if (!pmu)
+ return -EINVAL;
+
+ pmu->pmu.task_ctx_nr = perf_invalid_context;
+ pmu->pmu.event_init = nest_imc_event_init;
+ pmu->pmu.add = nest_imc_event_add;
+ pmu->pmu.del = nest_imc_event_stop;
+ pmu->pmu.start = nest_imc_event_start;
+ pmu->pmu.stop = nest_imc_event_stop;
+ pmu->pmu.read = imc_perf_event_update;
+ pmu->attr_groups[IMC_FORMAT_ATTR] = &imc_format_group;
+ pmu->pmu.attr_groups = pmu->attr_groups;
+
+ return 0;
+}
+
+/* dev_str_attr : Populate event "name" and string "str" in attribute */
+static struct attribute *dev_str_attr(const char *name, const char *str)
+{
+ struct perf_pmu_events_attr *attr;
+
+ attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ if (!attr)
+ return NULL;
+ sysfs_attr_init(&attr->attr.attr);
+
+ attr->event_str = str;
+ attr->attr.attr.name = name;
+ attr->attr.attr.mode = 0444;
+ attr->attr.show = perf_event_sysfs_show;
+
+ return &attr->attr.attr;
+}
+
+/*
+ * update_events_in_group: Update the "events" information in an attr_group
+ * and assign the attr_group to the pmu "pmu".
+ */
+static int update_events_in_group(struct imc_events *events,
+ int idx, struct imc_pmu *pmu)
+{
+ struct attribute_group *attr_group;
+ struct attribute **attrs;
+ int i;
+
+ /* If there is no events for this pmu, just return zero */
+ if (!events)
+ return 0;
+
+ /* Allocate memory for attribute group */
+ attr_group = kzalloc(sizeof(*attr_group), GFP_KERNEL);
+ if (!attr_group)
+ return -ENOMEM;
+
+ /* Allocate memory for attributes */
+ attrs = kzalloc((sizeof(struct attribute *) * (idx + 1)), GFP_KERNEL);
+ if (!attrs) {
+ kfree(attr_group);
+ return -ENOMEM;
+ }
+
+ attr_group->name = "events";
+ attr_group->attrs = attrs;
+ for (i = 0; i < idx; i++, events++) {
+ attrs[i] = dev_str_attr((char *)events->ev_name,
+ (char *)events->ev_value);
+ }
+
+ /* Save the event attribute */
+ pmu->attr_groups[IMC_EVENT_ATTR] = attr_group;
+ return 0;
+}
+
+/*
+ * init_imc_pmu : Setup and register the IMC pmu device.
+ *
+ * @events: events memory for this pmu.
+ * @idx: number of event entries created.
+ * @pmu_ptr: memory allocated for this pmu.
+ */
+int __init init_imc_pmu(struct imc_events *events, int idx,
+ struct imc_pmu *pmu_ptr)
+{
+ int ret = -ENODEV;
+
+ ret = update_events_in_group(events, idx, pmu_ptr);
+ if (ret)
+ goto err_free;
+
+ ret = update_pmu_ops(pmu_ptr);
+ if (ret)
+ goto err_free;
+
+ ret = perf_pmu_register(&pmu_ptr->pmu, pmu_ptr->pmu.name, -1);
+ if (ret)
+ goto err_free;
+
+ pr_info("%s performance monitor hardware support registered\n",
+ pmu_ptr->pmu.name);
+
+ return 0;
+
+err_free:
+ /* Only free the attr_groups which are dynamically allocated */
+ if (pmu_ptr->attr_groups[IMC_EVENT_ATTR]) {
+ if (pmu_ptr->attr_groups[IMC_EVENT_ATTR]->attrs)
+ kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]->attrs);
+ kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]);
+ }
+
+ return ret;
+}
diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
index 0ddaf7d..61f6d67 100644
--- a/arch/powerpc/platforms/powernv/opal-imc.c
+++ b/arch/powerpc/platforms/powernv/opal-imc.c
@@ -34,8 +34,6 @@
#include <asm/imc-pmu.h>
u64 nest_max_offset;
-struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
-struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
static int imc_event_prop_update(char *name, struct imc_events *events)
{
@@ -423,8 +421,16 @@ static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
if (prop)
events = imc_events_setup(parent, pmu_index, pmu_ptr,
prop, &idx);
+ /* Function to register IMC pmu */
+ ret = init_imc_pmu(events, idx, pmu_ptr);
+ if (ret) {
+ pr_err("IMC PMU %s Register failed\n", pmu_ptr->pmu.name);
+ goto free_events;
+ }
return 0;
+free_events:
+ imc_free_events(events, idx);
free_pmu:
kfree(pmu_ptr);
return ret;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Anju T Sudhakar <anju@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-04 16:30 +0200 |
| Subject | [PATCH v8 03/10] powerpc/powernv: Detect supported IMC units and its events |
| Message-ID | <tDpVx-4CY-33@gated-at.bofh.it> |
| In reply to | #1635778 |
Parse device tree to detect IMC units. Traverse through each IMC unit
node to find supported events and corresponding unit/scale files (if any).
Here is the DTS file for reference:
https://github.com/open-power/ima-catalog/blob/master/81E00612.4E0100.dts
The device tree for IMC counters starts at the node "imc-counters".
This node contains all the IMC PMU nodes and event nodes
for these IMC PMUs. The PMU nodes have an "events" property which has a
phandle value for the actual events node. The events are separated from
the PMU nodes to abstract out the common events. For example, PMU node
"mcs0", "mcs1" etc. will contain a pointer to "nest-mcs-events" since,
the events are common between these PMUs. These events have a different
prefix based on their relation to different PMUs, and hence, the PMU
nodes themselves contain an "events-prefix" property. The value for this
property concatenated to the event name, forms the actual event
name. Also, the PMU have a "reg" field as the base offset for the events
which belong to this PMU. This "reg" field is added to event's "reg" field
in the "events" node, which gives us the location of the counter data. Kernel
code uses this offset as event configuration value.
Device tree parser code also looks for scale/unit property in the event
node and passes on the value as an event attr for perf interface to use
in the post processing by the perf tool. Some PMUs may have common scale
and unit properties which implies that all events supported by this PMU
inherit the scale and unit properties of the PMU itself. For those
events, we need to set the common unit and scale values.
For failure to initialize any unit or any event, disable that unit and
continue setting up the rest of them.
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/opal-imc.c | 413 ++++++++++++++++++++++++++++++
1 file changed, 413 insertions(+)
diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
index 3a87000..0ddaf7d 100644
--- a/arch/powerpc/platforms/powernv/opal-imc.c
+++ b/arch/powerpc/platforms/powernv/opal-imc.c
@@ -33,15 +33,428 @@
#include <asm/cputable.h>
#include <asm/imc-pmu.h>
+u64 nest_max_offset;
struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
+struct imc_pmu *per_nest_pmu_arr[IMC_MAX_PMUS];
+
+static int imc_event_prop_update(char *name, struct imc_events *events)
+{
+ char *buf;
+
+ if (!events || !name)
+ return -EINVAL;
+
+ /* memory for content */
+ buf = kzalloc(IMC_MAX_NAME_VAL_LEN, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ events->ev_name = name;
+ events->ev_value = buf;
+ return 0;
+}
+
+static int imc_event_prop_str(struct property *pp, char *name,
+ struct imc_events *events)
+{
+ int ret;
+
+ ret = imc_event_prop_update(name, events);
+ if (ret)
+ return ret;
+
+ if (!pp->value || (strnlen(pp->value, pp->length) == pp->length) ||
+ (pp->length > IMC_MAX_NAME_VAL_LEN))
+ return -EINVAL;
+ strncpy(events->ev_value, (const char *)pp->value, pp->length);
+
+ return 0;
+}
+
+static int imc_event_prop_val(char *name, u32 val,
+ struct imc_events *events)
+{
+ int ret;
+
+ ret = imc_event_prop_update(name, events);
+ if (ret)
+ return ret;
+ snprintf(events->ev_value, IMC_MAX_NAME_VAL_LEN, "event=0x%x", val);
+
+ return 0;
+}
+
+static int set_event_property(struct property *pp, char *event_prop,
+ struct imc_events *events, char *ev_name)
+{
+ char *buf;
+ int ret;
+
+ buf = kzalloc(IMC_MAX_NAME_VAL_LEN, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ sprintf(buf, "%s.%s", ev_name, event_prop);
+ ret = imc_event_prop_str(pp, buf, events);
+ if (ret) {
+ if (events->ev_name)
+ kfree(events->ev_name);
+ if (events->ev_value)
+ kfree(events->ev_value);
+ }
+ return ret;
+}
+
+/*
+ * Updates the maximum offset for an event in the pmu with domain
+ * "pmu_domain".
+ */
+static void update_max_value(u32 value, int pmu_domain)
+{
+ switch (pmu_domain) {
+ case IMC_DOMAIN_NEST:
+ if (nest_max_offset < value)
+ nest_max_offset = value;
+ break;
+ default:
+ /* Unknown domain, return */
+ return;
+ }
+}
+
+/*
+ * imc_events_node_parser: Parse the event node "dev" and assign the parsed
+ * information to event "events".
+ *
+ * Parses the "reg", "scale" and "unit" properties of this event.
+ * "reg" gives us the event offset in the counter memory.
+ */
+static int imc_events_node_parser(struct device_node *dev,
+ struct imc_events *events,
+ struct property *event_scale,
+ struct property *event_unit,
+ struct property *name_prefix,
+ u32 reg, int pmu_domain)
+{
+ struct property *name, *pp;
+ char *ev_name;
+ u32 val;
+ int idx = 0, ret;
+
+ if (!dev)
+ goto fail;
+
+ /* Find the event name */
+ name = of_find_property(dev, "event-name", NULL);
+ if (!name)
+ return -ENODEV;
+
+ if (!name->value ||
+ (strnlen(name->value, name->length) == name->length) ||
+ (name->length > IMC_MAX_NAME_VAL_LEN))
+ return -EINVAL;
+
+ ev_name = kzalloc(IMC_MAX_NAME_VAL_LEN, GFP_KERNEL);
+ if (!ev_name)
+ return -ENOMEM;
+
+ snprintf(ev_name, IMC_MAX_NAME_VAL_LEN, "%s%s",
+ (char *)name_prefix->value,
+ (char *)name->value);
+
+ /*
+ * Parse each property of this event node "dev". Property "reg" has
+ * the offset which is assigned to the event name. Other properties
+ * like "scale" and "unit" are assigned to event.scale and event.unit
+ * accordingly.
+ */
+ for_each_property_of_node(dev, pp) {
+ /*
+ * If there is an issue in parsing a single property of
+ * this event, we just clean up the buffers, but we still
+ * continue to parse. XXX: This could be rewritten to skip the
+ * entire event node incase of parsing issues, but that can be
+ * done later.
+ */
+ if (strncmp(pp->name, "reg", 3) == 0) {
+ of_property_read_u32(dev, pp->name, &val);
+ val += reg;
+ update_max_value(val, pmu_domain);
+ ret = imc_event_prop_val(ev_name, val, &events[idx]);
+ if (ret) {
+ if (events[idx].ev_name)
+ kfree(events[idx].ev_name);
+ if (events[idx].ev_value)
+ kfree(events[idx].ev_value);
+ goto fail;
+ }
+ idx++;
+ /*
+ * If the common scale and unit properties available,
+ * then, assign them to this event
+ */
+ if (event_scale) {
+ ret = set_event_property(event_scale, "scale",
+ &events[idx],
+ ev_name);
+ if (ret)
+ goto fail;
+ idx++;
+ }
+ if (event_unit) {
+ ret = set_event_property(event_unit, "unit",
+ &events[idx],
+ ev_name);
+ if (ret)
+ goto fail;
+ idx++;
+ }
+ } else if (strncmp(pp->name, "unit", 4) == 0) {
+ /*
+ * The event's unit and scale properties can override the
+ * PMU's event and scale properties, if present.
+ */
+ ret = set_event_property(pp, "unit", &events[idx],
+ ev_name);
+ if (ret)
+ goto fail;
+ idx++;
+ } else if (strncmp(pp->name, "scale", 5) == 0) {
+ ret = set_event_property(pp, "scale", &events[idx],
+ ev_name);
+ if (ret)
+ goto fail;
+ idx++;
+ }
+ }
+
+ return idx;
+fail:
+ return -EINVAL;
+}
+
+/*
+ * get_nr_children : Returns the number of children for a pmu device node.
+ */
+static int get_nr_children(struct device_node *pmu_node)
+{
+ struct device_node *child;
+ int i = 0;
+
+ for_each_child_of_node(pmu_node, child)
+ i++;
+ return i;
+}
+
+/*
+ * imc_free_events : Cleanup the "events" list having "nr_entries" entries.
+ */
+static void imc_free_events(struct imc_events *events, int nr_entries)
+{
+ int i;
+
+ /* Nothing to clean, return */
+ if (!events)
+ return;
+
+ for (i = 0; i < nr_entries; i++) {
+ if (events[i].ev_name)
+ kfree(events[i].ev_name);
+ if (events[i].ev_value)
+ kfree(events[i].ev_value);
+ }
+
+ kfree(events);
+}
+
+/*
+ * imc_events_setup() : First finds the event node for the pmu and
+ * gets the number of supported events and then
+ * allocates memory for the same. Finally returns the address of events
+ * memory allocated.
+ */
+static struct imc_events *imc_events_setup(struct device_node *parent,
+ int pmu_index,
+ struct imc_pmu *pmu_ptr,
+ u32 prop,
+ int *idx)
+{
+ struct device_node *ev_node = NULL, *dir = NULL;
+ u32 reg;
+ struct imc_events *events;
+ struct property *scale_pp, *unit_pp, *name_prefix;
+ int ret = 0, nr_children = 0;
+
+ /*
+ * Fetch the actual node where the events for this PMU exist.
+ */
+ dir = of_find_node_by_phandle(prop);
+ if (!dir)
+ return NULL;
+ /*
+ * Get the maximum no. of events in this node.
+ * Multiply by 3 to account for .scale and .unit properties
+ * This number suggests the amount of memory needed to setup the
+ * events for this pmu.
+ */
+ nr_children = get_nr_children(dir) * 3;
+
+ events = kzalloc((sizeof(struct imc_events) * nr_children),
+ GFP_KERNEL);
+ if (!events)
+ return NULL;
+
+ /*
+ * Check if there is a common "scale" and "unit" properties inside
+ * the PMU node for all the events supported by this PMU.
+ */
+ scale_pp = of_find_property(parent, "scale", NULL);
+ unit_pp = of_find_property(parent, "unit", NULL);
+
+ /*
+ * Get the event-prefix property from the PMU node
+ * which needs to be attached with the event names.
+ */
+ name_prefix = of_find_property(parent, "events-prefix", NULL);
+ if (!name_prefix)
+ goto free_events;
+
+ /*
+ * "reg" property gives out the base offset of the counters data
+ * for this PMU.
+ */
+ of_property_read_u32(parent, "reg", ®);
+
+ if (!name_prefix->value ||
+ (strnlen(name_prefix->value, name_prefix->length) == name_prefix->length) ||
+ (name_prefix->length > IMC_MAX_NAME_VAL_LEN))
+ goto free_events;
+
+ /* Loop through event nodes */
+ for_each_child_of_node(dir, ev_node) {
+ ret = imc_events_node_parser(ev_node, &events[*idx], scale_pp,
+ unit_pp, name_prefix, reg, pmu_ptr->domain);
+ if (ret < 0) {
+ /* Unable to parse this event */
+ if (ret == -ENOMEM)
+ goto free_events;
+ continue;
+ }
+
+ /*
+ * imc_event_node_parser will return number of
+ * event entries created for this. This could include
+ * event scale and unit files also.
+ */
+ *idx += ret;
+ }
+ return events;
+
+free_events:
+ imc_free_events(events, *idx);
+ return NULL;
+
+}
+
+/*
+ * imc_pmu_create : Takes the parent device which is the pmu unit and a
+ * pmu_index as the inputs.
+ * Allocates memory for the pmu, sets up its domain (NEST), and
+ * calls imc_events_setup() to allocate memory for the events supported
+ * by this pmu. Assigns a name for the pmu. Calls imc_events_node_parser()
+ * to setup the individual events.
+ * If everything goes fine, it calls, init_imc_pmu() to setup the pmu device
+ * and register it.
+ */
+static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
+{
+ struct imc_events *events = NULL;
+ struct imc_pmu *pmu_ptr;
+ u32 prop = 0;
+ struct property *pp;
+ char *buf;
+ int idx = 0, ret = 0;
+
+ if (!parent)
+ return -EINVAL;
+
+ /* memory for pmu */
+ pmu_ptr = kzalloc(sizeof(struct imc_pmu), GFP_KERNEL);
+ if (!pmu_ptr)
+ return -ENOMEM;
+
+ pmu_ptr->domain = domain;
+ if (pmu_ptr->domain == IMC_DOMAIN_UNKNOWN)
+ goto free_pmu;
+
+ /* Needed for hotplug/migration */
+ per_nest_pmu_arr[pmu_index] = pmu_ptr;
+
+ pp = of_find_property(parent, "name", NULL);
+ if (!pp) {
+ ret = -ENODEV;
+ goto free_pmu;
+ }
+
+ if (!pp->value ||
+ (strnlen(pp->value, pp->length) == pp->length) ||
+ (pp->length > IMC_MAX_NAME_VAL_LEN)) {
+ ret = -EINVAL;
+ goto free_pmu;
+ }
+
+ buf = kzalloc(IMC_MAX_NAME_VAL_LEN, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto free_pmu;
+ }
+ /* Save the name to register it later */
+ sprintf(buf, "nest_%s", (char *)pp->value);
+ pmu_ptr->pmu.name = (char *)buf;
+
+ /*
+ * "events" property inside a PMU node contains the phandle value
+ * for the actual events node. The "events" node for the IMC PMU
+ * is not in this node, rather inside "imc-counters" node, since,
+ * we want to factor out the common events (thereby, reducing the
+ * size of the device tree)
+ */
+ of_property_read_u32(parent, "events", &prop);
+ if (prop)
+ events = imc_events_setup(parent, pmu_index, pmu_ptr,
+ prop, &idx);
+ return 0;
+
+free_pmu:
+ kfree(pmu_ptr);
+ return ret;
+}
+
/*
* imc_pmu_setup : Setup the IMC PMUs (children of "parent").
+ *
+ * Top level "imc-counters" node contains both event-nodes and pmu
+ * unit nodes. We only consider the pmu unit node here.
*/
static void __init imc_pmu_setup(struct device_node *parent)
{
+ struct device_node *child;
+ int pmu_count = 0, rc = 0, domain;
+
if (!parent)
return;
+ /*
+ * Loop through the imc-counters tree for each compatible
+ * "ibm,imc-counters-nest", and update "struct imc_pmu".
+ */
+ for_each_compatible_node(child, NULL, IMC_DTB_NEST_COMPAT) {
+ domain = IMC_DOMAIN_NEST;
+ rc = imc_pmu_create(child, pmu_count, domain);
+ if (rc)
+ return;
+ pmu_count++;
+ }
}
static int opal_imc_counters_probe(struct platform_device *pdev)
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Anju T Sudhakar <anju@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-04 16:30 +0200 |
| Subject | [PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module |
| Message-ID | <tDpVx-4CY-39@gated-at.bofh.it> |
| In reply to | #1635778 |
This patch does three things :
- Enables "opal.c" to create a platform device for the IMC interface
according to the appropriate compatibility string.
- Find the reserved-memory region details from the system device tree
and get the base address of HOMER (Reserved memory) region address for each chip.
- We also get the Nest PMU counter data offsets (in the HOMER region)
and their sizes. The offsets for the counters' data are fixed and
won't change from chip to chip.
The device tree parsing logic is separated from the PMU creation
functions (which is done in subsequent patches).
Patch also adds a CONFIG_HV_PERF_IMC_CTRS for the IMC driver.
Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/Kconfig | 10 +++
arch/powerpc/platforms/powernv/Makefile | 1 +
arch/powerpc/platforms/powernv/opal-imc.c | 140 ++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/opal.c | 18 ++++
4 files changed, 169 insertions(+)
create mode 100644 arch/powerpc/platforms/powernv/opal-imc.c
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index 3a07e4d..1b90a98 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -27,3 +27,13 @@ config OPAL_PRD
help
This enables the opal-prd driver, a facility to run processor
recovery diagnostics on OpenPower machines
+
+config HV_PERF_IMC_CTRS
+ bool "Hypervisor supplied In Memory Collection PMU events (Nest & Core)"
+ default y
+ depends on PERF_EVENTS && PPC_POWERNV
+ help
+ Enable access to hypervisor supplied in-memory collection counters
+ in perf. IMC counters are available from Power9 systems.
+
+ If unsure, select Y.
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index b5d98cb..715e531 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
obj-$(CONFIG_OPAL_PRD) += opal-prd.o
+obj-$(CONFIG_HV_PERF_IMC_CTRS) += opal-imc.o
diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
new file mode 100644
index 0000000..3a87000
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-imc.c
@@ -0,0 +1,140 @@
+/*
+ * OPAL IMC interface detection driver
+ * Supported on POWERNV platform
+ *
+ * Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
+ * (C) 2017 Anju T Sudhakar, IBM Corporation.
+ * (C) 2017 Hemant K Shaw, IBM Corporation.
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/crash_dump.h>
+#include <asm/opal.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/cputable.h>
+#include <asm/imc-pmu.h>
+
+struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
+
+/*
+ * imc_pmu_setup : Setup the IMC PMUs (children of "parent").
+ */
+static void __init imc_pmu_setup(struct device_node *parent)
+{
+ if (!parent)
+ return;
+}
+
+static int opal_imc_counters_probe(struct platform_device *pdev)
+{
+ struct device_node *imc_dev, *dn, *rm_node = NULL;
+ struct perchip_nest_info *pcni;
+ u32 pages, nest_offset, nest_size, chip_id;
+ int i = 0;
+ const __be32 *addrp;
+ u64 reg_addr, reg_size;
+
+ if (!pdev || !pdev->dev.of_node)
+ return -ENODEV;
+
+ /*
+ * Check whether this is kdump kernel. If yes, just return.
+ */
+ if (is_kdump_kernel())
+ return -ENODEV;
+
+ imc_dev = pdev->dev.of_node;
+
+ /*
+ * Nest counter data are saved in a reserved memory called HOMER.
+ * "imc-nest-offset" identifies the counter data location within HOMER.
+ * size : size of the entire nest-counters region
+ */
+ if (of_property_read_u32(imc_dev, "imc-nest-offset", &nest_offset))
+ goto err;
+
+ if (of_property_read_u32(imc_dev, "imc-nest-size", &nest_size))
+ goto err;
+
+ /* Sanity check */
+ if ((nest_size/PAGE_SIZE) > IMC_NEST_MAX_PAGES)
+ goto err;
+
+ /* Find the "HOMER region" for each chip */
+ rm_node = of_find_node_by_path("/reserved-memory");
+ if (!rm_node)
+ goto err;
+
+ /*
+ * We need to look for the "ibm,homer-image" node in the
+ * "/reserved-memory" node.
+ */
+ for (dn = of_find_node_by_name(rm_node, "ibm,homer-image"); dn;
+ dn = of_find_node_by_name(dn, "ibm,homer-image")) {
+
+ /* Get the chip id to which the above homer region belongs to */
+ if (of_property_read_u32(dn, "ibm,chip-id", &chip_id))
+ goto err;
+
+ pcni = &nest_perchip_info[chip_id];
+ addrp = of_get_address(dn, 0, ®_size, NULL);
+ if (!addrp)
+ goto err;
+
+ /* Fetch the homer region base address */
+ reg_addr = of_read_number(addrp, 2);
+ pcni->pbase = reg_addr;
+ /* Add the nest IMC Base offset */
+ pcni->pbase = pcni->pbase + nest_offset;
+ /* Fetch the size of the homer region */
+ pcni->size = nest_size;
+
+ for (i = 0; i < (pcni->size / PAGE_SIZE); i++) {
+ pages = PAGE_SIZE * i;
+ pcni->vbase[i] = (u64)phys_to_virt(pcni->pbase + pages);
+ }
+ }
+
+ imc_pmu_setup(imc_dev);
+
+ return 0;
+err:
+ return -ENODEV;
+}
+
+static const struct of_device_id opal_imc_match[] = {
+ { .compatible = IMC_DTB_COMPAT },
+ {},
+};
+
+static struct platform_driver opal_imc_driver = {
+ .driver = {
+ .name = "opal-imc-counters",
+ .of_match_table = opal_imc_match,
+ },
+ .probe = opal_imc_counters_probe,
+};
+
+MODULE_DEVICE_TABLE(of, opal_imc_match);
+module_platform_driver(opal_imc_driver);
+MODULE_DESCRIPTION("PowerNV OPAL IMC driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index e0f856b..b06f896 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -14,6 +14,7 @@
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/interrupt.h>
@@ -30,6 +31,7 @@
#include <asm/opal.h>
#include <asm/firmware.h>
#include <asm/mce.h>
+#include <asm/imc-pmu.h>
#include "powernv.h"
@@ -631,6 +633,17 @@ static void opal_pdev_init(const char *compatible)
of_platform_device_create(np, NULL, NULL);
}
+#ifdef CONFIG_HV_PERF_IMC_CTRS
+static void __init opal_imc_init_dev(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, IMC_DTB_COMPAT);
+ if (np)
+ of_platform_device_create(np, NULL, NULL);
+}
+#endif
+
static int kopald(void *unused)
{
unsigned long timeout = msecs_to_jiffies(opal_heartbeat) + 1;
@@ -704,6 +717,11 @@ static int __init opal_init(void)
/* Setup a heatbeat thread if requested by OPAL */
opal_init_heartbeat();
+#ifdef CONFIG_HV_PERF_IMC_CTRS
+ /* Detect IMC pmu counters support and create PMUs */
+ opal_imc_init_dev();
+#endif
+
/* Create leds platform devices */
leds = of_find_node_by_path("/ibm,opal/leds");
if (leds) {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Stewart Smith <stewart@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-11 09:50 +0200 |
| Subject | Re: [PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module |
| Message-ID | <tFR1g-3sb-17@gated-at.bofh.it> |
| In reply to | #1635792 |
Anju T Sudhakar <anju@linux.vnet.ibm.com> writes:
> This patch does three things :
> - Enables "opal.c" to create a platform device for the IMC interface
> according to the appropriate compatibility string.
> - Find the reserved-memory region details from the system device tree
> and get the base address of HOMER (Reserved memory) region address for each chip.
> - We also get the Nest PMU counter data offsets (in the HOMER region)
> and their sizes. The offsets for the counters' data are fixed and
> won't change from chip to chip.
>
> The device tree parsing logic is separated from the PMU creation
> functions (which is done in subsequent patches).
>
> Patch also adds a CONFIG_HV_PERF_IMC_CTRS for the IMC driver.
>
> Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
> Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
> Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
> ---
> arch/powerpc/platforms/powernv/Kconfig | 10 +++
> arch/powerpc/platforms/powernv/Makefile | 1 +
> arch/powerpc/platforms/powernv/opal-imc.c | 140 ++++++++++++++++++++++++++++++
> arch/powerpc/platforms/powernv/opal.c | 18 ++++
> 4 files changed, 169 insertions(+)
> create mode 100644 arch/powerpc/platforms/powernv/opal-imc.c
>
> diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
> index 3a07e4d..1b90a98 100644
> --- a/arch/powerpc/platforms/powernv/Kconfig
> +++ b/arch/powerpc/platforms/powernv/Kconfig
> @@ -27,3 +27,13 @@ config OPAL_PRD
> help
> This enables the opal-prd driver, a facility to run processor
> recovery diagnostics on OpenPower machines
> +
> +config HV_PERF_IMC_CTRS
> + bool "Hypervisor supplied In Memory Collection PMU events (Nest & Core)"
> + default y
> + depends on PERF_EVENTS && PPC_POWERNV
> + help
> + Enable access to hypervisor supplied in-memory collection counters
> + in perf. IMC counters are available from Power9 systems.
> +
> + If unsure, select Y.
> diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
> index b5d98cb..715e531 100644
> --- a/arch/powerpc/platforms/powernv/Makefile
> +++ b/arch/powerpc/platforms/powernv/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
> obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
> obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
> obj-$(CONFIG_OPAL_PRD) += opal-prd.o
> +obj-$(CONFIG_HV_PERF_IMC_CTRS) += opal-imc.o
> diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
> new file mode 100644
> index 0000000..3a87000
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/opal-imc.c
> @@ -0,0 +1,140 @@
> +/*
> + * OPAL IMC interface detection driver
> + * Supported on POWERNV platform
> + *
> + * Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
> + * (C) 2017 Anju T Sudhakar, IBM Corporation.
> + * (C) 2017 Hemant K Shaw, IBM Corporation.
> + *
> + * 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 in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/fs.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/poll.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/crash_dump.h>
> +#include <asm/opal.h>
> +#include <asm/io.h>
> +#include <asm/uaccess.h>
> +#include <asm/cputable.h>
> +#include <asm/imc-pmu.h>
> +
> +struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
> +
> +/*
> + * imc_pmu_setup : Setup the IMC PMUs (children of "parent").
> + */
> +static void __init imc_pmu_setup(struct device_node *parent)
> +{
> + if (!parent)
> + return;
> +}
> +
> +static int opal_imc_counters_probe(struct platform_device *pdev)
> +{
> + struct device_node *imc_dev, *dn, *rm_node = NULL;
> + struct perchip_nest_info *pcni;
> + u32 pages, nest_offset, nest_size, chip_id;
> + int i = 0;
> + const __be32 *addrp;
> + u64 reg_addr, reg_size;
> +
> + if (!pdev || !pdev->dev.of_node)
> + return -ENODEV;
> +
> + /*
> + * Check whether this is kdump kernel. If yes, just return.
> + */
> + if (is_kdump_kernel())
> + return -ENODEV;
> +
> + imc_dev = pdev->dev.of_node;
> +
> + /*
> + * Nest counter data are saved in a reserved memory called HOMER.
> + * "imc-nest-offset" identifies the counter data location within HOMER.
> + * size : size of the entire nest-counters region
> + */
> + if (of_property_read_u32(imc_dev, "imc-nest-offset", &nest_offset))
> + goto err;
> +
> + if (of_property_read_u32(imc_dev, "imc-nest-size", &nest_size))
> + goto err;
> +
> + /* Sanity check */
> + if ((nest_size/PAGE_SIZE) > IMC_NEST_MAX_PAGES)
> + goto err;
> +
> + /* Find the "HOMER region" for each chip */
> + rm_node = of_find_node_by_path("/reserved-memory");
> + if (!rm_node)
> + goto err;
> +
> + /*
> + * We need to look for the "ibm,homer-image" node in the
> + * "/reserved-memory" node.
> + */
> + for (dn = of_find_node_by_name(rm_node, "ibm,homer-image"); dn;
> + dn = of_find_node_by_name(dn, "ibm,homer-image")) {
> +
> + /* Get the chip id to which the above homer region belongs to */
> + if (of_property_read_u32(dn, "ibm,chip-id", &chip_id))
> + goto err;
So, I was thinking on this (and should probably comment on the firmware
side as well).
I'd prefer an OPAL interface where instead of looking up where
ibm,homer-image is, we provide the kernel with a base address and then
have offsets into it.
That way, we don't tie the kernel code to counters that are only in the
HOMER region.
--
Stewart Smith
OPAL Architect, IBM.
[toc] | [prev] | [next] | [standalone]
| From | Madhavan Srinivasan <maddy@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-05-12 06:40 +0200 |
| Subject | Re: [PATCH v8 02/10] powerpc/powernv: Autoload IMC device driver module |
| Message-ID | <tGawV-7Br-3@gated-at.bofh.it> |
| In reply to | #1639229 |
On Thursday 11 May 2017 01:19 PM, Stewart Smith wrote:
> Anju T Sudhakar <anju@linux.vnet.ibm.com> writes:
>> This patch does three things :
>> - Enables "opal.c" to create a platform device for the IMC interface
>> according to the appropriate compatibility string.
>> - Find the reserved-memory region details from the system device tree
>> and get the base address of HOMER (Reserved memory) region address for each chip.
>> - We also get the Nest PMU counter data offsets (in the HOMER region)
>> and their sizes. The offsets for the counters' data are fixed and
>> won't change from chip to chip.
>>
>> The device tree parsing logic is separated from the PMU creation
>> functions (which is done in subsequent patches).
>>
>> Patch also adds a CONFIG_HV_PERF_IMC_CTRS for the IMC driver.
>>
>> Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
>> Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
>> Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
>> ---
>> arch/powerpc/platforms/powernv/Kconfig | 10 +++
>> arch/powerpc/platforms/powernv/Makefile | 1 +
>> arch/powerpc/platforms/powernv/opal-imc.c | 140 ++++++++++++++++++++++++++++++
>> arch/powerpc/platforms/powernv/opal.c | 18 ++++
>> 4 files changed, 169 insertions(+)
>> create mode 100644 arch/powerpc/platforms/powernv/opal-imc.c
>>
>> diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
>> index 3a07e4d..1b90a98 100644
>> --- a/arch/powerpc/platforms/powernv/Kconfig
>> +++ b/arch/powerpc/platforms/powernv/Kconfig
>> @@ -27,3 +27,13 @@ config OPAL_PRD
>> help
>> This enables the opal-prd driver, a facility to run processor
>> recovery diagnostics on OpenPower machines
>> +
>> +config HV_PERF_IMC_CTRS
>> + bool "Hypervisor supplied In Memory Collection PMU events (Nest & Core)"
>> + default y
>> + depends on PERF_EVENTS && PPC_POWERNV
>> + help
>> + Enable access to hypervisor supplied in-memory collection counters
>> + in perf. IMC counters are available from Power9 systems.
>> +
>> + If unsure, select Y.
>> diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
>> index b5d98cb..715e531 100644
>> --- a/arch/powerpc/platforms/powernv/Makefile
>> +++ b/arch/powerpc/platforms/powernv/Makefile
>> @@ -12,3 +12,4 @@ obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
>> obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
>> obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
>> obj-$(CONFIG_OPAL_PRD) += opal-prd.o
>> +obj-$(CONFIG_HV_PERF_IMC_CTRS) += opal-imc.o
>> diff --git a/arch/powerpc/platforms/powernv/opal-imc.c b/arch/powerpc/platforms/powernv/opal-imc.c
>> new file mode 100644
>> index 0000000..3a87000
>> --- /dev/null
>> +++ b/arch/powerpc/platforms/powernv/opal-imc.c
>> @@ -0,0 +1,140 @@
>> +/*
>> + * OPAL IMC interface detection driver
>> + * Supported on POWERNV platform
>> + *
>> + * Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
>> + * (C) 2017 Anju T Sudhakar, IBM Corporation.
>> + * (C) 2017 Hemant K Shaw, IBM Corporation.
>> + *
>> + * 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 in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/miscdevice.h>
>> +#include <linux/fs.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/poll.h>
>> +#include <linux/mm.h>
>> +#include <linux/slab.h>
>> +#include <linux/crash_dump.h>
>> +#include <asm/opal.h>
>> +#include <asm/io.h>
>> +#include <asm/uaccess.h>
>> +#include <asm/cputable.h>
>> +#include <asm/imc-pmu.h>
>> +
>> +struct perchip_nest_info nest_perchip_info[IMC_MAX_CHIPS];
>> +
>> +/*
>> + * imc_pmu_setup : Setup the IMC PMUs (children of "parent").
>> + */
>> +static void __init imc_pmu_setup(struct device_node *parent)
>> +{
>> + if (!parent)
>> + return;
>> +}
>> +
>> +static int opal_imc_counters_probe(struct platform_device *pdev)
>> +{
>> + struct device_node *imc_dev, *dn, *rm_node = NULL;
>> + struct perchip_nest_info *pcni;
>> + u32 pages, nest_offset, nest_size, chip_id;
>> + int i = 0;
>> + const __be32 *addrp;
>> + u64 reg_addr, reg_size;
>> +
>> + if (!pdev || !pdev->dev.of_node)
>> + return -ENODEV;
>> +
>> + /*
>> + * Check whether this is kdump kernel. If yes, just return.
>> + */
>> + if (is_kdump_kernel())
>> + return -ENODEV;
>> +
>> + imc_dev = pdev->dev.of_node;
>> +
>> + /*
>> + * Nest counter data are saved in a reserved memory called HOMER.
>> + * "imc-nest-offset" identifies the counter data location within HOMER.
>> + * size : size of the entire nest-counters region
>> + */
>> + if (of_property_read_u32(imc_dev, "imc-nest-offset", &nest_offset))
>> + goto err;
>> +
>> + if (of_property_read_u32(imc_dev, "imc-nest-size", &nest_size))
>> + goto err;
>> +
>> + /* Sanity check */
>> + if ((nest_size/PAGE_SIZE) > IMC_NEST_MAX_PAGES)
>> + goto err;
>> +
>> + /* Find the "HOMER region" for each chip */
>> + rm_node = of_find_node_by_path("/reserved-memory");
>> + if (!rm_node)
>> + goto err;
>> +
>> + /*
>> + * We need to look for the "ibm,homer-image" node in the
>> + * "/reserved-memory" node.
>> + */
>> + for (dn = of_find_node_by_name(rm_node, "ibm,homer-image"); dn;
>> + dn = of_find_node_by_name(dn, "ibm,homer-image")) {
>> +
>> + /* Get the chip id to which the above homer region belongs to */
>> + if (of_property_read_u32(dn, "ibm,chip-id", &chip_id))
>> + goto err;
> So, I was thinking on this (and should probably comment on the firmware
> side as well).
>
> I'd prefer an OPAL interface where instead of looking up where
> ibm,homer-image is, we provide the kernel with a base address and then
> have offsets into it.
>
> That way, we don't tie the kernel code to counters that are only in the
> HOMER region.
Yes. This make sense. Adding something like this to IMC node
will be fine?
chip@<id> {
base_addr = < addr >;
ibm,chip-id = < id>;
};
Maddy
>
>
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | linux.kernel
csiph-web