nds32: Perf support
Subsystems (2)
cover nds32: Perf support
These two commit are perf supporting for nds32. There are three perfomance counters in nds32, and each of them can counts different events. You can use 'perf list' to show the available events that can be used.
Nickhu (5):
nds32: Perf porting
nds32: Fix bug in bitfield.h
nds32: Add perf call-graph support.
nds32: Fix perf multiple events map to same counter.
nds32: Add document for NDS32 PMU.
.../devicetree/bindings/nds32/pmu.txt | 17 + arch/nds32/Kconfig | 1 + arch/nds32/boot/dts/ae3xx.dts | 5 + arch/nds32/include/asm/Kbuild | 1 + arch/nds32/include/asm/bitfield.h | 4 +- arch/nds32/include/asm/perf_event.h | 16 + arch/nds32/include/asm/pmu.h | 431 +++++ arch/nds32/include/asm/stacktrace.h | 39 + arch/nds32/kernel/Makefile | 3 +- arch/nds32/kernel/perf_event_cpu.c | 1579 +++++++++++++++++ arch/nds32/mm/fault.c | 13 +- tools/include/asm/barrier.h | 2 + tools/perf/arch/nds32/Build | 1 + tools/perf/arch/nds32/util/Build | 1 + tools/perf/arch/nds32/util/header.c | 29 + tools/perf/pmu-events/arch/nds32/mapfile.csv | 15 + .../pmu-events/arch/nds32/n13/atcpmu.json | 290 +++ 17 files changed, 2439 insertions(+), 8 deletions(-) create mode 100644 Documentation/devicetree/bindings/nds32/pmu.txt create mode 100644 arch/nds32/include/asm/perf_event.h create mode 100644 arch/nds32/include/asm/pmu.h create mode 100644 arch/nds32/include/asm/stacktrace.h create mode 100644 arch/nds32/kernel/perf_event_cpu.c create mode 100644 tools/perf/arch/nds32/Build create mode 100644 tools/perf/arch/nds32/util/Build create mode 100644 tools/perf/arch/nds32/util/header.c create mode 100644 tools/perf/pmu-events/arch/nds32/mapfile.csv create mode 100644 tools/perf/pmu-events/arch/nds32/n13/atcpmu.json
Signature · 1 line
2.17.0
PATCH 1/5 nds32: Perf porting
This is the commit that porting the perf for nds32.
Raw event:
The raw events start with 'r'.
Usage:
perf stat -e rXYZ ./app
X: the index of performance counter.
YZ: the index(convert to hexdecimal) of events
Example:
'perf stat -e r101 ./app' means the counter 1 will count the instruction
event.
The index of counter and events can be found in "Andes System Privilege Architecture Version 3 Manual".
Or you can perform the 'perf list' to find the symbolic name of raw events.
Perf mmap2:
Fix unexpected perf mmap2() page fault
When the mmap2() called by perf application, you will encounter such condition:"failed to write." With return value -EFAULT
This is due to the page fault caused by "reading" buffer from the mapped legal address region to write to the descriptor. The page_fault handler will get a VM_FAULT_SIGBUS return value, which should not happens here.(Due to this is a read request.)
You can refer to kernel/events/core.c:perf_mmap_fault(...) If "(vmf->pgoff && (vmf->flags & FAULT_FLAG_WRITE))" is evaluated as true, you will get VM_FAULT_SIGBUS as return value.
However, this is not an write request. The flags which indicated why the page fault happens is wrong.
Furthermore, NDS32 SPAv3 is not able to detect it is read or write. It only know either it is instruction fetch or data access.
Therefore, by removing the wrong flag assignment(actually, the hardware is not able to show the reason), we can fix this bug.
--- arch/nds32/Kconfig | 1 + arch/nds32/boot/dts/ae3xx.dts | 5 + arch/nds32/include/asm/Kbuild | 1 + arch/nds32/include/asm/perf_event.h | 16 + arch/nds32/include/asm/pmu.h | 430 ++++++ arch/nds32/include/asm/stacktrace.h | 39 + arch/nds32/kernel/Makefile | 3 +- arch/nds32/kernel/perf_event_cpu.c | 1270 +++++++++++++++++ arch/nds32/mm/fault.c | 13 +- tools/include/asm/barrier.h | 2 + tools/perf/arch/nds32/Build | 1 + tools/perf/arch/nds32/util/Build | 1 + tools/perf/arch/nds32/util/header.c | 29 + tools/perf/pmu-events/arch/nds32/mapfile.csv | 15 + .../pmu-events/arch/nds32/n13/atcpmu.json | 290 ++++ 15 files changed, 2110 insertions(+), 6 deletions(-) create mode 100644 arch/nds32/include/asm/perf_event.h create mode 100644 arch/nds32/include/asm/pmu.h create mode 100644 arch/nds32/include/asm/stacktrace.h create mode 100644 arch/nds32/kernel/perf_event_cpu.c create mode 100644 tools/perf/arch/nds32/Build create mode 100644 tools/perf/arch/nds32/util/Build create mode 100644 tools/perf/arch/nds32/util/header.c create mode 100644 tools/perf/pmu-events/arch/nds32/mapfile.csv create mode 100644 tools/perf/pmu-events/arch/nds32/n13/atcpmu.json
arch/nds32/Kconfig +1 -0
@@ -31,6 +31,7 @@ config NDS32 select HAVE_DEBUG_KMEMLEAK select HAVE_MEMBLOCK select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_PERF_EVENTS select IRQ_DOMAIN select LOCKDEP_SUPPORT select MODULES_USE_ELF_RELA
arch/nds32/boot/dts/ae3xx.dts +5 -0
@@ -82,4 +82,9 @@ interrupts = <18>; }; }; + + pmu { + compatible = "andestech,atcpmu"; + interrupts= <13>; + }; };
arch/nds32/include/asm/Kbuild +1 -0
@@ -36,6 +36,7 @@ generic-y += kprobes.h generic-y += kvm_para.h generic-y += limits.h generic-y += local.h +generic-y += local64.h generic-y += mm-arch-hooks.h generic-y += mman.h generic-y += parport.h
arch/nds32/include/asm/perf_event.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2008-2018 Andes Technology Corporation */ + +#ifndef __ASM_PERF_EVENT_H +#define __ASM_PERF_EVENT_H + +/* +* This file is request by Perf, +* please refer to tools/perf/design.txt for more details +*/ +struct pt_regs ; +unsigned long perf_instruction_pointer (struct pt_regs *regs); +unsigned long perf_misc_flags (struct pt_regs *regs); +#define perf_misc_flags (regs) perf_misc_flags(regs) + +#endif
arch/nds32/include/asm/pmu.h new file +430 -0
430 lines not on this page · raw message
arch/nds32/include/asm/stacktrace.h new file +39 -0
@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2008-2018 Andes Technology Corporation */ + +#ifndef __ASM_STACKTRACE_H +#define __ASM_STACKTRACE_H + +/* Kernel callchain */ +struct stackframe { +unsigned long fp; +unsigned long sp; +unsigned long lp; +}; + +/* +* struct frame_tail: User callchain +* IMPORTANT: +* This struct is used for call-stack walking, +* the order and types matters. +* Do not use array, it only stores sizeof(pointer) +* +* The details can refer to arch/arm/kernel/perf_event.c +*/ +struct frame_tail { +unsigned long stack_fp; +unsigned long stack_lp; +}; + +/* For User callchain with optimize for size */ +struct frame_tail_opt_size { +unsigned long stack_r6; +unsigned long stack_fp; +unsigned long stack_gp; +unsigned long stack_lp; +}; + +extern void +get_real_ret_addr (unsigned long *addr,struct task_struct *tsk,int *graph); + +#endif /* __ASM_STACKTRACE_H */
arch/nds32/kernel/Makefile +1 -2
@@ -4,7 +4,6 @@ CPPFLAGS_vmlinux.lds := -DTEXTADDR=$(TEXTADDR) AFLAGS_head.o := -DTEXTADDR=$(TEXTADDR) - # Object file lists. obj-y := ex-entry.o ex-exit.o ex-scall.o irq.o \
@@ -16,10 +15,10 @@ obj-$(CONFIG_MODULES) += nds32_ksyms.o module.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_OF) += devtree.o obj-$(CONFIG_CACHE_L2) += atl2c.o +obj-$(CONFIG_PERF_EVENTS) += perf_event_cpu.o extra-y := head.o vmlinux.lds - obj-y += vdso/ obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
arch/nds32/kernel/perf_event_cpu.c new file +1270 -0
1,270 lines not on this page · raw message
arch/nds32/mm/fault.c +9 -4
@@ -9,6 +9,7 @@#include <linux/init.h> #include <linux/hardirq.h> #include <linux/uaccess.h> +#include <linux/perf_event.h> #include <asm/pgtable.h> #include <asm/tlbflush.h>
@@ -169,8 +170,6 @@ void do_page_fault(unsigned long entry, unsigned long addr, mask =VM_EXEC ; else { mask =VM_READ |VM_WRITE ; -if (vma->vm_flags &VM_WRITE ) - flags |=FAULT_FLAG_WRITE ; } }else if (entry ==ENTRY_TLB_MISC ) {switch (error_code & ITYPE_mskETYPE) {
@@ -231,11 +230,17 @@ void do_page_fault(unsigned long entry, unsigned long addr,* attempt. If we go through a retry, it is extremely likely that the * page will be found in page cache at that point. */ +perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS ,1 , regs, addr);if (flags &FAULT_FLAG_ALLOW_RETRY ) { -if (fault &VM_FAULT_MAJOR ) +if (fault &VM_FAULT_MAJOR ) { tsk->maj_flt++; -else +perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS_MAJ , +1 , regs, addr); + }else { tsk->min_flt++; +perf_sw_event (PERF_COUNT_SW_PAGE_FAULTS_MIN , +1 , regs, addr); + }if (fault &VM_FAULT_RETRY ) { flags &= ~FAULT_FLAG_ALLOW_RETRY ; flags |=FAULT_FLAG_TRIED ;
tools/include/asm/barrier.h +2 -0
@@ -23,6 +23,8 @@#include "../../arch/ia64/include/asm/barrier.h" #elif defined(__xtensa__)#include "../../arch/xtensa/include/asm/barrier.h" +#elif defined(__nds32__) +#include "../../arch/nds32/include/asm/barrier.h" #else #include <asm-generic/barrier.h> #endif
tools/perf/arch/nds32/Build new file +1 -0
@@ -0,0 +1 @@ +libperf-y += util/
tools/perf/arch/nds32/util/Build new file +1 -0
@@ -0,0 +1 @@ +libperf-y += header.o
tools/perf/arch/nds32/util/header.c new file +29 -0
@@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <stdio.h> +#include <stdlib.h> +#include <api/fs/fs.h> +#include "header.h" + +#define STR_LEN 1024 + +char *get_cpuid_str (struct perf_pmu *pmu) +{ +/* In nds32, we only have one cpu */ +char *buf =NULL ; +struct cpu_map *cpus; +const char *sysfs =sysfs__mountpoint (); + +if (!sysfs || !pmu || !pmu->cpus) +return NULL ; + + buf =malloc (STR_LEN ); +if (!buf) +return NULL ; + + cpus =cpu_map__get (pmu->cpus); +sprintf (buf,"0x%x" , cpus->nr -1 ); +cpu_map__put (cpus); +return buf; +}
tools/perf/pmu-events/arch/nds32/mapfile.csv new file +15 -0
@@ -0,0 +1,15 @@ +# Format: +# MIDR,Version,JSON/file/pathname,Type +# +# where +# MIDR Processor version +# Variant[23:20] and Revision [3:0] should be zero. +# Version could be used to track version of of JSON file +# but currently unused. +# JSON/file/pathname is the path to JSON file, relative +# to tools/perf/pmu-events/arch/arm64/. +# Type is core, uncore etc +# +# +#Family-model,Version,Filename,EventType +0x0,v3,n13,core
tools/perf/pmu-events/arch/nds32/n13/atcpmu.json new file +290 -0
290 lines not on this page · raw message
Signature · 1 line
2.17.0
Mark Rutland
reviewerRe: [PATCH 1/5] nds32: Perf porting
HI,
Quoted 15 lines · Nickhu
arch/nds32/include/asm/pmu.h new file +15
-0+#define PFM_CTL_OVF (idx) PFM_CTL_mskOVF ## idx +#define PFM_CTL_EN (idx) PFM_CTL_mskEN ## idx +#define PFM_CTL_OFFSEL (idx) PFM_CTL_offSEL ## idx +#define PFM_CTL_IE (idx) PFM_CTL_mskIE ## idx +#define PFM_CTL_KS (idx) PFM_CTL_mskKS ## idx +#define PFM_CTL_KU (idx) PFM_CTL_mskKU ## idx +#define PFM_CTL_SEL (idx) PFM_CTL_mskSEL ## idx + +#define macro_expansion (macro_name, var, idx) do { \ + switch (idx) { \ + case 0:\ + var = macro_name ## 0; \ + break; \ + case 1:\ + var = macro_name ## 1; \
arch/nds32/include/asm/pmu.h line 49
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+break ; \ +case 2 :\ + var = macro_name ## 2; \ +break ; \ +default :\ +pr_err ("mask index=%d not in the range at %s,line %d\n" , \ + idx, __FILE__, __LINE__); \ +break ; \ + } \ +}while (0 )
This macro makes things very difficult to understand, especially since in-context it's not clear what's happening to var.
Can you restructure your defintions so you can have things like:
/* valid for 0,1,2 */
#define
#define PFM_CTL_OVF(idx) BIT(6 + (idx))
... and avoid this macro entirely?
arch/nds32/include/asm/pmu.h line 62
Quoted 13 lines
arch/nds32/include/asm/pmu.h new file +13
-0+ +enum {PFMC0 ,PFMC1 ,PFMC2 ,MAX_COUNTERS }; + +/* +* Perf Events' indices +*/ +#define NDS32_IDX_CYCLE_COUNTER 0 +#define NDS32_IDX_COUNTER0 1 +#define NDS32_IDX_COUNTER_LAST (cpu_pmu) \ + (NDS32_IDX_CYCLE_COUNTER + (cpu_pmu)->num_events - 1) + +#define NDS32_MAX_COUNTERS 32 +#define NDS32_COUNTER_MASK (NDS32_MAX_COUNTERS - 1)
Huh? You only seem to have three above (ignoring the fixed-purpose cycle counter).
These definitions aren't used anywhere, regadless.
arch/nds32/include/asm/pmu.h line 70
Quoted 8 lines
arch/nds32/include/asm/pmu.h new file +8
-0+ +/* +* struct nds32_pmu_platdata - NDS32 PMU platform data +* +* @handle_irq: an optional handler which will be called from the +* interrupt and passed the address of the low level handler, +* and can be used to implement any platform specific handling +* before or after calling it.
Will you actually need this? We got rid of it in the arm_pmu framework.
Quoted 27 lines
+ * @runtime_resume: an optional handler which will be called by the
+ * runtime PM framework following a call to pm_runtime_get(). + * Note that if pm_runtime_get() is called more than once in + * succession this handler will only be called once. + * @runtime_suspend: an optional handler which will be called by the + * runtime PM framework following a call to pm_runtime_put(). + * Note that if pm_runtime_get() is called more than once in + * succession this handler will only be called following the + * final call to pm_runtime_put() that actually disables the + * hardware.+ */
arch/nds32/include/asm/pmu.h new file +16
-0+struct nds32_pmu_platdata { +irqreturn_t (*handle_irq)(int irq,void *dev, +irq_handler_t pmu_handler); +int (*runtime_resume)(struct device *dev); +int (*runtime_suspend)(struct device *dev); +}; + +/* The events for a given PMU register set. */ +struct pmu_hw_events { +/* +* The events that are active on the PMU for the given index. +*/ +struct perf_event **events; + +/* +* A 1 bit for an index indicates that the counter is being used for
arch/nds32/include/asm/pmu.h line 107
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+* an event. A 0 means that the counter can be used. +*/ +unsigned long *used_mask; + +/* +* Hardware lock to serialize accesses to PMU registers. Needed for the +* read/modify/write sequences. +*/ +raw_spinlock_t pmu_lock; +};
It looks like this is derived from the really early arm_pmu code, when we were trying to be far more flexible than was necessary. This can be simplified significantly.
e.g. since you know up-front the max number of counters, this can be:
struct pmu_hw_events {
struct perf_event *events[MAX_COUNTERS];
unsigned long mask[BITS_TO_LONGS(MAX_COUNTERS)];
raw_spinlock_t pmu_lock;
};... and you can avoid having to dynamically allocate each field separately.
I'm not sure the pmu_lock is even necessary in the asbence of NMI.
arch/nds32/include/asm/pmu.h line 112
Quoted 5 lines
arch/nds32/include/asm/pmu.h new file +5
-0+ +struct nds32_pmu { +struct pmu pmu; +cpumask_t active_irqs; +cpumask_t supported_cpus;
Do you intend to support heterogeneous systems (e.g big.LITTLE)?
If not, you don't need the supported_cpus mask.
Quoted 9 lines
arch/nds32/include/asm/pmu.h new file +9
-0+char *name; +irqreturn_t (*handle_irq)(int irq_num,void *dev); +void (*enable)(struct perf_event *event); +void (*disable)(struct perf_event *event); +int (*get_event_idx)(struct pmu_hw_events *hw_events, +struct perf_event *event); +int (*set_event_filter)(struct hw_perf_event *evt, +struct perf_event_attr *attr); +u32 (*read_counter)(struct perf_event *event);
arch/nds32/include/asm/pmu.h line 131
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+void (*write_counter)(struct perf_event *event,u32 val); +void (*start)(struct nds32_pmu *nds32_pmu); +void (*stop)(struct nds32_pmu *nds32_pmu); +void (*reset)(void *data); +int (*request_irq)(struct nds32_pmu *nds32_pmu,irq_handler_t handler); +void (*free_irq)(struct nds32_pmu *nds32_pmu); +int (*map_event)(struct perf_event *event); +int num_events; +atomic_t active_events; +struct mutex reserve_mutex;/* mutex */
I don't think you need the reserve_mutex; for arm_pmu that was a holdover form when there were multiple frameworks competing to mange the PMU, and now there's only perf.
Quoted 1 line
+ u64
max_period;
It looks like this is always 0xffffffff, so you don't need a variable for that.
[...]
arch/nds32/include/asm/pmu.h line 247
Quoted 13 lines
arch/nds32/include/asm/pmu.h new file +13
-0+/* Get converted event counter index */ +#define GET_CONVERTED_EVENT_IDX (event, idx) do { \ + if ((event) > SPAV3_0_SEL_BASE && event < SPAV3_0_SEL_LAST) { \ + idx = 0; \ + } else if ((event) > SPAV3_1_SEL_BASE && event < SPAV3_1_SEL_LAST) { \ + idx = 1; \ + } else if ((event) > SPAV3_2_SEL_BASE && event < SPAV3_2_SEL_LAST) { \ + idx = 2; \ + } else { \ + pr_err("GET_CONVERTED_EVENT_IDX PFM counter range error\n"); \ + return -EPERM; \ + } \ +} while (0)
This would be cleaner as a static inline.
Quoted 6 lines
arch/nds32/include/asm/pmu.h new file +6
-0+ +/* Get converted hardware event number */ +#define GET_CONVERTED_EVENT_HW_NUM (event) do { \ + if ((event) == 0) { \ +/*do nothing*/ \ + } else if ((event) > SPAV3_0_SEL_BASE && event < SPAV3_0_SEL_LAST) { \
arch/nds32/include/asm/pmu.h line 263
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+ (event) -=PFM_OFFSET_MAGIC_0 ; \ + }else if ((event) >SPAV3_1_SEL_BASE && event <SPAV3_1_SEL_LAST ) { \ + (event) -=PFM_OFFSET_MAGIC_1 ; \ + }else if ((event) >SPAV3_2_SEL_BASE && event <SPAV3_2_SEL_LAST ) { \ + (event) -=PFM_OFFSET_MAGIC_2 ; \ + }else { \ +pr_err (\ +"GET_CONVERTED_EVENT_HW_NUM PFM counter range error\n" ); \ + } \ +}while (0 )
Likewise, a static inline would be cleaner.
[...]
arch/nds32/kernel/perf_event_cpu.c line 30
Quoted 6 lines
arch/nds32/kernel/perf_event_cpu.c new file +6
-0+/* Set at runtime when we know what CPU type we are. */ +static struct nds32_pmu *cpu_pmu; + +static DEFINE_PER_CPU(struct perf_event * [MAX_COUNTERS], hw_events); +static DEFINE_PER_CPU(unsigned long[BITS_TO_LONGS(MAX_COUNTERS)], used_mask); +static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events);
As before, I think you can fold the first wo fields into pmu_hw_events, then you only need one DEFINE_PER_CPU() here.
[...]
Quoted 50 lines
arch/nds32/kernel/perf_event_cpu.c new file +50
-0+static irqreturn_t nds32_pmu_handle_irq (int irq_num,void *dev) +{ +u32 pfm; +struct perf_sample_data data; +struct nds32_pmu *cpu_pmu = (struct nds32_pmu *)dev; +struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events (); +struct pt_regs *regs; +int idx; +/* +* Get and reset the IRQ flags +*/ + pfm =nds32_pfm_getreset_flags (); + +/* +* Did an overflow occur? +*/ +if (!nds32_pfm_has_overflowed (pfm)) +return IRQ_NONE ; + +/* +* Handle the counter(s) overflow(s) +*/ + regs =get_irq_regs (); + +for (idx =0 ; idx < cpu_pmu->num_events; ++idx) { +struct perf_event *event = cpuc->events[idx]; +struct hw_perf_event *hwc; + +/* Ignore if we don't have an event. */ +if (!event) +continue ; + +/* +* We have a single interrupt for all counters. Check that +* each counter has overflowed before we process it. +*/ +if (!nds32_pfm_counter_has_overflowed (pfm, idx)) +continue ; + + hwc = &event->hw; +nds32_pmu_event_update (event); +perf_sample_data_init (&data,0 , hwc->last_period); +if (!nds32_pmu_event_set_period (event)) +continue ; + +if (perf_event_overflow (event, &data, regs)) + cpu_pmu->disable (event); + } + +/*
arch/nds32/kernel/perf_event_cpu.c line 280
Quoted 10 lines
arch/nds32/kernel/perf_event_cpu.c new file +10
-0+* Handle the pending perf events. +* +* Note: this call *must* be run with interrupts disabled. For +* platforms that can have the PMU interrupts raised as an NMI, this +* will not work. +*/ +irq_work_run (); + +return IRQ_HANDLED ; +}
You'll want to stop all counters over the IRQ handler to ensure that groups are consistent.
[...]
arch/nds32/kernel/perf_event_cpu.c line 655
Quoted 8 lines
arch/nds32/kernel/perf_event_cpu.c new file +8
-0+static int device_pmu_init (struct nds32_pmu *cpu_pmu) +{ +nds32_pmu_init (cpu_pmu); +/* +* This name should be devive-specific name, whatever you like :) +* I think "PMU" will be a good generic name. +*/ + cpu_pmu->name ="atcpmu" ;
This looks like it should have a more complete version string, e.g. with "spav3" in it.
[...]
arch/nds32/kernel/perf_event_cpu.c line 760
Quoted 6 lines
arch/nds32/kernel/perf_event_cpu.c new file +6
-0+static int +validate_event (struct pmu *pmu,struct pmu_hw_events *hw_events, +struct perf_event *event) +{ +struct nds32_pmu *nds32_pmu =to_nds32_pmu (event->pmu); +//struct pmu *leader_pmu = event->group_leader->pmu;
Code which is commented out should be deleted.
[...]
Quoted 35 lines
arch/nds32/kernel/perf_event_cpu.c new file +35
-0+static int cpu_pmu_request_irq (struct nds32_pmu *cpu_pmu,irq_handler_t handler) +{ +int i, err, irq, irqs; +struct platform_device *pmu_device = cpu_pmu->plat_device; + +if (!pmu_device) +return -ENODEV ; + + irqs =min (pmu_device->num_resources,num_possible_cpus ()); +if (irqs <1 ) { +pr_err ("no irqs for PMUs defined\n" ); +return -ENODEV ; + } + +for (i =0 ; i < irqs; ++i) { + err =0 ; + irq =platform_get_irq (pmu_device, i); +if (irq <0 ) +continue ; + +/* +* If we have a single PMU interrupt that we can't shift, +* assume that we're running on a uniprocessor machine and +* continue. Otherwise, continue without this interrupt. +*/ +if (irq_set_affinity (irq,cpumask_of (i)) && irqs >1 ) { +pr_warning + ("unable to set irq affinity (irq=%d, cpu=%u)\n" , + irq, i); +continue ; + } + + err =request_irq (irq, handler,IRQF_NOBALANCING ,"nds32-pfm" , + cpu_pmu); +if (err) {
arch/nds32/kernel/perf_event_cpu.c line 1130
Quoted 10 lines
arch/nds32/kernel/perf_event_cpu.c new file +10
-0+pr_err ("unable to request IRQ%d for NDS PMU counters\n" , + irq); +return err; + } + +cpumask_set_cpu (i, &cpu_pmu->active_irqs); + } + +return 0 ; +}
Is nds32 SMP?
If not, this should be simplifed to expect one CPU alone.
If so, this needs to clarify which IRQ maps to which CPU with an explicit property in the DT, as we do for arm with interrupt-affinity.
Thanks,
Mark.
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
Re: [PATCH 1/5] nds32: Perf porting
Hi Mark,
Quoted 32 lines · Mark Rutland
HI,
Quoted 15 lines · Nickhu
arch/nds32/include/asm/pmu.h new file +15
-0+#define PFM_CTL_OVF (idx) PFM_CTL_mskOVF ## idx +#define PFM_CTL_EN (idx) PFM_CTL_mskEN ## idx +#define PFM_CTL_OFFSEL (idx) PFM_CTL_offSEL ## idx +#define PFM_CTL_IE (idx) PFM_CTL_mskIE ## idx +#define PFM_CTL_KS (idx) PFM_CTL_mskKS ## idx +#define PFM_CTL_KU (idx) PFM_CTL_mskKU ## idx +#define PFM_CTL_SEL (idx) PFM_CTL_mskSEL ## idx + +#define macro_expansion (macro_name, var, idx) do { \ + switch (idx) { \ + case 0:\ + var = macro_name ## 0; \ + break; \ + case 1:\ + var = macro_name ## 1; \arch/nds32/include/asm/pmu.h line 49
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+break ; \ +case 2 :\ + var = macro_name ## 2; \ +break ; \ +default :\ +pr_err ("mask index=%d not in the range at %s,line %d\n" , \ + idx, __FILE__, __LINE__); \ +break ; \ + } \ +}while (0 )This macro makes things very difficult to understand, especially since in-context it's not clear what's happening to var.
Quoted 8 lines
Can you restructure your defintions so you can have things like:
/* valid for 0,1,2 */
#define
#definePFM_CTL_OVF(idx)BIT(6 + (idx))... and avoid this macro entirely?
Since we have already define the bit field of registers in 'arch/nds32/include/asm/bitfield.h', including performance control register. So I prefer use the array to replace the definitions, like:
u32 PFM_CTL_OVF[3] = { PFM_CTL_mskOVF0, PFM_CTL_mskOVF1,
PFM_CTL_mskOVF2 };
And 'macro_expansion' will be removed.
Quoted 19 lines
arch/nds32/include/asm/pmu.h line 62
Quoted 13 lines
arch/nds32/include/asm/pmu.h new file +13
-0+ +enum {PFMC0 ,PFMC1 ,PFMC2 ,MAX_COUNTERS }; + +/* +* Perf Events' indices +*/ +#define NDS32_IDX_CYCLE_COUNTER 0 +#define NDS32_IDX_COUNTER0 1 +#define NDS32_IDX_COUNTER_LAST (cpu_pmu) \ + (NDS32_IDX_CYCLE_COUNTER + (cpu_pmu)->num_events - 1) + +#define NDS32_MAX_COUNTERS 32 +#define NDS32_COUNTER_MASK (NDS32_MAX_COUNTERS - 1)Huh? You only seem to have three above (ignoring the fixed-purpose cycle counter).
These definitions aren't used anywhere, regadless.
Quoted 11 lines
arch/nds32/include/asm/pmu.h line 70
Quoted 8 lines
arch/nds32/include/asm/pmu.h new file +8
-0+ +/* +* struct nds32_pmu_platdata - NDS32 PMU platform data +* +* @handle_irq: an optional handler which will be called from the +* interrupt and passed the address of the low level handler, +* and can be used to implement any platform specific handling +* before or after calling it. Will you actually need this? We got rid of it in the
arm_pmuframework.
You are right, we don't need this. I will remove it in next patch.
Quoted 63 lines
Quoted 27 lines
+ * @runtime_resume: an optional handler which will be called by the
+ * runtime PM framework following a call to pm_runtime_get(). + * Note that if pm_runtime_get() is called more than once in + * succession this handler will only be called once. + * @runtime_suspend: an optional handler which will be called by the + * runtime PM framework following a call to pm_runtime_put(). + * Note that if pm_runtime_get() is called more than once in + * succession this handler will only be called following the + * final call to pm_runtime_put() that actually disables the + * hardware.+ */
arch/nds32/include/asm/pmu.h new file +16
-0+struct nds32_pmu_platdata { +irqreturn_t (*handle_irq)(int irq,void *dev, +irq_handler_t pmu_handler); +int (*runtime_resume)(struct device *dev); +int (*runtime_suspend)(struct device *dev); +}; + +/* The events for a given PMU register set. */ +struct pmu_hw_events { +/* +* The events that are active on the PMU for the given index. +*/ +struct perf_event **events; + +/* +* A 1 bit for an index indicates that the counter is being used for arch/nds32/include/asm/pmu.h line 107
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+* an event. A 0 means that the counter can be used. +*/ +unsigned long *used_mask; + +/* +* Hardware lock to serialize accesses to PMU registers. Needed for the +* read/modify/write sequences. +*/ +raw_spinlock_t pmu_lock; +};It looks like this is derived from the really early
arm_pmucode, when we were trying to be far more flexible than was necessary. This can be simplified significantly.e.g. since you know up-front the max number of counters, this can be:
struct pmu_hw_events { struct perf_event *events[MAX_COUNTERS]; unsigned long mask[BITS_TO_LONGS(MAX_COUNTERS)]; raw_spinlock_t pmu_lock; };... and you can avoid having to dynamically allocate each field separately.
I'm not sure the
pmu_lockis even necessary in the asbence of NMI.arch/nds32/include/asm/pmu.h line 112
Quoted 5 lines
arch/nds32/include/asm/pmu.h new file +5
-0+ +struct nds32_pmu { +struct pmu pmu; +cpumask_t active_irqs; +cpumask_t supported_cpus;Do you intend to support heterogeneous systems (e.g big.LITTLE)?
Quoted 2 lines
If not, you don't need the
supported_cpusmask.
In nds32 V3, we only have one CPU. So we don't need it. Thanks!
Quoted 24 lines
Quoted 9 lines
arch/nds32/include/asm/pmu.h new file +9
-0+char *name; +irqreturn_t (*handle_irq)(int irq_num,void *dev); +void (*enable)(struct perf_event *event); +void (*disable)(struct perf_event *event); +int (*get_event_idx)(struct pmu_hw_events *hw_events, +struct perf_event *event); +int (*set_event_filter)(struct hw_perf_event *evt, +struct perf_event_attr *attr); +u32 (*read_counter)(struct perf_event *event);arch/nds32/include/asm/pmu.h line 131
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+void (*write_counter)(struct perf_event *event,u32 val); +void (*start)(struct nds32_pmu *nds32_pmu); +void (*stop)(struct nds32_pmu *nds32_pmu); +void (*reset)(void *data); +int (*request_irq)(struct nds32_pmu *nds32_pmu,irq_handler_t handler); +void (*free_irq)(struct nds32_pmu *nds32_pmu); +int (*map_event)(struct perf_event *event); +int num_events; +atomic_t active_events; +struct mutex reserve_mutex;/* mutex */ I don't think you need the
reserve_mutex; forarm_pmuthat was a holdover form when there were multiple frameworks competing to mange the PMU, and now there's only perf.
Quoted 7 lines
Quoted 1 line
+ u64
max_period;It looks like this is always
0xffffffff, so you don't need a variable for that.[...]
In arch/nds32/kernel/perf_event_cpu.c: nds32_pmu_event_set_period():
if (left > (s64)nds32_pmu->max_period)
left = nds32_pmu->max_period;
the variable 'max_period' looks better than
if (left > 0xffffffff)
left = 0xffffffff;
I think it makes code easy to understand.
Quoted 113 lines
arch/nds32/include/asm/pmu.h line 247
Quoted 13 lines
arch/nds32/include/asm/pmu.h new file +13
-0+/* Get converted event counter index */ +#define GET_CONVERTED_EVENT_IDX (event, idx) do { \ + if ((event) > SPAV3_0_SEL_BASE && event < SPAV3_0_SEL_LAST) { \ + idx = 0; \ + } else if ((event) > SPAV3_1_SEL_BASE && event < SPAV3_1_SEL_LAST) { \ + idx = 1; \ + } else if ((event) > SPAV3_2_SEL_BASE && event < SPAV3_2_SEL_LAST) { \ + idx = 2; \ + } else { \ + pr_err("GET_CONVERTED_EVENT_IDX PFM counter range error\n"); \ + return -EPERM; \ + } \ +} while (0)This would be cleaner as a static inline.
Quoted 6 lines
arch/nds32/include/asm/pmu.h new file +6
-0+ +/* Get converted hardware event number */ +#define GET_CONVERTED_EVENT_HW_NUM (event) do { \ + if ((event) == 0) { \ +/*do nothing*/ \ + } else if ((event) > SPAV3_0_SEL_BASE && event < SPAV3_0_SEL_LAST) { \arch/nds32/include/asm/pmu.h line 263
Quoted 10 lines
arch/nds32/include/asm/pmu.h new file +10
-0+ (event) -=PFM_OFFSET_MAGIC_0 ; \ + }else if ((event) >SPAV3_1_SEL_BASE && event <SPAV3_1_SEL_LAST ) { \ + (event) -=PFM_OFFSET_MAGIC_1 ; \ + }else if ((event) >SPAV3_2_SEL_BASE && event <SPAV3_2_SEL_LAST ) { \ + (event) -=PFM_OFFSET_MAGIC_2 ; \ + }else { \ +pr_err (\ +"GET_CONVERTED_EVENT_HW_NUM PFM counter range error\n" ); \ + } \ +}while (0 )Likewise, a static inline would be cleaner.
[...]
arch/nds32/kernel/perf_event_cpu.c line 30
Quoted 6 lines
arch/nds32/kernel/perf_event_cpu.c new file +6
-0+/* Set at runtime when we know what CPU type we are. */ +static struct nds32_pmu *cpu_pmu; + +static DEFINE_PER_CPU(struct perf_event * [MAX_COUNTERS], hw_events); +static DEFINE_PER_CPU(unsigned long[BITS_TO_LONGS(MAX_COUNTERS)], used_mask); +static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events);As before, I think you can fold the first wo fields into
pmu_hw_events, then you only need oneDEFINE_PER_CPU()here.[...]
Quoted 50 lines
arch/nds32/kernel/perf_event_cpu.c new file +50
-0+static irqreturn_t nds32_pmu_handle_irq (int irq_num,void *dev) +{ +u32 pfm; +struct perf_sample_data data; +struct nds32_pmu *cpu_pmu = (struct nds32_pmu *)dev; +struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events (); +struct pt_regs *regs; +int idx; +/* +* Get and reset the IRQ flags +*/ + pfm =nds32_pfm_getreset_flags (); + +/* +* Did an overflow occur? +*/ +if (!nds32_pfm_has_overflowed (pfm)) +return IRQ_NONE ; + +/* +* Handle the counter(s) overflow(s) +*/ + regs =get_irq_regs (); + +for (idx =0 ; idx < cpu_pmu->num_events; ++idx) { +struct perf_event *event = cpuc->events[idx]; +struct hw_perf_event *hwc; + +/* Ignore if we don't have an event. */ +if (!event) +continue ; + +/* +* We have a single interrupt for all counters. Check that +* each counter has overflowed before we process it. +*/ +if (!nds32_pfm_counter_has_overflowed (pfm, idx)) +continue ; + + hwc = &event->hw; +nds32_pmu_event_update (event); +perf_sample_data_init (&data,0 , hwc->last_period); +if (!nds32_pmu_event_set_period (event)) +continue ; + +if (perf_event_overflow (event, &data, regs)) + cpu_pmu->disable (event); + } + +/* arch/nds32/kernel/perf_event_cpu.c line 280
Quoted 10 lines
arch/nds32/kernel/perf_event_cpu.c new file +10
-0+* Handle the pending perf events. +* +* Note: this call *must* be run with interrupts disabled. For +* platforms that can have the PMU interrupts raised as an NMI, this +* will not work. +*/ +irq_work_run (); + +return IRQ_HANDLED ; +}You'll want to stop all counters over the IRQ handler to ensure that groups are consistent.
Quoted 2 lines
[...]
Our implementation of IRQ handler references the irq_handler of arch/arm/kernel/perf_event_v7.c.
Do you mean that when one counter overflowed, other counters do not count the events caused by the counter overflow handler?
Can you explain more specifically that why groups will not consistent?
Thanks!
Quoted 64 lines
arch/nds32/kernel/perf_event_cpu.c line 655
Quoted 8 lines
arch/nds32/kernel/perf_event_cpu.c new file +8
-0+static int device_pmu_init (struct nds32_pmu *cpu_pmu) +{ +nds32_pmu_init (cpu_pmu); +/* +* This name should be devive-specific name, whatever you like :) +* I think "PMU" will be a good generic name. +*/ + cpu_pmu->name ="atcpmu" ;This looks like it should have a more complete version string, e.g. with "spav3" in it.
[...]
arch/nds32/kernel/perf_event_cpu.c line 760
Quoted 6 lines
arch/nds32/kernel/perf_event_cpu.c new file +6
-0+static int +validate_event (struct pmu *pmu,struct pmu_hw_events *hw_events, +struct perf_event *event) +{ +struct nds32_pmu *nds32_pmu =to_nds32_pmu (event->pmu); +//struct pmu *leader_pmu = event->group_leader->pmu; Code which is commented out should be deleted.
[...]
Quoted 39 lines
arch/nds32/kernel/perf_event_cpu.c new file +39
-0+static int cpu_pmu_request_irq (struct nds32_pmu *cpu_pmu,irq_handler_t handler) +{ +int i, err, irq, irqs; +struct platform_device *pmu_device = cpu_pmu->plat_device; + +if (!pmu_device) +return -ENODEV ; + + irqs =min (pmu_device->num_resources,num_possible_cpus ()); +if (irqs <1 ) { +pr_err ("no irqs for PMUs defined\n" ); +return -ENODEV ; + } + +for (i =0 ; i < irqs; ++i) { + err =0 ; + irq =platform_get_irq (pmu_device, i); +if (irq <0 ) +continue ; + +/* +* If we have a single PMU interrupt that we can't shift, +* assume that we're running on a uniprocessor machine and +* continue. Otherwise, continue without this interrupt. +*/ +if (irq_set_affinity (irq,cpumask_of (i)) && irqs >1 ) { +pr_warning + ("unable to set irq affinity (irq=%d, cpu=%u)\n" , + irq, i); +continue ; + } + + err =request_irq (irq, handler,IRQF_NOBALANCING ,"nds32-pfm" , + cpu_pmu); +if (err) { +pr_err ("unable to request IRQ%d for NDS PMU counters\n" , + irq); +return err; + }
Quoted 9 lines
arch/nds32/kernel/perf_event_cpu.c line 1130
Quoted 6 lines
arch/nds32/kernel/perf_event_cpu.c new file +6
-0+ +cpumask_set_cpu (i, &cpu_pmu->active_irqs); + } + +return 0 ; +}Is nds32 SMP?
No, we only have one cpu in nds32 V3.
I will simplifed it in next patch.
Thanks!
Quoted 8 lines
If not, this should be simplifed to expect one CPU alone.
If so, this needs to clarify which IRQ maps to which CPU with an explicit property in the DT, as we do for arm with interrupt-affinity.
Thanks,
Mark.
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
Thank you for the quick reply. Your advices help a lots. I will send another patch for it.
Mark Rutland
reviewerRe: [PATCH 1/5] nds32: Perf porting
Quoted 67 lines · Nick Hu
Quoted 66 lines · Mark Rutland
Quoted 50 lines · Nickhu
arch/nds32/kernel/perf_event_cpu.c new file +50
-0+static irqreturn_t nds32_pmu_handle_irq (int irq_num,void *dev) +{ +u32 pfm; +struct perf_sample_data data; +struct nds32_pmu *cpu_pmu = (struct nds32_pmu *)dev; +struct pmu_hw_events *cpuc = cpu_pmu->get_hw_events (); +struct pt_regs *regs; +int idx; +/* +* Get and reset the IRQ flags +*/ + pfm =nds32_pfm_getreset_flags (); + +/* +* Did an overflow occur? +*/ +if (!nds32_pfm_has_overflowed (pfm)) +return IRQ_NONE ; + +/* +* Handle the counter(s) overflow(s) +*/ + regs =get_irq_regs (); + +for (idx =0 ; idx < cpu_pmu->num_events; ++idx) { +struct perf_event *event = cpuc->events[idx]; +struct hw_perf_event *hwc; + +/* Ignore if we don't have an event. */ +if (!event) +continue ; + +/* +* We have a single interrupt for all counters. Check that +* each counter has overflowed before we process it. +*/ +if (!nds32_pfm_counter_has_overflowed (pfm, idx)) +continue ; + + hwc = &event->hw; +nds32_pmu_event_update (event); +perf_sample_data_init (&data,0 , hwc->last_period); +if (!nds32_pmu_event_set_period (event)) +continue ; + +if (perf_event_overflow (event, &data, regs)) + cpu_pmu->disable (event); + } + +/* arch/nds32/kernel/perf_event_cpu.c line 280
Quoted 10 lines
arch/nds32/kernel/perf_event_cpu.c new file +10
-0+* Handle the pending perf events. +* +* Note: this call *must* be run with interrupts disabled. For +* platforms that can have the PMU interrupts raised as an NMI, this +* will not work. +*/ +irq_work_run (); + +return IRQ_HANDLED ; +}You'll want to stop all counters over the IRQ handler to ensure that groups are consistent.
Quoted 4 lines
Quoted 1 line
[...]
Our implementation of IRQ handler references the
irq_handlerofarch/arm/kernel/perf_event_v7.c.
Ah, it looks like we forgot to fix that up when we fixed arm64 in commit:
3cce50dfec4a5b04 ("arm64: perf: Disable PMU while processing counter overflows")
... we should do that for all the arch/arm PMU IRQ handlers.
Quoted 2 lines
Do you mean that when one counter overflowed, other counters do not count the events caused by the counter overflow handler?
The problem is that counters are still counting while they're being read/modified, so there is skew.
Groups of counters should be scheduled atomically (i.e. at any point in time, all counters are counting, or all are not counting). This isn't true if they're read one-by-one in the IRQ handler (and maybe reprogrammed afterwards).
Consider a group of three events, programmed with the same event (e.g. bus-cycles). You would expect that all three events read the same value, since they're identical.
In the IRQ handler we do something like:
count0 = read_counter(0); // reads N < X bus cycles occur >
count1 = read_counter(1); // reads N + X < Y bus cycles occur >
count2 = read_counter(2); // reads N + X + Y
We read the first counter, and before we read the next counter, more events occur. Likewise for the next pair of counters. This leads to surprising results, especially if counters are reprogrammed on overflow.
That means that you can't necessarily derive a meaningful ratio between counter values.
The simplest way to solve this is to stop the PMU before reading the counters, and start it again afterwards. It leaves a small window where the counters aren't counting, but it ensures the ratio between counters is meaningful.
See commit:
3cce50dfec4a5b04 ("arm64: perf: Disable PMU while processing counter overflows")
... for how we did that on arm64.
Thanks,
Mark.
PATCH 2/5 nds32: Fix bug in bitfield.h
In reply to NickhuThere two bitfield bug for perfomance counter
in bitfield.h:
PFM_CTL_offSEL1 21 --> 16 PFM_CTL_offSEL2 27 --> 22
This commit fix it.
--- arch/nds32/include/asm/bitfield.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/nds32/include/asm/bitfield.h +2 -2
@@ -692,8 +692,8 @@#define PFM_CTL_offKU1 13/* Enable user mode event counting for PFMC1 */ #define PFM_CTL_offKU2 14/* Enable user mode event counting for PFMC2 */ #define PFM_CTL_offSEL0 15/* The event selection for PFMC0 */ -#define PFM_CTL_offSEL1 21/* The event selection for PFMC1 */ -#define PFM_CTL_offSEL2 27/* The event selection for PFMC2 */ +#define PFM_CTL_offSEL1 16/* The event selection for PFMC1 */ +#define PFM_CTL_offSEL2 22/* The event selection for PFMC2 */ /* bit 28:31 reserved */ #define PFM_CTL_mskEN0 ( 0x01 << PFM_CTL_offEN0 )
Signature · 1 line
2.17.0
Mark Rutland
reviewerRe: [PATCH 2/5] nds32: Fix bug in bitfield.h
Quoted 9 lines · Nickhu
There two bitfield bug for perfomance counter
inbitfield.h:PFM_CTL_offSEL1 21 --> 16 PFM_CTL_offSEL2 27 --> 22This commit fix it.
Signed-off-by Nickhu <[email protected]>
This patch should probably be move before the patch adding perf support. That way, perf support isn't broken at the point it is added.
Thanks,
Mark.
Quoted 22 lines
--- arch/nds32/include/asm/bitfield.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)arch/nds32/include/asm/bitfield.h +2
-2@@ -692,8 +692,8 @@#define PFM_CTL_offKU1 13/* Enable user mode event counting for PFMC1 */ #define PFM_CTL_offKU2 14/* Enable user mode event counting for PFMC2 */ #define PFM_CTL_offSEL0 15/* The event selection for PFMC0 */ -#define PFM_CTL_offSEL1 21/* The event selection for PFMC1 */ -#define PFM_CTL_offSEL2 27/* The event selection for PFMC2 */ +#define PFM_CTL_offSEL1 16/* The event selection for PFMC1 */ +#define PFM_CTL_offSEL2 22/* The event selection for PFMC2 */ /* bit 28:31 reserved */ #define PFM_CTL_mskEN0 ( 0x01 << PFM_CTL_offEN0 )Signature · 1 line
2.17.0
Re: [PATCH 2/5] nds32: Fix bug in bitfield.h
Hi Mark,
Quoted 35 lines · Mark Rutland
Quoted 9 lines · Nickhu
There two bitfield bug for perfomance counter
inbitfield.h:PFM_CTL_offSEL1 21 --> 16 PFM_CTL_offSEL2 27 --> 22This commit fix it.
Signed-off-by Nickhu <[email protected]>This patch should probably be move before the patch adding perf support. That way, perf support isn't broken at the point it is added.
Thanks,
Mark.Quoted 18 lines
--- arch/nds32/include/asm/bitfield.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)arch/nds32/include/asm/bitfield.h +2
-2@@ -692,8 +692,8 @@#define PFM_CTL_offKU1 13/* Enable user mode event counting for PFMC1 */ #define PFM_CTL_offKU2 14/* Enable user mode event counting for PFMC2 */ #define PFM_CTL_offSEL0 15/* The event selection for PFMC0 */ -#define PFM_CTL_offSEL1 21/* The event selection for PFMC1 */ -#define PFM_CTL_offSEL2 27/* The event selection for PFMC2 */ +#define PFM_CTL_offSEL1 16/* The event selection for PFMC1 */ +#define PFM_CTL_offSEL2 22/* The event selection for PFMC2 */ /* bit 28:31 reserved */
Quoted 4 lines
Quoted 4 lines
arch/nds32/include/asm/bitfield.h +0
-0#define PFM_CTL_mskEN0 ( 0x01 << PFM_CTL_offEN0 )Signature · 1 line
2.17.0
Thank you for the advice !!
I will prepare another patch for it.
PATCH 3/5 nds32: Add perf call-graph support.
In reply to NickhuThe perf call-graph option can trace the callchain between functions. This commit add the perf callchain for nds32. There are kerenl callchain and user callchain. The kerenl callchain can trace the function in kernel space. There are two type for user callchain. One for the 'optimize for size' config is set, and another one for the config is not set. The difference between two types is that the index of frame-pointer in user stack is not the same.
For example:
With optimize for size: User Stack: --------- | lp | --------- | gp | --------- | fp | Without optimize for size: User Stack: 1. non-leaf function: --------- | lp | --------- | fp | 2. leaf function: --------- | fp |
--- arch/nds32/kernel/perf_event_cpu.c | 299 +++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+)
arch/nds32/kernel/perf_event_cpu.c +299 -0
@@ -1240,6 +1240,305 @@ static int __init register_pmu_driver(void)device_initcall (register_pmu_driver); +/* +* References: arch/nds32/kernel/traps.c:__dump() +* You will need to know the NDS ABI first. +*/ +static int unwind_frame_kernel (struct stackframe *frame) +{ +int graph =0 ; +#ifdef CONFIG_FRAME_POINTER +/* 0x3 means misalignment */ +if (!kstack_end ((void *)frame->fp) && + !((unsigned long )frame->fp &0x3 ) && + ((unsigned long )frame->fp >=TASK_SIZE )) { +/* +* The array index is based on the ABI, the below graph +* illustrate the reasons. +* Function call procedure: "smw" and "lmw" will always +* update SP and FP for you automatically. +* +* Stack Relative Address +* | | 0 +* ---- +* |LP| <-- SP(before smw) <-- FP(after smw) -1 +* ---- +* |FP| -2 +* ---- +* | | <-- SP(after smw) -3 +*/ + frame->lp = ((unsigned long *)frame->fp)[-1 ]; + frame->fp = ((unsigned long *)frame->fp)[FP_OFFSET ]; +/* make sure CONFIG_FUNCTION_GRAPH_TRACER is turned on */ +if (__kernel_text_address (frame->lp)) + frame->lp =ftrace_graph_ret_addr + (NULL , &graph, frame->lp,NULL ); + +return 0 ; + }else { +return -EPERM ; + } +#else +/* +* You can refer to arch/nds32/kernel/traps.c:__dump() +* Treat "sp" as "fp", but the "sp" is one frame ahead of "fp". +* And, the "sp" is not always correct. +* +* Stack Relative Address +* | | 0 +* ---- +* |LP| <-- SP(before smw) -1 +* ---- +* | | <-- SP(after smw) -2 +* ---- +*/ +if (!kstack_end ((void *)frame->sp)) { + frame->lp = ((unsigned long *)frame->sp)[1 ]; +/* TODO: How to deal with the value in first +* "sp" is not correct? +*/ +if (__kernel_text_address (frame->lp)) + frame->lp =ftrace_graph_ret_addr + (tsk, &graph, frame->lp,NULL ); + + frame->sp = ((unsigned long *)frame->sp) +1 ; + +return 0 ; + }else { +return -EPERM ; + } +#endif +} + +static void notrace +walk_stackframe (struct stackframe *frame, +int (*fn_record)(struct stackframe *,void *), +void *data) +{ +while (1 ) { +int ret; + +if (fn_record (frame, data)) +break ; + + ret =unwind_frame_kernel (frame); +if (ret <0 ) +break ; + } +} + +/* +* Gets called by walk_stackframe() for every stackframe. This will be called +* whist unwinding the stackframe and is like a subroutine return so we use +* the PC. +*/ +static int callchain_trace (struct stackframe *fr,void *data) +{ +struct perf_callchain_entry_ctx *entry = data; + +perf_callchain_store (entry, fr->lp); +return 0 ; +} + +/* +* Get the return address for a single stackframe and return a pointer to the +* next frame tail. +*/ +static unsigned long +user_backtrace (struct perf_callchain_entry_ctx *entry,unsigned long fp) +{ +struct frame_tail buftail; +unsigned long lp =0 ; +unsigned long *user_frame_tail = + (unsigned long *)(fp - (unsigned long )sizeof (buftail)); + +/* Check accessibility of one struct frame_tail beyond */ +if (!access_ok (VERIFY_READ , user_frame_tail,sizeof (buftail))) +return 0 ; +if (__copy_from_user_inatomic + (&buftail, user_frame_tail,sizeof (buftail))) +return 0 ; + +/* +* Refer to unwind_frame_kernel() for more illurstration +*/ + lp = buftail.stack_lp;/* ((unsigned long *)fp)[-1] */ + fp = buftail.stack_fp;/* ((unsigned long *)fp)[FP_OFFSET] */ +perf_callchain_store (entry, lp); +return fp; +} + +static unsigned long +user_backtrace_opt_size (struct perf_callchain_entry_ctx *entry, +unsigned long fp) +{ +struct frame_tail_opt_size buftail; +unsigned long lp =0 ; + +unsigned long *user_frame_tail = + (unsigned long *)(fp - (unsigned long )sizeof (buftail)); + +/* Check accessibility of one struct frame_tail beyond */ +if (!access_ok (VERIFY_READ , user_frame_tail,sizeof (buftail))) +return 0 ; +if (__copy_from_user_inatomic + (&buftail, user_frame_tail,sizeof (buftail))) +return 0 ; + +/* +* Refer to unwind_frame_kernel() for more illurstration +*/ + lp = buftail.stack_lp;/* ((unsigned long *)fp)[-1] */ + fp = buftail.stack_fp;/* ((unsigned long *)fp)[FP_OFFSET] */ + +perf_callchain_store (entry, lp); +return fp; +} + +/* +* This will be called when the target is in user mode +* This function will only be called when we use +* "PERF_SAMPLE_CALLCHAIN" in +* kernel/events/core.c:perf_prepare_sample() +* +* How to trigger perf_callchain_[user/kernel] : +* $ perf record -e cpu-clock --call-graph fp ./program +* $ perf report --call-graph +*/ +unsigned long leaf_fp; +void +perf_callchain_user (struct perf_callchain_entry_ctx *entry, +struct pt_regs *regs) +{ +unsigned long fp =0 ; +unsigned long gp =0 ; +unsigned long lp =0 ; +unsigned long sp =0 ; +unsigned long *user_frame_tail; + + leaf_fp =0 ; + +if (perf_guest_cbs && perf_guest_cbs->is_in_guest ()) { +/* We don't support guest os callchain now */ +return ; + } + +perf_callchain_store (entry, regs->ipc); + fp = regs->fp; + gp = regs->gp; + lp = regs->lp; + sp = regs->sp; +if (entry->nr <PERF_MAX_STACK_DEPTH && + (unsigned long )fp && !((unsigned long )fp &0x7 ) && fp > sp) { + user_frame_tail = + (unsigned long *)(fp - (unsigned long )sizeof (fp)); + +if (!access_ok (VERIFY_READ , user_frame_tail,sizeof (fp))) +return ; + +if (__copy_from_user_inatomic + (&leaf_fp, user_frame_tail,sizeof (fp))) +return ; + +if (leaf_fp == lp) { +/* +* Maybe this is non leaf function +* with optimize for size, +* or maybe this is the function +* with optimize for size +*/ +struct frame_tail buftail; + + user_frame_tail = + (unsigned long *)(fp - + (unsigned long )sizeof (buftail)); + +if (!access_ok + (VERIFY_READ , user_frame_tail,sizeof (buftail))) +return ; + +if (__copy_from_user_inatomic + (&buftail, user_frame_tail,sizeof (buftail))) +return ; + +if (buftail.stack_fp == gp) { +/* non leaf function with optimize +* for size condition +*/ +struct frame_tail_opt_size buftail_opt_size; + + user_frame_tail = + (unsigned long *)(fp - (unsigned long ) +sizeof (buftail_opt_size)); + +if (!access_ok (VERIFY_READ , user_frame_tail, +sizeof (buftail_opt_size))) +return ; + +if (__copy_from_user_inatomic + (&buftail_opt_size, user_frame_tail, +sizeof (buftail_opt_size))) +return ; + +perf_callchain_store (entry, lp); + fp = buftail_opt_size.stack_fp; + +while ((entry->nr <PERF_MAX_STACK_DEPTH ) && + (unsigned long )fp && + !((unsigned long )fp &0x7 ) && + fp > sp) { + sp = fp; + fp =user_backtrace_opt_size (entry, fp); + } + + }else { +/* this is the function +* without optimize for size +*/ + fp = buftail.stack_fp; +perf_callchain_store (entry, lp); +while ((entry->nr <PERF_MAX_STACK_DEPTH ) && + (unsigned long )fp && + !((unsigned long )fp &0x7 ) && + fp > sp) { + sp = fp; + fp =user_backtrace (entry, fp); + } + } + }else { +/* this is leaf function */ + fp = leaf_fp; +perf_callchain_store (entry, lp); + +/* previous function callcahin */ +while ((entry->nr <PERF_MAX_STACK_DEPTH ) && + (unsigned long )fp && + !((unsigned long )fp &0x7 ) && fp > sp) { + sp = fp; + fp =user_backtrace (entry, fp); + } + } +return ; + } +} + +/* This will be called when the target is in kernel mode */ +void +perf_callchain_kernel (struct perf_callchain_entry_ctx *entry, +struct pt_regs *regs) +{ +struct stackframe fr; + +if (perf_guest_cbs && perf_guest_cbs->is_in_guest ()) { +/* We don't support guest os callchain now */ +return ; + } + fr.fp = regs->fp; + fr.lp = regs->lp; + fr.sp = regs->sp; +walk_stackframe (&fr, callchain_trace, entry); +} +unsigned long perf_instruction_pointer (struct pt_regs *regs) {/* However, NDS32 does not support virtualization */
Signature · 1 line
2.17.0
PATCH 4/5 nds32: Fix perf multiple events map to same counter.
In reply to NickhuWhen there are multiple events map to the same counter, the counter counts inaccurately. This is because each counter only counts one event in the same time.
So when there are multiple events map to same counter, they have to take turns in each context.
There are two solution:
1. Print the error message when multiple events map to the same counter. But print the error message would let the program hang in loop. The ltp (linux test program) would be failed when the program hang in loop.
2. Don't print the error message, the ltp would pass. But the user need to have the knowledge that don't count the events which map to the same counter, or the user will get the inaccurate results.
We choose method 2 for the solution
--- arch/nds32/include/asm/pmu.h | 1 + arch/nds32/kernel/perf_event_cpu.c | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-)
arch/nds32/include/asm/pmu.h +1 -0
@@ -55,6 +55,7 @@ enum { PFMC0, PFMC1, PFMC2, MAX_COUNTERS };*/ #define NDS32_IDX_CYCLE_COUNTER 0#define NDS32_IDX_COUNTER0 1 +#define NDS32_IDX_COUNTER1 2#define NDS32_IDX_COUNTER_LAST (cpu_pmu) \ (NDS32_IDX_CYCLE_COUNTER + (cpu_pmu)->num_events - 1)
arch/nds32/kernel/perf_event_cpu.c +20 -10
@@ -566,16 +566,26 @@ static int nds32_pmu_get_event_idx(struct pmu_hw_events *cpuc,/* * Try to get the counter for correpsonding event */ -if (!test_and_set_bit (idx, cpuc->used_mask)) -return idx; - -/* -* The counter is in use. -* The system will hang in the loop. -*/ -pr_err - ("Multiple events map to one counter, the behavior is undefined.\n" ); -return -EPERM ; +if (evtype ==SPAV3_0_SEL_TOTAL_CYCLES ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +if (!test_and_set_bit (NDS32_IDX_COUNTER0 , cpuc->used_mask)) +return NDS32_IDX_COUNTER0 ; +if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; + }else if (evtype ==SPAV3_1_SEL_COMPLETED_INSTRUCTION ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +else if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; +else if (!test_and_set_bit + (NDS32_IDX_CYCLE_COUNTER , cpuc->used_mask)) +return NDS32_IDX_CYCLE_COUNTER ; + }else { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; + } +return -EAGAIN ; }static void nds32_pmu_start (struct nds32_pmu *cpu_pmu)
Signature · 1 line
2.17.0
Mark Rutland
reviewerRe: [PATCH 4/5] nds32: Fix perf multiple events map to same counter.
Quoted 11 lines · Nickhu
When there are multiple events map to the same counter, the counter counts inaccurately. This is because each counter only counts one event in the same time.
So when there are multiple events map to same counter, they have to take turns in each context.There are two solution:
1. Print the error message when multiple events map to the same counter. But print the error message would let the program hang in loop. The ltp (linux test program) would be failed when the program hang in loop.
Quoted 5 lines
2. Don't print the error message, the ltp would pass. But the user need to have the knowledge that don't count the events which map to the same counter, or the user will get the inaccurate results.
We choose method 2 for the solution
This is the correct solution. Perf exposes the active/enabled time in the perf event, so the user can determine that the event wasn't enabled all of the time.
This should be folded into the commit adding perf support.
Thanks,
Mark.
Quoted 63 lines
Signed-off-by Nickhu <[email protected]>--- arch/nds32/include/asm/pmu.h | 1 + arch/nds32/kernel/perf_event_cpu.c | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-)arch/nds32/include/asm/pmu.h +1
-0@@ -55,6 +55,7 @@ enum { PFMC0, PFMC1, PFMC2, MAX_COUNTERS };*/ #define NDS32_IDX_CYCLE_COUNTER 0#define NDS32_IDX_COUNTER0 1 +#define NDS32_IDX_COUNTER1 2#define NDS32_IDX_COUNTER_LAST (cpu_pmu) \ (NDS32_IDX_CYCLE_COUNTER + (cpu_pmu)->num_events - 1)arch/nds32/kernel/perf_event_cpu.c +20
-10@@ -566,16 +566,26 @@ static int nds32_pmu_get_event_idx(struct pmu_hw_events *cpuc,/* * Try to get the counter for correpsonding event */ -if (!test_and_set_bit (idx, cpuc->used_mask)) -return idx; - -/* -* The counter is in use. -* The system will hang in the loop. -*/ -pr_err - ("Multiple events map to one counter, the behavior is undefined.\n" ); -return -EPERM ; +if (evtype ==SPAV3_0_SEL_TOTAL_CYCLES ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +if (!test_and_set_bit (NDS32_IDX_COUNTER0 , cpuc->used_mask)) +return NDS32_IDX_COUNTER0 ; +if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; + }else if (evtype ==SPAV3_1_SEL_COMPLETED_INSTRUCTION ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +else if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; +else if (!test_and_set_bit + (NDS32_IDX_CYCLE_COUNTER , cpuc->used_mask)) +return NDS32_IDX_CYCLE_COUNTER ; + }else { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; + } +return -EAGAIN ; }static void nds32_pmu_start (struct nds32_pmu *cpu_pmu)Signature · 1 line
2.17.0
Re: [PATCH 4/5] nds32: Fix perf multiple events map to same counter.
Hi Mark,
Quoted 86 lines · Mark Rutland
Quoted 11 lines · Nickhu
When there are multiple events map to the same counter, the counter counts inaccurately. This is because each counter only counts one event in the same time.
So when there are multiple events map to same counter, they have to take turns in each context.There are two solution:
1. Print the error message when multiple events map to the same counter. But print the error message would let the program hang in loop. The ltp (linux test program) would be failed when the program hang in loop.Quoted 5 lines
2. Don't print the error message, the ltp would pass. But the user need to have the knowledge that don't count the events which map to the same counter, or the user will get the inaccurate results.
We choose method 2 for the solution
This is the correct solution. Perf exposes the active/enabled time in the perf event, so the user can determine that the event wasn't enabled all of the time.
This should be folded into the commit adding perf support.
Thanks,
Mark.Quoted 59 lines
Signed-off-by Nickhu <[email protected]>--- arch/nds32/include/asm/pmu.h | 1 + arch/nds32/kernel/perf_event_cpu.c | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-)arch/nds32/include/asm/pmu.h +1
-0@@ -55,6 +55,7 @@ enum { PFMC0, PFMC1, PFMC2, MAX_COUNTERS };*/ #define NDS32_IDX_CYCLE_COUNTER 0#define NDS32_IDX_COUNTER0 1 +#define NDS32_IDX_COUNTER1 2#define NDS32_IDX_COUNTER_LAST (cpu_pmu) \ (NDS32_IDX_CYCLE_COUNTER + (cpu_pmu)->num_events - 1)arch/nds32/kernel/perf_event_cpu.c +20
-10@@ -566,16 +566,26 @@ static int nds32_pmu_get_event_idx(struct pmu_hw_events *cpuc,/* * Try to get the counter for correpsonding event */ -if (!test_and_set_bit (idx, cpuc->used_mask)) -return idx; - -/* -* The counter is in use. -* The system will hang in the loop. -*/ -pr_err - ("Multiple events map to one counter, the behavior is undefined.\n" ); -return -EPERM ; +if (evtype ==SPAV3_0_SEL_TOTAL_CYCLES ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +if (!test_and_set_bit (NDS32_IDX_COUNTER0 , cpuc->used_mask)) +return NDS32_IDX_COUNTER0 ; +if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; + }else if (evtype ==SPAV3_1_SEL_COMPLETED_INSTRUCTION ) { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; +else if (!test_and_set_bit (NDS32_IDX_COUNTER1 , cpuc->used_mask)) +return NDS32_IDX_COUNTER1 ; +else if (!test_and_set_bit + (NDS32_IDX_CYCLE_COUNTER , cpuc->used_mask)) +return NDS32_IDX_CYCLE_COUNTER ; + }else { +if (!test_and_set_bit (idx, cpuc->used_mask)) +return idx; + } +return -EAGAIN ; }
Quoted 4 lines
Quoted 4 lines
arch/nds32/kernel/perf_event_cpu.c +0
-0static void nds32_pmu_start (struct nds32_pmu *cpu_pmu)Signature · 1 line
2.17.0
Thanks for the comment.
I will folded it into the commit adding perf support.
PATCH 5/5 nds32: Add document for NDS32 PMU.
In reply to NickhuThe document for how to add NDS32 PMU
in devicetree.
--- Documentation/devicetree/bindings/nds32/pmu.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/nds32/pmu.txt
Documentation/devicetree/bindings/nds32/pmu.txt new file +17 -0
@@ -0,0 +1,17 @@ +* NDS32 Performance Monitor Units + +NDS32 core have a PMU for counting cpu and cache events like cache misses. +The NDS32 PMU representation in the device tree should be done as under: + +Required properties: + +- compatilbe : + "andestech,atcpmu" + +- interrupts : The interrupt number for NDS32 PMU is 13. + +Example: +pmu{ + compatible = "andestech,atcpmu"; + interrupts = <13>; +}
Signature · 1 line
2.17.0
Mark Rutland
reviewerRe: [PATCH 5/5] nds32: Add document for NDS32 PMU.
Quoted 15 lines · Nickhu
The document for how to add NDS32 PMU
in devicetree.Signed-off-by Nickhu <[email protected]>--- Documentation/devicetree/bindings/nds32/pmu.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/nds32/pmu.txtDocumentation/devicetree/bindings/nds32/pmu.txt new file +0
-0
Documentation/devicetree/bindings/nds32/pmu.txt line 9
Quoted 9 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +9
-0@@ -0,0 +1,17 @@ +* NDS32 Performance Monitor Units + +NDS32 core have a PMU for counting cpu and cache events like cache misses. +The NDS32 PMU representation in the device tree should be done as under: + +Required properties: + +- compatilbe : + "andestech,atcpmu"
Which CPUs have this PMU?
I expected more specific strings, e.g. "andestech,n13-pmu" and/or "andestech,andestech,nds32v3-pmu".
Documentation/devicetree/bindings/nds32/pmu.txt line 17
Quoted 8 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +8
-0+ +- interrupts : The interrupt number for NDS32 PMU is 13. + +Example: +pmu{ + compatible = "andestech,atcpmu"; + interrupts = <13>; +}
The driver tried to find multiple interrupts. Is there only a single interrupt in all cases?
Thanks,
Mark.
Re: [PATCH 5/5] nds32: Add document for NDS32 PMU.
Hi Mark,
Quoted 28 lines · Mark Rutland
Quoted 15 lines · Nickhu
The document for how to add NDS32 PMU
in devicetree.Signed-off-by Nickhu <[email protected]>--- Documentation/devicetree/bindings/nds32/pmu.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/nds32/pmu.txtDocumentation/devicetree/bindings/nds32/pmu.txt new file +0
-0Documentation/devicetree/bindings/nds32/pmu.txt line 9
Quoted 9 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +9
-0@@ -0,0 +1,17 @@ +* NDS32 Performance Monitor Units + +NDS32 core have a PMU for counting cpu and cache events like cache misses. +The NDS32 PMU representation in the device tree should be done as under: + +Required properties: + +- compatilbe : + "andestech,atcpmu"Which CPUs have this PMU?
Quoted 3 lines
I expected more specific strings, e.g. "andestech,n13-pmu" and/or "andestech,andestech,nds32v3-pmu".
In nds32 V3, all of our CPU have PMU.
So I will change the string to andestech,nds32v3-pmu.
Quoted 12 lines
Documentation/devicetree/bindings/nds32/pmu.txt line 17
Quoted 8 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +8
-0+ +- interrupts : The interrupt number for NDS32 PMU is 13. + +Example: +pmu{ + compatible = "andestech,atcpmu"; + interrupts = <13>; +}The driver tried to find multiple interrupts. Is there only a single interrupt in all cases?
There is only overflow interrupt for performance couner in nds32 V3. I will modified the driver and prepare another patch. Thanks!
Quoted 2 lines
Thanks,
Mark.
Thank you for replying the patch set so quickly.
I will prepare the next patch set for it.
Thanks,
Nick
Mark Rutland
reviewerRe: [PATCH 5/5] nds32: Add document for NDS32 PMU.
Quoted 31 lines · Nick Hu
Hi Mark,
Quoted 28 lines · Mark Rutland
Quoted 15 lines · Nickhu
The document for how to add NDS32 PMU
in devicetree.Signed-off-by Nickhu <[email protected]>--- Documentation/devicetree/bindings/nds32/pmu.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/nds32/pmu.txtDocumentation/devicetree/bindings/nds32/pmu.txt new file +0
-0Documentation/devicetree/bindings/nds32/pmu.txt line 9
Quoted 9 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +9
-0@@ -0,0 +1,17 @@ +* NDS32 Performance Monitor Units + +NDS32 core have a PMU for counting cpu and cache events like cache misses. +The NDS32 PMU representation in the device tree should be done as under: + +Required properties: + +- compatilbe : + "andestech,atcpmu"Which CPUs have this PMU?
Quoted 5 lines
Quoted 3 lines
I expected more specific strings, e.g. "andestech,n13-pmu" and/or "andestech,andestech,nds32v3-pmu".
In nds32 V3, all of our CPU have PMU.
So I will change the string to andestech,nds32v3-pmu.
That sounds good to me; thanks.
Quoted 11 lines
Quoted 11 lines
Documentation/devicetree/bindings/nds32/pmu.txt line 17
Quoted 7 lines
Documentation/devicetree/bindings/nds32/pmu.txt new file +7
-0+- interrupts : The interrupt number for NDS32 PMU is 13. + +Example: +pmu{ + compatible = "andestech,atcpmu"; + interrupts = <13>; +}The driver tried to find multiple interrupts. Is there only a single interrupt in all cases?
Quoted 2 lines
There is only overflow interrupt for performance couner in nds32 V3. I will modified the driver and prepare another patch.
Ok.
Thanks,
Mark.