Skip to content

Commit 0259bf6

Browse files
namhyungingomolnar
authored andcommitted
perf/core: Optimize perf_adjust_freq_unthr_context()
It was unnecessarily disabling and enabling PMUs for each event. It should be done at PMU level. Add pmu_ctx->nr_freq counter to check it at each PMU. As PMU context has separate active lists for pinned group and flexible group, factor out a new function to do the job. Another minor optimization is that it can skip PMUs w/ CAP_NO_INTERRUPT even if it needs to unthrottle sampling events. Signed-off-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Tested-by: Mingwei Zhang <mizhang@google.com> Reviewed-by: Ian Rogers <irogers@google.com> Reviewed-by: Kan Liang <kan.liang@linux.intel.com> Link: https://lore.kernel.org/r/20240207050545.2727923-1-namhyung@kernel.org
1 parent 9794563 commit 0259bf6

2 files changed

Lines changed: 50 additions & 26 deletions

File tree

include/linux/perf_event.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ struct perf_event_pmu_context {
883883

884884
unsigned int nr_events;
885885
unsigned int nr_cgroups;
886+
unsigned int nr_freq;
886887

887888
atomic_t refcount; /* event <-> epc */
888889
struct rcu_head rcu_head;
@@ -897,6 +898,11 @@ struct perf_event_pmu_context {
897898
int rotate_necessary;
898899
};
899900

901+
static inline bool perf_pmu_ctx_is_active(struct perf_event_pmu_context *epc)
902+
{
903+
return !list_empty(&epc->flexible_active) || !list_empty(&epc->pinned_active);
904+
}
905+
900906
struct perf_event_groups {
901907
struct rb_root tree;
902908
u64 index;

kernel/events/core.c

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,8 +2302,10 @@ event_sched_out(struct perf_event *event, struct perf_event_context *ctx)
23022302

23032303
if (!is_software_event(event))
23042304
cpc->active_oncpu--;
2305-
if (event->attr.freq && event->attr.sample_freq)
2305+
if (event->attr.freq && event->attr.sample_freq) {
23062306
ctx->nr_freq--;
2307+
epc->nr_freq--;
2308+
}
23072309
if (event->attr.exclusive || !cpc->active_oncpu)
23082310
cpc->exclusive = 0;
23092311

@@ -2558,9 +2560,10 @@ event_sched_in(struct perf_event *event, struct perf_event_context *ctx)
25582560

25592561
if (!is_software_event(event))
25602562
cpc->active_oncpu++;
2561-
if (event->attr.freq && event->attr.sample_freq)
2563+
if (event->attr.freq && event->attr.sample_freq) {
25622564
ctx->nr_freq++;
2563-
2565+
epc->nr_freq++;
2566+
}
25642567
if (event->attr.exclusive)
25652568
cpc->exclusive = 1;
25662569

@@ -4123,39 +4126,21 @@ static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count, bo
41234126
}
41244127
}
41254128

4126-
/*
4127-
* combine freq adjustment with unthrottling to avoid two passes over the
4128-
* events. At the same time, make sure, having freq events does not change
4129-
* the rate of unthrottling as that would introduce bias.
4130-
*/
4131-
static void
4132-
perf_adjust_freq_unthr_context(struct perf_event_context *ctx, bool unthrottle)
4129+
static void perf_adjust_freq_unthr_events(struct list_head *event_list)
41334130
{
41344131
struct perf_event *event;
41354132
struct hw_perf_event *hwc;
41364133
u64 now, period = TICK_NSEC;
41374134
s64 delta;
41384135

4139-
/*
4140-
* only need to iterate over all events iff:
4141-
* - context have events in frequency mode (needs freq adjust)
4142-
* - there are events to unthrottle on this cpu
4143-
*/
4144-
if (!(ctx->nr_freq || unthrottle))
4145-
return;
4146-
4147-
raw_spin_lock(&ctx->lock);
4148-
4149-
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
4136+
list_for_each_entry(event, event_list, active_list) {
41504137
if (event->state != PERF_EVENT_STATE_ACTIVE)
41514138
continue;
41524139

41534140
// XXX use visit thingy to avoid the -1,cpu match
41544141
if (!event_filter_match(event))
41554142
continue;
41564143

4157-
perf_pmu_disable(event->pmu);
4158-
41594144
hwc = &event->hw;
41604145

41614146
if (hwc->interrupts == MAX_INTERRUPTS) {
@@ -4165,7 +4150,7 @@ perf_adjust_freq_unthr_context(struct perf_event_context *ctx, bool unthrottle)
41654150
}
41664151

41674152
if (!event->attr.freq || !event->attr.sample_freq)
4168-
goto next;
4153+
continue;
41694154

41704155
/*
41714156
* stop the event and update event->count
@@ -4187,8 +4172,41 @@ perf_adjust_freq_unthr_context(struct perf_event_context *ctx, bool unthrottle)
41874172
perf_adjust_period(event, period, delta, false);
41884173

41894174
event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0);
4190-
next:
4191-
perf_pmu_enable(event->pmu);
4175+
}
4176+
}
4177+
4178+
/*
4179+
* combine freq adjustment with unthrottling to avoid two passes over the
4180+
* events. At the same time, make sure, having freq events does not change
4181+
* the rate of unthrottling as that would introduce bias.
4182+
*/
4183+
static void
4184+
perf_adjust_freq_unthr_context(struct perf_event_context *ctx, bool unthrottle)
4185+
{
4186+
struct perf_event_pmu_context *pmu_ctx;
4187+
4188+
/*
4189+
* only need to iterate over all events iff:
4190+
* - context have events in frequency mode (needs freq adjust)
4191+
* - there are events to unthrottle on this cpu
4192+
*/
4193+
if (!(ctx->nr_freq || unthrottle))
4194+
return;
4195+
4196+
raw_spin_lock(&ctx->lock);
4197+
4198+
list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry) {
4199+
if (!(pmu_ctx->nr_freq || unthrottle))
4200+
continue;
4201+
if (!perf_pmu_ctx_is_active(pmu_ctx))
4202+
continue;
4203+
if (pmu_ctx->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT)
4204+
continue;
4205+
4206+
perf_pmu_disable(pmu_ctx->pmu);
4207+
perf_adjust_freq_unthr_events(&pmu_ctx->pinned_active);
4208+
perf_adjust_freq_unthr_events(&pmu_ctx->flexible_active);
4209+
perf_pmu_enable(pmu_ctx->pmu);
41924210
}
41934211

41944212
raw_spin_unlock(&ctx->lock);

0 commit comments

Comments
 (0)