2010-02-02 22:25:44 +03:00
# undef DEBUG
/*
* ARM performance counter support .
*
* Copyright ( C ) 2009 picoChip Designs , Ltd . , Jamie Iles
2010-11-13 22:04:32 +03:00
* Copyright ( C ) 2010 ARM Ltd . , Will Deacon < will . deacon @ arm . com >
2010-01-26 20:51:05 +03:00
*
2010-02-02 22:25:44 +03:00
* This code is based on the sparc64 perf event code , which is in turn based
2014-09-29 20:15:32 +04:00
* on the x86 code .
2010-02-02 22:25:44 +03:00
*/
# define pr_fmt(fmt) "hw perfevents: " fmt
2015-05-26 19:23:39 +03:00
# include <linux/bitmap.h>
2015-05-13 19:12:25 +03:00
# include <linux/cpumask.h>
2015-05-26 19:23:39 +03:00
# include <linux/export.h>
2010-02-02 22:25:44 +03:00
# include <linux/kernel.h>
2015-06-30 15:56:57 +03:00
# include <linux/of_device.h>
2015-07-06 14:23:53 +03:00
# include <linux/perf/arm_pmu.h>
2010-04-29 20:13:24 +04:00
# include <linux/platform_device.h>
2015-05-26 19:23:39 +03:00
# include <linux/slab.h>
# include <linux/spinlock.h>
2014-02-08 01:01:19 +04:00
# include <linux/irq.h>
# include <linux/irqdesc.h>
2010-02-02 22:25:44 +03:00
2015-05-26 19:23:39 +03:00
# include <asm/cputype.h>
2010-02-02 22:25:44 +03:00
# include <asm/irq_regs.h>
static int
2011-04-28 18:47:10 +04:00
armpmu_map_cache_event ( const unsigned ( * cache_map )
[ PERF_COUNT_HW_CACHE_MAX ]
[ PERF_COUNT_HW_CACHE_OP_MAX ]
[ PERF_COUNT_HW_CACHE_RESULT_MAX ] ,
u64 config )
2010-02-02 22:25:44 +03:00
{
unsigned int cache_type , cache_op , cache_result , ret ;
cache_type = ( config > > 0 ) & 0xff ;
if ( cache_type > = PERF_COUNT_HW_CACHE_MAX )
return - EINVAL ;
cache_op = ( config > > 8 ) & 0xff ;
if ( cache_op > = PERF_COUNT_HW_CACHE_OP_MAX )
return - EINVAL ;
cache_result = ( config > > 16 ) & 0xff ;
if ( cache_result > = PERF_COUNT_HW_CACHE_RESULT_MAX )
return - EINVAL ;
2011-04-28 18:47:10 +04:00
ret = ( int ) ( * cache_map ) [ cache_type ] [ cache_op ] [ cache_result ] ;
2010-02-02 22:25:44 +03:00
if ( ret = = CACHE_OP_UNSUPPORTED )
return - ENOENT ;
return ret ;
}
2010-11-13 20:13:56 +03:00
static int
2012-07-29 15:36:28 +04:00
armpmu_map_hw_event ( const unsigned ( * event_map ) [ PERF_COUNT_HW_MAX ] , u64 config )
2010-11-13 20:13:56 +03:00
{
2013-08-08 21:41:59 +04:00
int mapping ;
if ( config > = PERF_COUNT_HW_MAX )
return - EINVAL ;
mapping = ( * event_map ) [ config ] ;
2011-04-28 18:47:10 +04:00
return mapping = = HW_OP_UNSUPPORTED ? - ENOENT : mapping ;
2010-11-13 20:13:56 +03:00
}
static int
2011-04-28 18:47:10 +04:00
armpmu_map_raw_event ( u32 raw_event_mask , u64 config )
2010-11-13 20:13:56 +03:00
{
2011-04-28 18:47:10 +04:00
return ( int ) ( config & raw_event_mask ) ;
}
2012-07-29 15:36:28 +04:00
int
armpmu_map_event ( struct perf_event * event ,
const unsigned ( * event_map ) [ PERF_COUNT_HW_MAX ] ,
const unsigned ( * cache_map )
[ PERF_COUNT_HW_CACHE_MAX ]
[ PERF_COUNT_HW_CACHE_OP_MAX ]
[ PERF_COUNT_HW_CACHE_RESULT_MAX ] ,
u32 raw_event_mask )
2011-04-28 18:47:10 +04:00
{
u64 config = event - > attr . config ;
2012-09-12 13:53:23 +04:00
int type = event - > attr . type ;
2011-04-28 18:47:10 +04:00
2012-09-12 13:53:23 +04:00
if ( type = = event - > pmu - > type )
return armpmu_map_raw_event ( raw_event_mask , config ) ;
switch ( type ) {
2011-04-28 18:47:10 +04:00
case PERF_TYPE_HARDWARE :
2012-07-29 15:36:28 +04:00
return armpmu_map_hw_event ( event_map , config ) ;
2011-04-28 18:47:10 +04:00
case PERF_TYPE_HW_CACHE :
return armpmu_map_cache_event ( cache_map , config ) ;
case PERF_TYPE_RAW :
return armpmu_map_raw_event ( raw_event_mask , config ) ;
}
return - ENOENT ;
2010-11-13 20:13:56 +03:00
}
2012-07-30 15:00:02 +04:00
int armpmu_event_set_period ( struct perf_event * event )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2012-07-30 15:00:02 +04:00
struct hw_perf_event * hwc = & event - > hw ;
2010-05-21 16:43:08 +04:00
s64 left = local64_read ( & hwc - > period_left ) ;
2010-02-02 22:25:44 +03:00
s64 period = hwc - > sample_period ;
int ret = 0 ;
if ( unlikely ( left < = - period ) ) {
left = period ;
2010-05-21 16:43:08 +04:00
local64_set ( & hwc - > period_left , left ) ;
2010-02-02 22:25:44 +03:00
hwc - > last_period = period ;
ret = 1 ;
}
if ( unlikely ( left < = 0 ) ) {
left + = period ;
2010-05-21 16:43:08 +04:00
local64_set ( & hwc - > period_left , left ) ;
2010-02-02 22:25:44 +03:00
hwc - > last_period = period ;
ret = 1 ;
}
2015-01-05 17:58:54 +03:00
/*
* Limit the maximum period to prevent the counter value
* from overtaking the one we are about to program . In
* effect we are reducing max_period to account for
* interrupt latency ( and we are being very conservative ) .
*/
if ( left > ( armpmu - > max_period > > 1 ) )
left = armpmu - > max_period > > 1 ;
2010-02-02 22:25:44 +03:00
2010-05-21 16:43:08 +04:00
local64_set ( & hwc - > prev_count , ( u64 ) - left ) ;
2010-02-02 22:25:44 +03:00
2012-07-30 15:00:02 +04:00
armpmu - > write_counter ( event , ( u64 ) ( - left ) & 0xffffffff ) ;
2010-02-02 22:25:44 +03:00
perf_event_update_userpage ( event ) ;
return ret ;
}
2012-07-30 15:00:02 +04:00
u64 armpmu_event_update ( struct perf_event * event )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2012-07-30 15:00:02 +04:00
struct hw_perf_event * hwc = & event - > hw ;
2011-03-25 19:12:37 +03:00
u64 delta , prev_raw_count , new_raw_count ;
2010-02-02 22:25:44 +03:00
again :
2010-05-21 16:43:08 +04:00
prev_raw_count = local64_read ( & hwc - > prev_count ) ;
2012-07-30 15:00:02 +04:00
new_raw_count = armpmu - > read_counter ( event ) ;
2010-02-02 22:25:44 +03:00
2010-05-21 16:43:08 +04:00
if ( local64_cmpxchg ( & hwc - > prev_count , prev_raw_count ,
2010-02-02 22:25:44 +03:00
new_raw_count ) ! = prev_raw_count )
goto again ;
2012-03-06 20:33:17 +04:00
delta = ( new_raw_count - prev_raw_count ) & armpmu - > max_period ;
2010-02-02 22:25:44 +03:00
2010-05-21 16:43:08 +04:00
local64_add ( delta , & event - > count ) ;
local64_sub ( delta , & hwc - > period_left ) ;
2010-02-02 22:25:44 +03:00
return new_raw_count ;
}
static void
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
armpmu_read ( struct perf_event * event )
2010-02-02 22:25:44 +03:00
{
2012-07-30 15:00:02 +04:00
armpmu_event_update ( event ) ;
2010-02-02 22:25:44 +03:00
}
static void
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
armpmu_stop ( struct perf_event * event , int flags )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2010-02-02 22:25:44 +03:00
struct hw_perf_event * hwc = & event - > hw ;
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
/*
* ARM pmu always has to update the counter , so ignore
* PERF_EF_UPDATE , see comments in armpmu_start ( ) .
*/
if ( ! ( hwc - > state & PERF_HES_STOPPED ) ) {
2012-07-30 15:00:02 +04:00
armpmu - > disable ( event ) ;
armpmu_event_update ( event ) ;
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
hwc - > state | = PERF_HES_STOPPED | PERF_HES_UPTODATE ;
}
2010-02-02 22:25:44 +03:00
}
2012-07-30 15:00:02 +04:00
static void armpmu_start ( struct perf_event * event , int flags )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2010-02-02 22:25:44 +03:00
struct hw_perf_event * hwc = & event - > hw ;
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
/*
* ARM pmu always has to reprogram the period , so ignore
* PERF_EF_RELOAD , see the comment below .
*/
if ( flags & PERF_EF_RELOAD )
WARN_ON_ONCE ( ! ( hwc - > state & PERF_HES_UPTODATE ) ) ;
hwc - > state = 0 ;
2010-02-02 22:25:44 +03:00
/*
* Set the period again . Some counters can ' t be stopped , so when we
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
* were stopped we simply disabled the IRQ source and the counter
2010-02-02 22:25:44 +03:00
* may have been left counting . If we don ' t do this step then we may
* get an interrupt too soon or * way * too late if the overflow has
* happened since disabling .
*/
2012-07-30 15:00:02 +04:00
armpmu_event_set_period ( event ) ;
armpmu - > enable ( event ) ;
2010-02-02 22:25:44 +03:00
}
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
static void
armpmu_del ( struct perf_event * event , int flags )
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2014-05-13 22:36:31 +04:00
struct pmu_hw_events * hw_events = this_cpu_ptr ( armpmu - > hw_events ) ;
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
struct hw_perf_event * hwc = & event - > hw ;
int idx = hwc - > idx ;
armpmu_stop ( event , PERF_EF_UPDATE ) ;
2011-05-17 14:20:11 +04:00
hw_events - > events [ idx ] = NULL ;
clear_bit ( idx , hw_events - > used_mask ) ;
2014-02-08 01:01:22 +04:00
if ( armpmu - > clear_event_idx )
armpmu - > clear_event_idx ( hw_events , event ) ;
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
perf_event_update_userpage ( event ) ;
}
2010-02-02 22:25:44 +03:00
static int
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
armpmu_add ( struct perf_event * event , int flags )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2014-05-13 22:36:31 +04:00
struct pmu_hw_events * hw_events = this_cpu_ptr ( armpmu - > hw_events ) ;
2010-02-02 22:25:44 +03:00
struct hw_perf_event * hwc = & event - > hw ;
int idx ;
int err = 0 ;
2015-05-13 19:12:25 +03:00
/* An event following a process won't be stopped earlier */
if ( ! cpumask_test_cpu ( smp_processor_id ( ) , & armpmu - > supported_cpus ) )
return - ENOENT ;
2010-06-14 10:49:00 +04:00
perf_pmu_disable ( event - > pmu ) ;
2010-06-11 19:32:03 +04:00
2010-02-02 22:25:44 +03:00
/* If we don't have a space for the counter then finish early. */
2012-07-30 15:00:02 +04:00
idx = armpmu - > get_event_idx ( hw_events , event ) ;
2010-02-02 22:25:44 +03:00
if ( idx < 0 ) {
err = idx ;
goto out ;
}
/*
* If there is an event in the counter we are going to use then make
* sure it is disabled .
*/
event - > hw . idx = idx ;
2012-07-30 15:00:02 +04:00
armpmu - > disable ( event ) ;
2011-05-17 14:20:11 +04:00
hw_events - > events [ idx ] = event ;
2010-02-02 22:25:44 +03:00
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
hwc - > state = PERF_HES_STOPPED | PERF_HES_UPTODATE ;
if ( flags & PERF_EF_START )
armpmu_start ( event , PERF_EF_RELOAD ) ;
2010-02-02 22:25:44 +03:00
/* Propagate our changes to the userspace mapping. */
perf_event_update_userpage ( event ) ;
out :
2010-06-14 10:49:00 +04:00
perf_pmu_enable ( event - > pmu ) ;
2010-02-02 22:25:44 +03:00
return err ;
}
static int
2015-03-17 21:14:58 +03:00
validate_event ( struct pmu * pmu , struct pmu_hw_events * hw_events ,
struct perf_event * event )
2010-02-02 22:25:44 +03:00
{
2015-03-17 21:14:58 +03:00
struct arm_pmu * armpmu ;
2010-02-02 22:25:44 +03:00
2013-08-08 02:39:41 +04:00
if ( is_software_event ( event ) )
return 1 ;
2015-03-17 21:14:58 +03:00
/*
* Reject groups spanning multiple HW PMUs ( e . g . CPU + CCI ) . The
* core perf code won ' t check that the pmu - > ctx = = leader - > ctx
* until after pmu - > event_init ( event ) .
*/
if ( event - > pmu ! = pmu )
return 0 ;
2013-10-09 16:51:29 +04:00
if ( event - > state < PERF_EVENT_STATE_OFF )
2013-04-12 22:04:19 +04:00
return 1 ;
if ( event - > state = = PERF_EVENT_STATE_OFF & & ! event - > attr . enable_on_exec )
2010-09-02 12:32:08 +04:00
return 1 ;
2010-02-02 22:25:44 +03:00
2015-03-17 21:14:58 +03:00
armpmu = to_arm_pmu ( event - > pmu ) ;
2012-07-30 15:00:02 +04:00
return armpmu - > get_event_idx ( hw_events , event ) > = 0 ;
2010-02-02 22:25:44 +03:00
}
static int
validate_group ( struct perf_event * event )
{
struct perf_event * sibling , * leader = event - > group_leader ;
2011-05-17 14:20:11 +04:00
struct pmu_hw_events fake_pmu ;
2010-02-02 22:25:44 +03:00
2011-11-17 19:05:14 +04:00
/*
* Initialise the fake PMU . We only need to populate the
* used_mask for the purposes of validation .
*/
2014-05-13 22:08:19 +04:00
memset ( & fake_pmu . used_mask , 0 , sizeof ( fake_pmu . used_mask ) ) ;
2010-02-02 22:25:44 +03:00
2015-03-17 21:14:58 +03:00
if ( ! validate_event ( event - > pmu , & fake_pmu , leader ) )
2011-11-09 20:56:37 +04:00
return - EINVAL ;
2010-02-02 22:25:44 +03:00
list_for_each_entry ( sibling , & leader - > sibling_list , group_entry ) {
2015-03-17 21:14:58 +03:00
if ( ! validate_event ( event - > pmu , & fake_pmu , sibling ) )
2011-11-09 20:56:37 +04:00
return - EINVAL ;
2010-02-02 22:25:44 +03:00
}
2015-03-17 21:14:58 +03:00
if ( ! validate_event ( event - > pmu , & fake_pmu , event ) )
2011-11-09 20:56:37 +04:00
return - EINVAL ;
2010-02-02 22:25:44 +03:00
return 0 ;
}
2012-07-31 13:34:25 +04:00
static irqreturn_t armpmu_dispatch_irq ( int irq , void * dev )
2011-02-08 06:54:36 +03:00
{
2014-02-08 01:01:19 +04:00
struct arm_pmu * armpmu ;
struct platform_device * plat_device ;
struct arm_pmu_platdata * plat ;
2014-02-11 22:08:41 +04:00
int ret ;
u64 start_clock , finish_clock ;
2014-02-08 01:01:19 +04:00
2014-05-13 22:46:10 +04:00
/*
* we request the IRQ with a ( possibly percpu ) struct arm_pmu * * , but
* the handlers expect a struct arm_pmu * . The percpu_irq framework will
* do any necessary shifting , we just need to perform the first
* dereference .
*/
armpmu = * ( void * * ) dev ;
2014-02-08 01:01:19 +04:00
plat_device = armpmu - > plat_device ;
plat = dev_get_platdata ( & plat_device - > dev ) ;
2011-02-08 06:54:36 +03:00
2014-02-11 22:08:41 +04:00
start_clock = sched_clock ( ) ;
2012-07-31 13:34:25 +04:00
if ( plat & & plat - > handle_irq )
2014-05-13 22:46:10 +04:00
ret = plat - > handle_irq ( irq , armpmu , armpmu - > handle_irq ) ;
2012-07-31 13:34:25 +04:00
else
2014-05-13 22:46:10 +04:00
ret = armpmu - > handle_irq ( irq , armpmu ) ;
2014-02-11 22:08:41 +04:00
finish_clock = sched_clock ( ) ;
perf_sample_event_took ( finish_clock - start_clock ) ;
return ret ;
2011-02-08 06:54:36 +03:00
}
2011-07-27 18:18:59 +04:00
static void
2011-04-28 19:27:54 +04:00
armpmu_release_hardware ( struct arm_pmu * armpmu )
2011-07-27 18:18:59 +04:00
{
2012-07-30 15:00:02 +04:00
armpmu - > free_irq ( armpmu ) ;
2011-07-27 18:18:59 +04:00
}
2010-02-02 22:25:44 +03:00
static int
2011-04-28 19:27:54 +04:00
armpmu_reserve_hardware ( struct arm_pmu * armpmu )
2010-02-02 22:25:44 +03:00
{
2015-05-26 19:23:34 +03:00
int err = armpmu - > request_irq ( armpmu , armpmu_dispatch_irq ) ;
2012-07-31 13:34:25 +04:00
if ( err ) {
armpmu_release_hardware ( armpmu ) ;
return err ;
2010-04-29 20:13:24 +04:00
}
2010-02-02 22:25:44 +03:00
2011-07-27 18:18:59 +04:00
return 0 ;
2010-02-02 22:25:44 +03:00
}
static void
hw_perf_event_destroy ( struct perf_event * event )
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2011-04-27 14:20:11 +04:00
atomic_t * active_events = & armpmu - > active_events ;
struct mutex * pmu_reserve_mutex = & armpmu - > reserve_mutex ;
if ( atomic_dec_and_mutex_lock ( active_events , pmu_reserve_mutex ) ) {
2011-04-28 19:27:54 +04:00
armpmu_release_hardware ( armpmu ) ;
2011-04-27 14:20:11 +04:00
mutex_unlock ( pmu_reserve_mutex ) ;
2010-02-02 22:25:44 +03:00
}
}
2011-07-19 14:57:30 +04:00
static int
event_requires_mode_exclusion ( struct perf_event_attr * attr )
{
return attr - > exclude_idle | | attr - > exclude_user | |
attr - > exclude_kernel | | attr - > exclude_hv ;
}
2010-02-02 22:25:44 +03:00
static int
__hw_perf_event_init ( struct perf_event * event )
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2010-02-02 22:25:44 +03:00
struct hw_perf_event * hwc = & event - > hw ;
2013-01-18 20:10:06 +04:00
int mapping ;
2010-02-02 22:25:44 +03:00
2011-04-28 18:47:10 +04:00
mapping = armpmu - > map_event ( event ) ;
2010-02-02 22:25:44 +03:00
if ( mapping < 0 ) {
pr_debug ( " event %x:%llx not supported \n " , event - > attr . type ,
event - > attr . config ) ;
return mapping ;
}
2011-07-19 14:57:30 +04:00
/*
* We don ' t assign an index until we actually place the event onto
* hardware . Use - 1 to signify that we haven ' t decided where to put it
* yet . For SMP systems , each core has it ' s own PMU so we can ' t do any
* clever allocation or constraints checking at this point .
*/
hwc - > idx = - 1 ;
hwc - > config_base = 0 ;
hwc - > config = 0 ;
hwc - > event_base = 0 ;
2010-02-02 22:25:44 +03:00
/*
* Check whether we need to exclude the counter from certain modes .
*/
2011-07-19 14:57:30 +04:00
if ( ( ! armpmu - > set_event_filter | |
armpmu - > set_event_filter ( hwc , & event - > attr ) ) & &
event_requires_mode_exclusion ( & event - > attr ) ) {
2010-02-02 22:25:44 +03:00
pr_debug ( " ARM performance counters do not support "
" mode exclusion \n " ) ;
2012-07-04 21:15:42 +04:00
return - EOPNOTSUPP ;
2010-02-02 22:25:44 +03:00
}
/*
2011-07-19 14:57:30 +04:00
* Store the event encoding into the config_base field .
2010-02-02 22:25:44 +03:00
*/
2011-07-19 14:57:30 +04:00
hwc - > config_base | = ( unsigned long ) mapping ;
2010-02-02 22:25:44 +03:00
2014-05-17 01:15:49 +04:00
if ( ! is_sampling_event ( event ) ) {
2012-03-06 20:33:17 +04:00
/*
* For non - sampling runs , limit the sample_period to half
* of the counter width . That way , the new counter value
* is far less likely to overtake the previous one unless
* you have some serious IRQ latency issues .
*/
hwc - > sample_period = armpmu - > max_period > > 1 ;
2010-02-02 22:25:44 +03:00
hwc - > last_period = hwc - > sample_period ;
2010-05-21 16:43:08 +04:00
local64_set ( & hwc - > period_left , hwc - > sample_period ) ;
2010-02-02 22:25:44 +03:00
}
if ( event - > group_leader ! = event ) {
2013-02-28 20:51:29 +04:00
if ( validate_group ( event ) ! = 0 )
2010-02-02 22:25:44 +03:00
return - EINVAL ;
}
2013-01-18 20:10:06 +04:00
return 0 ;
2010-02-02 22:25:44 +03:00
}
2010-06-11 15:35:08 +04:00
static int armpmu_event_init ( struct perf_event * event )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
2010-02-02 22:25:44 +03:00
int err = 0 ;
2011-04-27 14:20:11 +04:00
atomic_t * active_events = & armpmu - > active_events ;
2010-02-02 22:25:44 +03:00
2015-05-13 19:12:25 +03:00
/*
* Reject CPU - affine events for CPUs that are of a different class to
* that which this PMU handles . Process - following events ( where
* event - > cpu = = - 1 ) can be migrated between CPUs , and thus we have to
* reject them later ( in armpmu_add ) if they ' re scheduled on a
* different class of CPU .
*/
if ( event - > cpu ! = - 1 & &
! cpumask_test_cpu ( event - > cpu , & armpmu - > supported_cpus ) )
return - ENOENT ;
2012-02-10 02:20:59 +04:00
/* does not support taken branch sampling */
if ( has_branch_stack ( event ) )
return - EOPNOTSUPP ;
2011-04-28 18:47:10 +04:00
if ( armpmu - > map_event ( event ) = = - ENOENT )
2010-06-11 15:35:08 +04:00
return - ENOENT ;
2010-02-02 22:25:44 +03:00
event - > destroy = hw_perf_event_destroy ;
2011-04-27 14:20:11 +04:00
if ( ! atomic_inc_not_zero ( active_events ) ) {
mutex_lock ( & armpmu - > reserve_mutex ) ;
if ( atomic_read ( active_events ) = = 0 )
2011-04-28 19:27:54 +04:00
err = armpmu_reserve_hardware ( armpmu ) ;
2010-02-02 22:25:44 +03:00
if ( ! err )
2011-04-27 14:20:11 +04:00
atomic_inc ( active_events ) ;
mutex_unlock ( & armpmu - > reserve_mutex ) ;
2010-02-02 22:25:44 +03:00
}
if ( err )
2010-06-11 15:35:08 +04:00
return err ;
2010-02-02 22:25:44 +03:00
err = __hw_perf_event_init ( event ) ;
if ( err )
hw_perf_event_destroy ( event ) ;
2010-06-11 15:35:08 +04:00
return err ;
2010-02-02 22:25:44 +03:00
}
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
static void armpmu_enable ( struct pmu * pmu )
2010-02-02 22:25:44 +03:00
{
2011-05-17 14:20:11 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( pmu ) ;
2014-05-13 22:36:31 +04:00
struct pmu_hw_events * hw_events = this_cpu_ptr ( armpmu - > hw_events ) ;
2011-08-23 14:59:49 +04:00
int enabled = bitmap_weight ( hw_events - > used_mask , armpmu - > num_events ) ;
2010-02-02 22:25:44 +03:00
2015-05-13 19:12:25 +03:00
/* For task-bound events we may be called on other CPUs */
if ( ! cpumask_test_cpu ( smp_processor_id ( ) , & armpmu - > supported_cpus ) )
return ;
2011-07-01 17:38:12 +04:00
if ( enabled )
2012-07-30 15:00:02 +04:00
armpmu - > start ( armpmu ) ;
2010-02-02 22:25:44 +03:00
}
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-06-16 16:37:10 +04:00
static void armpmu_disable ( struct pmu * pmu )
2010-02-02 22:25:44 +03:00
{
2011-04-28 19:27:54 +04:00
struct arm_pmu * armpmu = to_arm_pmu ( pmu ) ;
2015-05-13 19:12:25 +03:00
/* For task-bound events we may be called on other CPUs */
if ( ! cpumask_test_cpu ( smp_processor_id ( ) , & armpmu - > supported_cpus ) )
return ;
2012-07-30 15:00:02 +04:00
armpmu - > stop ( armpmu ) ;
2010-02-02 22:25:44 +03:00
}
2015-05-13 19:12:26 +03:00
/*
* In heterogeneous systems , events are specific to a particular
* microarchitecture , and aren ' t suitable for another . Thus , only match CPUs of
* the same microarchitecture .
*/
static int armpmu_filter_match ( struct perf_event * event )
{
struct arm_pmu * armpmu = to_arm_pmu ( event - > pmu ) ;
unsigned int cpu = smp_processor_id ( ) ;
return cpumask_test_cpu ( cpu , & armpmu - > supported_cpus ) ;
}
2013-03-05 06:54:06 +04:00
static void armpmu_init ( struct arm_pmu * armpmu )
2011-04-27 14:20:11 +04:00
{
atomic_set ( & armpmu - > active_events , 0 ) ;
mutex_init ( & armpmu - > reserve_mutex ) ;
2011-04-28 19:27:54 +04:00
armpmu - > pmu = ( struct pmu ) {
. pmu_enable = armpmu_enable ,
. pmu_disable = armpmu_disable ,
. event_init = armpmu_event_init ,
. add = armpmu_add ,
. del = armpmu_del ,
. start = armpmu_start ,
. stop = armpmu_stop ,
. read = armpmu_read ,
2015-05-13 19:12:26 +03:00
. filter_match = armpmu_filter_match ,
2011-04-28 19:27:54 +04:00
} ;
}
2015-05-26 19:23:39 +03:00
/* Set at runtime when we know what CPU type we are. */
static struct arm_pmu * __oprofile_cpu_pmu ;
/*
* Despite the names , these two functions are CPU - specific and are used
* by the OProfile / perf code .
*/
const char * perf_pmu_name ( void )
{
if ( ! __oprofile_cpu_pmu )
return NULL ;
return __oprofile_cpu_pmu - > name ;
}
EXPORT_SYMBOL_GPL ( perf_pmu_name ) ;
int perf_num_counters ( void )
{
int max_events = 0 ;
if ( __oprofile_cpu_pmu ! = NULL )
max_events = __oprofile_cpu_pmu - > num_events ;
return max_events ;
}
EXPORT_SYMBOL_GPL ( perf_num_counters ) ;
static void cpu_pmu_enable_percpu_irq ( void * data )
{
int irq = * ( int * ) data ;
enable_percpu_irq ( irq , IRQ_TYPE_NONE ) ;
}
static void cpu_pmu_disable_percpu_irq ( void * data )
{
int irq = * ( int * ) data ;
disable_percpu_irq ( irq ) ;
}
static void cpu_pmu_free_irq ( struct arm_pmu * cpu_pmu )
{
int i , irq , irqs ;
struct platform_device * pmu_device = cpu_pmu - > plat_device ;
struct pmu_hw_events __percpu * hw_events = cpu_pmu - > hw_events ;
irqs = min ( pmu_device - > num_resources , num_possible_cpus ( ) ) ;
irq = platform_get_irq ( pmu_device , 0 ) ;
if ( irq > = 0 & & irq_is_percpu ( irq ) ) {
on_each_cpu ( cpu_pmu_disable_percpu_irq , & irq , 1 ) ;
free_percpu_irq ( irq , & hw_events - > percpu_pmu ) ;
} else {
for ( i = 0 ; i < irqs ; + + i ) {
int cpu = i ;
if ( cpu_pmu - > irq_affinity )
cpu = cpu_pmu - > irq_affinity [ i ] ;
if ( ! cpumask_test_and_clear_cpu ( cpu , & cpu_pmu - > active_irqs ) )
continue ;
irq = platform_get_irq ( pmu_device , i ) ;
if ( irq > = 0 )
free_irq ( irq , per_cpu_ptr ( & hw_events - > percpu_pmu , cpu ) ) ;
}
}
}
static int cpu_pmu_request_irq ( struct arm_pmu * cpu_pmu , irq_handler_t handler )
{
int i , err , irq , irqs ;
struct platform_device * pmu_device = cpu_pmu - > plat_device ;
struct pmu_hw_events __percpu * hw_events = cpu_pmu - > hw_events ;
if ( ! pmu_device )
return - ENODEV ;
irqs = min ( pmu_device - > num_resources , num_possible_cpus ( ) ) ;
if ( irqs < 1 ) {
pr_warn_once ( " perf/ARM: No irqs for PMU defined, sampling events not supported \n " ) ;
return 0 ;
}
irq = platform_get_irq ( pmu_device , 0 ) ;
if ( irq > = 0 & & irq_is_percpu ( irq ) ) {
err = request_percpu_irq ( irq , handler , " arm-pmu " ,
& hw_events - > percpu_pmu ) ;
if ( err ) {
pr_err ( " unable to request IRQ%d for ARM PMU counters \n " ,
irq ) ;
return err ;
}
on_each_cpu ( cpu_pmu_enable_percpu_irq , & irq , 1 ) ;
} else {
for ( i = 0 ; i < irqs ; + + i ) {
int cpu = i ;
err = 0 ;
irq = platform_get_irq ( pmu_device , i ) ;
if ( irq < 0 )
continue ;
if ( cpu_pmu - > irq_affinity )
cpu = cpu_pmu - > irq_affinity [ i ] ;
/*
* 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 ( cpu ) ) & & irqs > 1 ) {
pr_warn ( " unable to set irq affinity (irq=%d, cpu=%u) \n " ,
irq , cpu ) ;
continue ;
}
err = request_irq ( irq , handler ,
IRQF_NOBALANCING | IRQF_NO_THREAD , " arm-pmu " ,
per_cpu_ptr ( & hw_events - > percpu_pmu , cpu ) ) ;
if ( err ) {
pr_err ( " unable to request IRQ%d for ARM PMU counters \n " ,
irq ) ;
return err ;
}
cpumask_set_cpu ( cpu , & cpu_pmu - > active_irqs ) ;
}
}
return 0 ;
}
/*
* PMU hardware loses all context when a CPU goes offline .
* When a CPU is hotplugged back in , since some hardware registers are
* UNKNOWN at reset , the PMU must be explicitly reset to avoid reading
* junk values out of them .
*/
static int cpu_pmu_notify ( struct notifier_block * b , unsigned long action ,
void * hcpu )
{
int cpu = ( unsigned long ) hcpu ;
struct arm_pmu * pmu = container_of ( b , struct arm_pmu , hotplug_nb ) ;
if ( ( action & ~ CPU_TASKS_FROZEN ) ! = CPU_STARTING )
return NOTIFY_DONE ;
if ( ! cpumask_test_cpu ( cpu , & pmu - > supported_cpus ) )
return NOTIFY_DONE ;
if ( pmu - > reset )
pmu - > reset ( pmu ) ;
else
return NOTIFY_DONE ;
return NOTIFY_OK ;
}
static int cpu_pmu_init ( struct arm_pmu * cpu_pmu )
{
int err ;
int cpu ;
struct pmu_hw_events __percpu * cpu_hw_events ;
cpu_hw_events = alloc_percpu ( struct pmu_hw_events ) ;
if ( ! cpu_hw_events )
return - ENOMEM ;
cpu_pmu - > hotplug_nb . notifier_call = cpu_pmu_notify ;
err = register_cpu_notifier ( & cpu_pmu - > hotplug_nb ) ;
if ( err )
goto out_hw_events ;
for_each_possible_cpu ( cpu ) {
struct pmu_hw_events * events = per_cpu_ptr ( cpu_hw_events , cpu ) ;
raw_spin_lock_init ( & events - > pmu_lock ) ;
events - > percpu_pmu = cpu_pmu ;
}
cpu_pmu - > hw_events = cpu_hw_events ;
cpu_pmu - > request_irq = cpu_pmu_request_irq ;
cpu_pmu - > free_irq = cpu_pmu_free_irq ;
/* Ensure the PMU has sane values out of reset. */
if ( cpu_pmu - > reset )
on_each_cpu_mask ( & cpu_pmu - > supported_cpus , cpu_pmu - > reset ,
cpu_pmu , 1 ) ;
/* If no interrupts available, set the corresponding capability flag */
if ( ! platform_get_irq ( cpu_pmu - > plat_device , 0 ) )
cpu_pmu - > pmu . capabilities | = PERF_PMU_CAP_NO_INTERRUPT ;
return 0 ;
out_hw_events :
free_percpu ( cpu_hw_events ) ;
return err ;
}
static void cpu_pmu_destroy ( struct arm_pmu * cpu_pmu )
{
unregister_cpu_notifier ( & cpu_pmu - > hotplug_nb ) ;
free_percpu ( cpu_pmu - > hw_events ) ;
}
/*
* CPU PMU identification and probing .
*/
static int probe_current_pmu ( struct arm_pmu * pmu ,
const struct pmu_probe_info * info )
{
int cpu = get_cpu ( ) ;
unsigned int cpuid = read_cpuid_id ( ) ;
int ret = - ENODEV ;
pr_info ( " probing PMU on CPU %d \n " , cpu ) ;
for ( ; info - > init ! = NULL ; info + + ) {
if ( ( cpuid & info - > mask ) ! = info - > cpuid )
continue ;
ret = info - > init ( pmu ) ;
break ;
}
put_cpu ( ) ;
return ret ;
}
static int of_pmu_irq_cfg ( struct arm_pmu * pmu )
{
2015-06-29 15:59:01 +03:00
int * irqs , i = 0 ;
bool using_spi = false ;
2015-05-26 19:23:39 +03:00
struct platform_device * pdev = pmu - > plat_device ;
irqs = kcalloc ( pdev - > num_resources , sizeof ( * irqs ) , GFP_KERNEL ) ;
if ( ! irqs )
return - ENOMEM ;
2015-06-29 15:59:01 +03:00
do {
2015-05-26 19:23:39 +03:00
struct device_node * dn ;
2015-06-29 15:59:01 +03:00
int cpu , irq ;
2015-05-26 19:23:39 +03:00
2015-06-29 15:59:01 +03:00
/* See if we have an affinity entry */
dn = of_parse_phandle ( pdev - > dev . of_node , " interrupt-affinity " , i ) ;
if ( ! dn )
2015-05-26 19:23:39 +03:00
break ;
2015-06-29 15:59:01 +03:00
/* Check the IRQ type and prohibit a mix of PPIs and SPIs */
irq = platform_get_irq ( pdev , i ) ;
if ( irq > = 0 ) {
bool spi = ! irq_is_percpu ( irq ) ;
if ( i > 0 & & spi ! = using_spi ) {
pr_err ( " PPI/SPI IRQ type mismatch for %s! \n " ,
dn - > name ) ;
kfree ( irqs ) ;
return - EINVAL ;
}
using_spi = spi ;
2015-05-26 19:23:39 +03:00
}
2015-06-29 15:59:01 +03:00
/* Now look up the logical CPU number */
2015-10-12 16:48:39 +03:00
for_each_possible_cpu ( cpu ) {
struct device_node * cpu_dn ;
cpu_dn = of_cpu_device_node_get ( cpu ) ;
of_node_put ( cpu_dn ) ;
if ( dn = = cpu_dn )
2015-05-26 19:23:39 +03:00
break ;
2015-10-12 16:48:39 +03:00
}
2015-05-26 19:23:39 +03:00
if ( cpu > = nr_cpu_ids ) {
pr_warn ( " Failed to find logical CPU for %s \n " ,
dn - > name ) ;
2015-07-07 20:17:05 +03:00
of_node_put ( dn ) ;
2015-06-29 15:59:01 +03:00
cpumask_setall ( & pmu - > supported_cpus ) ;
2015-05-26 19:23:39 +03:00
break ;
}
2015-07-07 20:17:05 +03:00
of_node_put ( dn ) ;
2015-05-26 19:23:39 +03:00
2015-06-29 15:59:01 +03:00
/* For SPIs, we need to track the affinity per IRQ */
if ( using_spi ) {
if ( i > = pdev - > num_resources ) {
of_node_put ( dn ) ;
break ;
}
irqs [ i ] = cpu ;
}
/* Keep track of the CPUs containing this PMU type */
2015-05-26 19:23:39 +03:00
cpumask_set_cpu ( cpu , & pmu - > supported_cpus ) ;
2015-06-29 15:59:01 +03:00
of_node_put ( dn ) ;
i + + ;
} while ( 1 ) ;
2015-05-26 19:23:39 +03:00
2015-06-29 15:59:01 +03:00
/* If we didn't manage to parse anything, claim to support all CPUs */
if ( cpumask_weight ( & pmu - > supported_cpus ) = = 0 )
cpumask_setall ( & pmu - > supported_cpus ) ;
/* If we matched up the IRQ affinities, use them to route the SPIs */
if ( using_spi & & i = = pdev - > num_resources )
2015-05-26 19:23:39 +03:00
pmu - > irq_affinity = irqs ;
2015-06-29 15:59:01 +03:00
else
2015-05-26 19:23:39 +03:00
kfree ( irqs ) ;
return 0 ;
}
int arm_pmu_device_probe ( struct platform_device * pdev ,
const struct of_device_id * of_table ,
const struct pmu_probe_info * probe_table )
{
const struct of_device_id * of_id ;
const int ( * init_fn ) ( struct arm_pmu * ) ;
struct device_node * node = pdev - > dev . of_node ;
struct arm_pmu * pmu ;
int ret = - ENODEV ;
pmu = kzalloc ( sizeof ( struct arm_pmu ) , GFP_KERNEL ) ;
if ( ! pmu ) {
pr_info ( " failed to allocate PMU device! \n " ) ;
return - ENOMEM ;
}
2015-10-28 15:32:17 +03:00
armpmu_init ( pmu ) ;
2015-05-26 19:23:39 +03:00
if ( ! __oprofile_cpu_pmu )
__oprofile_cpu_pmu = pmu ;
pmu - > plat_device = pdev ;
if ( node & & ( of_id = of_match_node ( of_table , pdev - > dev . of_node ) ) ) {
init_fn = of_id - > data ;
ret = of_pmu_irq_cfg ( pmu ) ;
if ( ! ret )
ret = init_fn ( pmu ) ;
} else {
ret = probe_current_pmu ( pmu , probe_table ) ;
cpumask_setall ( & pmu - > supported_cpus ) ;
}
if ( ret ) {
pr_info ( " failed to probe PMU! \n " ) ;
goto out_free ;
}
ret = cpu_pmu_init ( pmu ) ;
if ( ret )
goto out_free ;
2015-10-28 15:32:17 +03:00
ret = perf_pmu_register ( & pmu - > pmu , pmu - > name , - 1 ) ;
2015-05-26 19:23:39 +03:00
if ( ret )
goto out_destroy ;
2015-10-28 15:32:17 +03:00
pr_info ( " enabled with %s PMU driver, %d counters available \n " ,
pmu - > name , pmu - > num_events ) ;
2015-05-26 19:23:39 +03:00
return 0 ;
out_destroy :
cpu_pmu_destroy ( pmu ) ;
out_free :
pr_info ( " failed to register PMU devices! \n " ) ;
kfree ( pmu ) ;
return ret ;
}