Skip to content

Commit 272325c

Browse files
Peter ZijlstraKAGA-KOKO
authored andcommitted
perf: Fix mux_interval hrtimer wreckage
Thomas stumbled over the hrtimer_forward_now() in perf_event_mux_interval_ms_store() and noticed its broken-ness. You cannot just change the expiry time of an active timer, it will destroy the red-black tree order and cause havoc. Change it to (re)start the timer instead, (re)starting a timer will dequeue and enqueue a timer and therefore preserve rb-tree order. Since we cannot enqueue remotely, wrap the thing in cpu_function_call(), this however mandates that we restrict ourselves to online cpus. Also serialize the entire setting so we don't get multiple concurrent threads trying to update to different values. Also fix a problem in perf_mux_hrtimer_restart(), checking against hrtimer_active() can actually loose us the timer when timer->state == HRTIMER_STATE_CALLBACK and the callback has already decided NORESTART. Furthermore it doesn't make any sense to test hrtimer_callback_running() when we already tested hrtimer_active(), but with the above change, we explicitly must call it when callback_running. Lastly, rename a few functions: s/perf_cpu_hrtimer_/perf_mux_hrtimer_/ -- because I could not find the mux timer function s/\<hr\>/timer/ -- because that's the normal way of calling things. Fixes: 62b8563 ("perf: Add sysfs entry to adjust multiplexing interval per PMU") Reported-by: Thomas Gleixner <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 77a4d1a commit 272325c

File tree

1 file changed

+36
-27
lines changed

1 file changed

+36
-27
lines changed

kernel/events/core.c

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@
5151

5252
static struct workqueue_struct *perf_wq;
5353

