aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/perf_event.h6
-rw-r--r--kernel/events/core.c49
2 files changed, 44 insertions, 11 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 39156619e108..f5c5a3fa2c81 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -906,7 +906,7 @@ perf_sw_event_sched(u32 event_id, u64 nr, u64 addr)
906 } 906 }
907} 907}
908 908
909extern struct static_key_deferred perf_sched_events; 909extern struct static_key_false perf_sched_events;
910 910
911static __always_inline bool 911static __always_inline bool
912perf_sw_migrate_enabled(void) 912perf_sw_migrate_enabled(void)
@@ -925,7 +925,7 @@ static inline void perf_event_task_migrate(struct task_struct *task)
925static inline void perf_event_task_sched_in(struct task_struct *prev, 925static inline void perf_event_task_sched_in(struct task_struct *prev,
926 struct task_struct *task) 926 struct task_struct *task)
927{ 927{
928 if (static_key_false(&perf_sched_events.key)) 928 if (static_branch_unlikely(&perf_sched_events))
929 __perf_event_task_sched_in(prev, task); 929 __perf_event_task_sched_in(prev, task);
930 930
931 if (perf_sw_migrate_enabled() && task->sched_migrated) { 931 if (perf_sw_migrate_enabled() && task->sched_migrated) {
@@ -942,7 +942,7 @@ static inline void perf_event_task_sched_out(struct task_struct *prev,
942{ 942{
943 perf_sw_event_sched(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 0); 943 perf_sw_event_sched(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 0);
944 944
945 if (static_key_false(&perf_sched_events.key)) 945 if (static_branch_unlikely(&perf_sched_events))
946 __perf_event_task_sched_out(prev, next); 946 __perf_event_task_sched_out(prev, next);
947} 947}
948 948
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 92d6999a4f2f..ea064ca8dd3c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -321,7 +321,13 @@ enum event_type_t {
321 * perf_sched_events : >0 events exist 321 * perf_sched_events : >0 events exist
322 * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu 322 * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu
323 */ 323 */
324struct static_key_deferred perf_sched_events __read_mostly; 324
325static void perf_sched_delayed(struct work_struct *work);
326DEFINE_STATIC_KEY_FALSE(perf_sched_events);
327static DECLARE_DELAYED_WORK(perf_sched_work, perf_sched_delayed);
328static DEFINE_MUTEX(perf_sched_mutex);
329static atomic_t perf_sched_count;
330
325static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); 331static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
326static DEFINE_PER_CPU(int, perf_sched_cb_usages); 332static DEFINE_PER_CPU(int, perf_sched_cb_usages);
327 333
@@ -3536,12 +3542,22 @@ static void unaccount_event(struct perf_event *event)
3536 if (has_branch_stack(event)) 3542 if (has_branch_stack(event))
3537 dec = true; 3543 dec = true;
3538 3544
3539 if (dec) 3545 if (dec) {
3540 static_key_slow_dec_deferred(&perf_sched_events); 3546 if (!atomic_add_unless(&perf_sched_count, -1, 1))
3547 schedule_delayed_work(&perf_sched_work, HZ);
3548 }
3541 3549
3542 unaccount_event_cpu(event, event->cpu); 3550 unaccount_event_cpu(event, event->cpu);
3543} 3551}
3544 3552
3553static void perf_sched_delayed(struct work_struct *work)
3554{
3555 mutex_lock(&perf_sched_mutex);
3556 if (atomic_dec_and_test(&perf_sched_count))
3557 static_branch_disable(&perf_sched_events);
3558 mutex_unlock(&perf_sched_mutex);
3559}
3560
3545/* 3561/*
3546 * The following implement mutual exclusion of events on "exclusive" pmus 3562 * The following implement mutual exclusion of events on "exclusive" pmus
3547 * (PERF_PMU_CAP_EXCLUSIVE). Such pmus can only have one event scheduled 3563 * (PERF_PMU_CAP_EXCLUSIVE). Such pmus can only have one event scheduled
@@ -7780,8 +7796,28 @@ static void account_event(struct perf_event *event)
7780 if (is_cgroup_event(event)) 7796 if (is_cgroup_event(event))
7781 inc = true; 7797 inc = true;
7782 7798
7783 if (inc) 7799 if (inc) {
7784 static_key_slow_inc(&perf_sched_events.key); 7800 if (atomic_inc_not_zero(&perf_sched_count))
7801 goto enabled;
7802
7803 mutex_lock(&perf_sched_mutex);
7804 if (!atomic_read(&perf_sched_count)) {
7805 static_branch_enable(&perf_sched_events);
7806 /*
7807 * Guarantee that all CPUs observe they key change and
7808 * call the perf scheduling hooks before proceeding to
7809 * install events that need them.
7810 */
7811 synchronize_sched();
7812 }
7813 /*
7814 * Now that we have waited for the sync_sched(), allow further
7815 * increments to by-pass the mutex.
7816 */
7817 atomic_inc(&perf_sched_count);
7818 mutex_unlock(&perf_sched_mutex);
7819 }
7820enabled:
7785 7821
7786 account_event_cpu(event, event->cpu); 7822 account_event_cpu(event, event->cpu);
7787} 7823}
@@ -9344,9 +9380,6 @@ void __init perf_event_init(void)
9344 ret = init_hw_breakpoint(); 9380 ret = init_hw_breakpoint();
9345 WARN(ret, "hw_breakpoint initialization failed with: %d", ret); 9381 WARN(ret, "hw_breakpoint initialization failed with: %d", ret);
9346 9382
9347 /* do not patch jump label more than once per second */
9348 jump_label_rate_limit(&perf_sched_events, HZ);
9349
9350 /* 9383 /*
9351 * Build time assertion that we keep the data_head at the intended 9384 * Build time assertion that we keep the data_head at the intended
9352 * location. IOW, validation we got the __reserved[] size right. 9385 * location. IOW, validation we got the __reserved[] size right.