Skip to content

Commit

Permalink
Merge tag 'arm-perf-3.19' of git://git.kernel.org/pub/scm/linux/kerne…
Browse files Browse the repository at this point in the history
…l/git/will/linux into next/drivers

Pull "ARM: perf: updates for 3.19" from Will Deacon:

This patch series takes us slightly further on the road to big.LITTLE
support in perf. The main change enabling this is moving the CCI PMU
driver away from the arm-pmu abstraction, allowing the arch code to
focus specifically on support for CPU PMUs.

* tag 'arm-perf-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux:
  arm: perf: fold hotplug notifier into arm_pmu
  arm: perf: dynamically allocate cpu hardware data
  arm: perf: fold percpu_pmu into pmu_hw_events
  arm: perf: kill get_hw_events()
  arm: perf: limit size of accounting data
  arm: perf: use IDR types for CPU PMUs
  arm: perf: make PMU probing data-driven
  arm: perf: add missing pr_info newlines
  arm: perf: factor out callchain code
  ARM: perf: use pr_* instead of printk
  ARM: perf: remove useless return and check of idx in counter handling
  bus: cci: move away from arm_pmu framework

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
  • Loading branch information
Arnd Bergmann committed Nov 20, 2014
2 parents c3e6dc6 + af66abf commit b9e0e5a
Show file tree
Hide file tree
Showing 10 changed files with 772 additions and 403 deletions.
2 changes: 1 addition & 1 deletion arch/arm/include/asm/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#ifndef __ARM_PERF_EVENT_H__
#define __ARM_PERF_EVENT_H__

#ifdef CONFIG_HW_PERF_EVENTS
#ifdef CONFIG_PERF_EVENTS
struct pt_regs;
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
extern unsigned long perf_misc_flags(struct pt_regs *regs);
Expand Down
36 changes: 33 additions & 3 deletions arch/arm/include/asm/pmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <linux/interrupt.h>
#include <linux/perf_event.h>

#include <asm/cputype.h>

/*
* struct arm_pmu_platdata - ARM PMU platform data
*
Expand Down Expand Up @@ -66,19 +68,25 @@ struct pmu_hw_events {
/*
* The events that are active on the PMU for the given index.
*/
struct perf_event **events;
struct perf_event *events[ARMPMU_MAX_HWEVENTS];

/*
* A 1 bit for an index indicates that the counter is being used for
* an event. A 0 means that the counter can be used.
*/
unsigned long *used_mask;
DECLARE_BITMAP(used_mask, ARMPMU_MAX_HWEVENTS);

/*
* Hardware lock to serialize accesses to PMU registers. Needed for the
* read/modify/write sequences.
*/
raw_spinlock_t pmu_lock;

/*
* When using percpu IRQs, we need a percpu dev_id. Place it here as we
* already have to allocate this struct per cpu.
*/
struct arm_pmu *percpu_pmu;
};

struct arm_pmu {
Expand Down Expand Up @@ -107,7 +115,8 @@ struct arm_pmu {
struct mutex reserve_mutex;
u64 max_period;
struct platform_device *plat_device;
struct pmu_hw_events *(*get_hw_events)(void);
struct pmu_hw_events __percpu *hw_events;
struct notifier_block hotplug_nb;
};

#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
Expand All @@ -127,6 +136,27 @@ int armpmu_map_event(struct perf_event *event,
[PERF_COUNT_HW_CACHE_RESULT_MAX],
u32 raw_event_mask);

struct pmu_probe_info {
unsigned int cpuid;
unsigned int mask;
int (*init)(struct arm_pmu *);
};

#define PMU_PROBE(_cpuid, _mask, _fn) \
{ \
.cpuid = (_cpuid), \
.mask = (_mask), \
.init = (_fn), \
}

#define ARM_PMU_PROBE(_cpuid, _fn) \
PMU_PROBE(_cpuid, ARM_CPU_PART_MASK, _fn)

#define ARM_PMU_XSCALE_MASK ((0xff << 24) | ARM_CPU_XSCALE_ARCH_MASK)

#define XSCALE_PMU_PROBE(_version, _fn) \
PMU_PROBE(ARM_CPU_IMP_INTEL << 24 | _version, ARM_PMU_XSCALE_MASK, _fn)

#endif /* CONFIG_HW_PERF_EVENTS */

#endif /* __ARM_PMU_H__ */
2 changes: 1 addition & 1 deletion arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
obj-$(CONFIG_CPU_PJ4B) += pj4-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
Expand Down
136 changes: 136 additions & 0 deletions arch/arm/kernel/perf_callchain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* ARM callchain support
*
* Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles
* Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com>
*
* This code is based on the ARM OProfile backtrace code.
*/
#include <linux/perf_event.h>
#include <linux/uaccess.h>

#include <asm/stacktrace.h>

/*
* The registers we're interested in are at the end of the variable
* length saved register structure. The fp points at the end of this
* structure so the address of this struct is:
* (struct frame_tail *)(xxx->fp)-1
*
* This code has been adapted from the ARM OProfile support.
*/
struct frame_tail {
struct frame_tail __user *fp;
unsigned long sp;
unsigned long lr;
} __attribute__((packed));

/*
* Get the return address for a single stackframe and return a pointer to the
* next frame tail.
*/
static struct frame_tail __user *
user_backtrace(struct frame_tail __user *tail,
struct perf_callchain_entry *entry)
{
struct frame_tail buftail;
unsigned long err;

if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
return NULL;

pagefault_disable();
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
pagefault_enable();

if (err)
return NULL;

perf_callchain_store(entry, buftail.lr);

/*
* Frame pointers should strictly progress back up the stack
* (towards higher addresses).
*/
if (tail + 1 >= buftail.fp)
return NULL;

return buftail.fp - 1;
}

void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
struct frame_tail __user *tail;

if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
/* We don't support guest os callchain now */
return;
}

perf_callchain_store(entry, regs->ARM_pc);

if (!current->mm)
return;

tail = (struct frame_tail __user *)regs->ARM_fp - 1;

while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, entry);
}

/*
* 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 *entry = data;
perf_callchain_store(entry, fr->pc);
return 0;
}

void
perf_callchain_kernel(struct perf_callchain_entry *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;
}

arm_get_current_stackframe(regs, &fr);
walk_stackframe(&fr, callchain_trace, entry);
}

unsigned long perf_instruction_pointer(struct pt_regs *regs)
{
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
return perf_guest_cbs->get_guest_ip();

return instruction_pointer(regs);
}

unsigned long perf_misc_flags(struct pt_regs *regs)
{
int misc = 0;

if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
if (perf_guest_cbs->is_user_mode())
misc |= PERF_RECORD_MISC_GUEST_USER;
else
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else {
if (user_mode(regs))
misc |= PERF_RECORD_MISC_USER;
else
misc |= PERF_RECORD_MISC_KERNEL;
}

return misc;
}
Loading

0 comments on commit b9e0e5a

Please sign in to comment.