54+
typedef int (*remote_function_f)(void *);
55+
5456
struct remote_function_call {
5557
struct task_struct *p;
56-
int (*func)(void *info);
58+
remote_function_f func;
5759
void *info;
5860
int ret;
5961
};
@@ -86,7 +88,7 @@ static void remote_function(void *data)
8688
* -EAGAIN - when the process moved away
8789
*/
8890
static int
89-
task_function_call(struct task_struct *p, int (*func) (void *info), void *info)
91+
task_function_call(struct task_struct *p, remote_function_f func, void *info)
9092
{
9193
struct remote_function_call data = {
9294
.p = p,
@@ -110,7 +112,7 @@ task_function_call(struct task_struct *p, int (*func) (void *info), void *info)
110112
*
111113
* returns: @func return value or -ENXIO when the cpu is offline
112114
*/
113-
static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
115+
static int cpu_function_call(int cpu, remote_function_f func, void *info)
114116
{
115117
struct remote_function_call data = {
116118
.p = NULL,
@@ -747,7 +749,7 @@ perf_cgroup_mark_enabled(struct perf_event *event,
747749
/*
748750
* function must be called with interrupts disbled
749751
*/
750-
static enum hrtimer_restart perf_cpu_hrtimer_handler(struct hrtimer *hr)
752+
static enum hrtimer_restart perf_mux_hrtimer_handler(struct hrtimer *hr)
751753
{
752754
struct perf_cpu_context *cpuctx;
753755
enum hrtimer_restart ret = HRTIMER_NORESTART;
@@ -771,7 +773,7 @@ static enum hrtimer_restart perf_cpu_hrtimer_handler(struct hrtimer *hr)
771773
}
772774

773775
/* CPU is going down */
774-
void perf_cpu_hrtimer_cancel(int cpu)
776+
void perf_mux_hrtimer_cancel(int cpu)
775777
{
776778
struct perf_cpu_context *cpuctx;
777779
struct pmu *pmu;
@@ -798,11 +800,11 @@ void perf_cpu_hrtimer_cancel(int cpu)
798800
local_irq_restore(flags);
799801
}
800802

801-
static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
803+
static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
802804
{
803-
struct hrtimer *hr = &cpuctx->hrtimer;
805+
struct hrtimer *timer = &cpuctx->hrtimer;
804806
struct pmu *pmu = cpuctx->ctx.pmu;
805-
int timer;
807+
u64 interval;
806808

807809
/* no multiplexing needed for SW PMU */
808810
if (pmu->task_ctx_nr == perf_sw_context)
@@ -812,29 +814,30 @@ static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
812814
* check default is sane, if not set then force to
813815
* default interval (1/tick)
814816
*/
815-
timer = pmu->hrtimer_interval_ms;
816-
if (timer < 1)
817-
timer = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
817+
interval = pmu->hrtimer_interval_ms;
818+
if (interval < 1)
819+
interval = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
818820

819-
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
821+
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
820822

821-
hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
822-
hr->function = perf_cpu_hrtimer_handler;
823+
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
824+
timer->function = perf_mux_hrtimer_handler;
823825
}
824826

825-
static void perf_cpu_hrtimer_restart(struct perf_cpu_context *cpuctx)
827+
static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
826828
{
827-
struct hrtimer *hr = &cpuctx->hrtimer;
829+
struct hrtimer *timer = &cpuctx->hrtimer;
828830
struct pmu *pmu = cpuctx->ctx.pmu;
829831

830832
/* not for SW PMU */
831833
if (pmu->task_ctx_nr == perf_sw_context)
832-
return;
834+
return 0;
833835

834-
if (hrtimer_active(hr))
835-
return;
836+
if (hrtimer_is_queued(timer))
837+
return 0;
836838

837-
hrtimer_start(hr, cpuctx->hrtimer_interval, HRTIMER_MODE_REL_PINNED);
839+
hrtimer_start(timer, cpuctx->hrtimer_interval, HRTIMER_MODE_REL_PINNED);
840+
return 0;
838841
}
839842

840843
void perf_pmu_disable(struct pmu *pmu)
@@ -1913,7 +1916,7 @@ group_sched_in(struct perf_event *group_event,
19131916

19141917
if (event_sched_in(group_event, cpuctx, ctx)) {
19151918
pmu->cancel_txn(pmu);
1916-
perf_cpu_hrtimer_restart(cpuctx);
1919+
perf_mux_hrtimer_restart(cpuctx);
19171920
return -EAGAIN;
19181921
}
19191922

@@ -1960,7 +1963,7 @@ group_sched_in(struct perf_event *group_event,
19601963

19611964
pmu->cancel_txn(pmu);
19621965

1963-
perf_cpu_hrtimer_restart(cpuctx);
1966+
perf_mux_hrtimer_restart(cpuctx);
19641967

19651968
return -EAGAIN;
19661969
}
@@ -2233,7 +2236,7 @@ static int __perf_event_enable(void *info)
22332236
*/
22342237
if (leader != event) {
22352238
group_sched_out(leader, cpuctx, ctx);
2236-
perf_cpu_hrtimer_restart(cpuctx);
2239+
perf_mux_hrtimer_restart(cpuctx);
22372240
}
22382241
if (leader->attr.pinned) {
22392242
update_group_times(leader);
@@ -7143,6 +7146,8 @@ perf_event_mux_interval_ms_show(struct device *dev,
71437146
return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->hrtimer_interval_ms);
71447147
}
71457148

7149+
static DEFINE_MUTEX(mux_interval_mutex);
7150+
71467151
static ssize_t
71477152
perf_event_mux_interval_ms_store(struct device *dev,
71487153
struct device_attribute *attr,
@@ -7162,17 +7167,21 @@ perf_event_mux_interval_ms_store(struct device *dev,
71627167
if (timer == pmu->hrtimer_interval_ms)
71637168
return count;
71647169

7170+
mutex_lock(&mux_interval_mutex);
71657171
pmu->hrtimer_interval_ms = timer;
71667172

71677173
/* update all cpuctx for this PMU */
7168-
for_each_possible_cpu(cpu) {
7174+
get_online_cpus();
7175+
for_each_online_cpu(cpu) {
71697176
struct perf_cpu_context *cpuctx;
71707177
cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
71717178
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
71727179

7173-
if (hrtimer_active(&cpuctx->hrtimer))
7174-
hrtimer_forward_now(&cpuctx->hrtimer, cpuctx->hrtimer_interval);
7180+
cpu_function_call(cpu,
7181+
(remote_function_f)perf_mux_hrtimer_restart, cpuctx);
71757182
}
7183+
put_online_cpus();
7184+
mutex_unlock(&mux_interval_mutex);
71767185

71777186
return count;
71787187
}
@@ -7277,7 +7286,7 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
72777286
lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
72787287
cpuctx->ctx.pmu = pmu;
72797288

7280-
__perf_cpu_hrtimer_init(cpuctx, cpu);
7289+
__perf_mux_hrtimer_init(cpuctx, cpu);
72817290

72827291
cpuctx->unique_pmu = pmu;
72837292
}

0 commit comments

Comments
 (0)