Commit b40d0156 authored by Sandipan Das's avatar Sandipan Das Committed by Peter Zijlstra

perf/x86/amd/brs: Move feature-specific functions

Move some of the Branch Sampling (BRS) specific functions out of the Core
events sources and into the BRS sources in preparation for adding other
mechanisms to record branches.
Signed-off-by: default avatarSandipan Das <sandipan.das@amd.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/b60283b57179475d18ee242d117c335c16733693.1660211399.git.sandipan.das@amd.com
parent 1c23f9e6
...@@ -81,7 +81,7 @@ static bool __init amd_brs_detect(void) ...@@ -81,7 +81,7 @@ static bool __init amd_brs_detect(void)
* a br_sel_map. Software filtering is not supported because it would not correlate well * a br_sel_map. Software filtering is not supported because it would not correlate well
* with a sampling period. * with a sampling period.
*/ */
int amd_brs_setup_filter(struct perf_event *event) static int amd_brs_setup_filter(struct perf_event *event)
{ {
u64 type = event->attr.branch_sample_type; u64 type = event->attr.branch_sample_type;
...@@ -96,6 +96,73 @@ int amd_brs_setup_filter(struct perf_event *event) ...@@ -96,6 +96,73 @@ int amd_brs_setup_filter(struct perf_event *event)
return 0; return 0;
} }
static inline int amd_is_brs_event(struct perf_event *e)
{
return (e->hw.config & AMD64_RAW_EVENT_MASK) == AMD_FAM19H_BRS_EVENT;
}
int amd_brs_hw_config(struct perf_event *event)
{
int ret = 0;
/*
* Due to interrupt holding, BRS is not recommended in
* counting mode.
*/
if (!is_sampling_event(event))
return -EINVAL;
/*
* Due to the way BRS operates by holding the interrupt until
* lbr_nr entries have been captured, it does not make sense
* to allow sampling on BRS with an event that does not match
* what BRS is capturing, i.e., retired taken branches.
* Otherwise the correlation with the event's period is even
* more loose:
*
* With retired taken branch:
* Effective P = P + 16 + X
* With any other event:
* Effective P = P + Y + X
*
* Where X is the number of taken branches due to interrupt
* skid. Skid is large.
*
* Where Y is the occurences of the event while BRS is
* capturing the lbr_nr entries.
*
* By using retired taken branches, we limit the impact on the
* Y variable. We know it cannot be more than the depth of
* BRS.
*/
if (!amd_is_brs_event(event))
return -EINVAL;
/*
* BRS implementation does not work with frequency mode
* reprogramming of the period.
*/
if (event->attr.freq)
return -EINVAL;
/*
* The kernel subtracts BRS depth from period, so it must
* be big enough.
*/
if (event->attr.sample_period <= x86_pmu.lbr_nr)
return -EINVAL;
/*
* Check if we can allow PERF_SAMPLE_BRANCH_STACK
*/
ret = amd_brs_setup_filter(event);
/* only set in case of success */
if (!ret)
event->hw.flags |= PERF_X86_EVENT_AMD_BRS;
return ret;
}
/* tos = top of stack, i.e., last valid entry written */ /* tos = top of stack, i.e., last valid entry written */
static inline int amd_brs_get_tos(union amd_debug_extn_cfg *cfg) static inline int amd_brs_get_tos(union amd_debug_extn_cfg *cfg)
{ {
......
...@@ -330,16 +330,8 @@ static inline bool amd_is_pair_event_code(struct hw_perf_event *hwc) ...@@ -330,16 +330,8 @@ static inline bool amd_is_pair_event_code(struct hw_perf_event *hwc)
} }
} }
#define AMD_FAM19H_BRS_EVENT 0xc4 /* RETIRED_TAKEN_BRANCH_INSTRUCTIONS */
static inline int amd_is_brs_event(struct perf_event *e)
{
return (e->hw.config & AMD64_RAW_EVENT_MASK) == AMD_FAM19H_BRS_EVENT;
}
static int amd_core_hw_config(struct perf_event *event) static int amd_core_hw_config(struct perf_event *event)
{ {
int ret = 0;
if (event->attr.exclude_host && event->attr.exclude_guest) if (event->attr.exclude_host && event->attr.exclude_guest)
/* /*
* When HO == GO == 1 the hardware treats that as GO == HO == 0 * When HO == GO == 1 the hardware treats that as GO == HO == 0
...@@ -356,66 +348,10 @@ static int amd_core_hw_config(struct perf_event *event) ...@@ -356,66 +348,10 @@ static int amd_core_hw_config(struct perf_event *event)
if ((x86_pmu.flags & PMU_FL_PAIR) && amd_is_pair_event_code(&event->hw)) if ((x86_pmu.flags & PMU_FL_PAIR) && amd_is_pair_event_code(&event->hw))
event->hw.flags |= PERF_X86_EVENT_PAIR; event->hw.flags |= PERF_X86_EVENT_PAIR;
/* if (has_branch_stack(event))
* if branch stack is requested return amd_brs_hw_config(event);
*/
if (has_branch_stack(event)) {
/*
* Due to interrupt holding, BRS is not recommended in
* counting mode.
*/
if (!is_sampling_event(event))
return -EINVAL;
/*
* Due to the way BRS operates by holding the interrupt until
* lbr_nr entries have been captured, it does not make sense
* to allow sampling on BRS with an event that does not match
* what BRS is capturing, i.e., retired taken branches.
* Otherwise the correlation with the event's period is even
* more loose:
*
* With retired taken branch:
* Effective P = P + 16 + X
* With any other event:
* Effective P = P + Y + X
*
* Where X is the number of taken branches due to interrupt
* skid. Skid is large.
*
* Where Y is the occurences of the event while BRS is
* capturing the lbr_nr entries.
*
* By using retired taken branches, we limit the impact on the
* Y variable. We know it cannot be more than the depth of
* BRS.
*/
if (!amd_is_brs_event(event))
return -EINVAL;
/*
* BRS implementation does not work with frequency mode
* reprogramming of the period.
*/
if (event->attr.freq)
return -EINVAL;
/*
* The kernel subtracts BRS depth from period, so it must
* be big enough.
*/
if (event->attr.sample_period <= x86_pmu.lbr_nr)
return -EINVAL;
/* return 0;
* Check if we can allow PERF_SAMPLE_BRANCH_STACK
*/
ret = amd_brs_setup_filter(event);
/* only set in case of success */
if (!ret)
event->hw.flags |= PERF_X86_EVENT_AMD_BRS;
}
return ret;
} }
static inline int amd_is_nb_event(struct hw_perf_event *hwc) static inline int amd_is_nb_event(struct hw_perf_event *hwc)
......
...@@ -1233,6 +1233,9 @@ static inline bool fixed_counter_disabled(int i, struct pmu *pmu) ...@@ -1233,6 +1233,9 @@ static inline bool fixed_counter_disabled(int i, struct pmu *pmu)
int amd_pmu_init(void); int amd_pmu_init(void);
#ifdef CONFIG_PERF_EVENTS_AMD_BRS #ifdef CONFIG_PERF_EVENTS_AMD_BRS
#define AMD_FAM19H_BRS_EVENT 0xc4 /* RETIRED_TAKEN_BRANCH_INSTRUCTIONS */
int amd_brs_init(void); int amd_brs_init(void);
void amd_brs_disable(void); void amd_brs_disable(void);
void amd_brs_enable(void); void amd_brs_enable(void);
...@@ -1241,7 +1244,7 @@ void amd_brs_disable_all(void); ...@@ -1241,7 +1244,7 @@ void amd_brs_disable_all(void);
void amd_brs_drain(void); void amd_brs_drain(void);
void amd_brs_lopwr_init(void); void amd_brs_lopwr_init(void);
void amd_brs_disable_all(void); void amd_brs_disable_all(void);
int amd_brs_setup_filter(struct perf_event *event); int amd_brs_hw_config(struct perf_event *event);
void amd_brs_reset(void); void amd_brs_reset(void);
static inline void amd_pmu_brs_add(struct perf_event *event) static inline void amd_pmu_brs_add(struct perf_event *event)
...@@ -1277,7 +1280,7 @@ static inline void amd_brs_enable(void) {} ...@@ -1277,7 +1280,7 @@ static inline void amd_brs_enable(void) {}
static inline void amd_brs_drain(void) {} static inline void amd_brs_drain(void) {}
static inline void amd_brs_lopwr_init(void) {} static inline void amd_brs_lopwr_init(void) {}
static inline void amd_brs_disable_all(void) {} static inline void amd_brs_disable_all(void) {}
static inline int amd_brs_setup_filter(struct perf_event *event) static inline int amd_brs_hw_config(struct perf_event *event)
{ {
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment