perf: Add support for exclude_idle attribute
Use the exclude_idle attribute of the perf events to avoid reading PMUs of idle CPUs. The counter values are updated when CPU enters idle and the saved value is returned when the idle CPU is queried for that event provided the attribute is set in the perf_event. Change-Id: I61f7a7474856abf67ac6dfd9e531702072e108a5 Signed-off-by: Patrick Fay <pfay@codeaurora.org>
This commit is contained in:
parent
1f0f95c5fe
commit
573979dee2
2 changed files with 89 additions and 6 deletions
|
@ -232,6 +232,11 @@ static const unsigned armv8_a57_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
|
|||
#define ARMV8_EXCLUDE_EL0 (1 << 30)
|
||||
#define ARMV8_INCLUDE_EL2 (1 << 27)
|
||||
|
||||
struct arm_pmu_and_idle_nb {
|
||||
struct arm_pmu *cpu_pmu;
|
||||
struct notifier_block perf_cpu_idle_nb;
|
||||
};
|
||||
|
||||
static inline u32 armv8pmu_pmcr_read(void)
|
||||
{
|
||||
u32 val;
|
||||
|
@ -541,8 +546,6 @@ static int armv8pmu_set_event_filter(struct hw_perf_event *event,
|
|||
{
|
||||
unsigned long config_base = 0;
|
||||
|
||||
if (attr->exclude_idle)
|
||||
return -EPERM;
|
||||
if (attr->exclude_user)
|
||||
config_base |= ARMV8_EXCLUDE_EL0;
|
||||
if (attr->exclude_kernel)
|
||||
|
@ -575,6 +578,33 @@ static inline void armv8pmu_init_usermode(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void armv8pmu_idle_update(struct arm_pmu *cpu_pmu)
|
||||
{
|
||||
struct pmu_hw_events *hw_events;
|
||||
struct perf_event *event;
|
||||
int idx;
|
||||
|
||||
if (!cpu_pmu)
|
||||
return;
|
||||
|
||||
hw_events = this_cpu_ptr(cpu_pmu->hw_events);
|
||||
|
||||
for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
|
||||
|
||||
if (!test_bit(idx, hw_events->used_mask))
|
||||
continue;
|
||||
|
||||
event = hw_events->events[idx];
|
||||
|
||||
if (!event || !event->attr.exclude_idle ||
|
||||
event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
continue;
|
||||
|
||||
cpu_pmu->pmu.read(event);
|
||||
}
|
||||
}
|
||||
|
||||
static void armv8pmu_reset(void *info)
|
||||
{
|
||||
struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
|
||||
|
@ -624,11 +654,40 @@ static void armv8pmu_read_num_pmnc_events(void *info)
|
|||
*nb_cnt += 1;
|
||||
}
|
||||
|
||||
static int perf_cpu_idle_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct arm_pmu_and_idle_nb *pmu_nb = container_of(nb,
|
||||
struct arm_pmu_and_idle_nb, perf_cpu_idle_nb);
|
||||
|
||||
if (action == IDLE_START)
|
||||
armv8pmu_idle_update(pmu_nb->cpu_pmu);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int armv8pmu_probe_num_events(struct arm_pmu *arm_pmu)
|
||||
{
|
||||
return smp_call_function_any(&arm_pmu->supported_cpus,
|
||||
int ret;
|
||||
struct arm_pmu_and_idle_nb *pmu_idle_nb;
|
||||
|
||||
pmu_idle_nb = devm_kzalloc(&arm_pmu->plat_device->dev,
|
||||
sizeof(*pmu_idle_nb), GFP_KERNEL);
|
||||
if (!pmu_idle_nb)
|
||||
return -ENOMEM;
|
||||
|
||||
pmu_idle_nb->cpu_pmu = arm_pmu;
|
||||
pmu_idle_nb->perf_cpu_idle_nb.notifier_call = perf_cpu_idle_notifier;
|
||||
idle_notifier_register(&pmu_idle_nb->perf_cpu_idle_nb);
|
||||
|
||||
ret = smp_call_function_any(&arm_pmu->supported_cpus,
|
||||
armv8pmu_read_num_pmnc_events,
|
||||
&arm_pmu->num_events, 1);
|
||||
if (!ret)
|
||||
idle_notifier_unregister(&pmu_idle_nb->perf_cpu_idle_nb);
|
||||
return ret;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void armv8_pmu_init(struct arm_pmu *cpu_pmu)
|
||||
|
|
|
@ -158,6 +158,7 @@ enum event_type_t {
|
|||
struct static_key_deferred perf_sched_events __read_mostly;
|
||||
static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
|
||||
static DEFINE_PER_CPU(int, perf_sched_cb_usages);
|
||||
static DEFINE_PER_CPU(bool, is_idle);
|
||||
|
||||
static atomic_t nr_mmap_events __read_mostly;
|
||||
static atomic_t nr_comm_events __read_mostly;
|
||||
|
@ -3388,9 +3389,12 @@ static int perf_event_read(struct perf_event *event, bool group)
|
|||
.group = group,
|
||||
.ret = 0,
|
||||
};
|
||||
smp_call_function_single(event->oncpu,
|
||||
__perf_event_read, &data, 1);
|
||||
ret = data.ret;
|
||||
if (!event->attr.exclude_idle ||
|
||||
!per_cpu(is_idle, event->oncpu)) {
|
||||
smp_call_function_single(event->oncpu,
|
||||
__perf_event_read, &data, 1);
|
||||
ret = data.ret;
|
||||
}
|
||||
} else if (event->state == PERF_EVENT_STATE_INACTIVE) {
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
unsigned long flags;
|
||||
|
@ -9479,6 +9483,25 @@ perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int event_idle_notif(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
switch (action) {
|
||||
case IDLE_START:
|
||||
__this_cpu_write(is_idle, true);
|
||||
break;
|
||||
case IDLE_END:
|
||||
__this_cpu_write(is_idle, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block perf_event_idle_nb = {
|
||||
.notifier_call = event_idle_notif,
|
||||
};
|
||||
|
||||
void __init perf_event_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -9492,6 +9515,7 @@ void __init perf_event_init(void)
|
|||
perf_pmu_register(&perf_task_clock, NULL, -1);
|
||||
perf_tp_register();
|
||||
perf_cpu_notifier(perf_cpu_notify);
|
||||
idle_notifier_register(&perf_event_idle_nb);
|
||||
register_reboot_notifier(&perf_reboot_notifier);
|
||||
|
||||
ret = init_hw_breakpoint();
|
||||
|
|
Loading…
Add table
Reference in a new issue