perf/core: Fix time on IOC_ENABLE
Vince reported that when we do IOC_ENABLE/IOC_DISABLE while the task is SIGSTOP'ed state the timestamps go wobbly. It turns out we indeed fail to correctly account time while in 'OFF' state and doing IOC_ENABLE without getting scheduled in exposes the problem. Further thinking about this problem, it occurred to me that we can suffer a similar fate when we migrate an uncore event between CPUs. The perf_event_install() on the 'new' CPU will do add_event_to_ctx() which will reset all the time stamp, resulting in a subsequent update_event_times() to overwrite the total_time_* fields with smaller values. Reported-by: Vince Weaver <vincent.weaver@maine.edu> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
bfe334924c
commit
9b231d9f47
@ -2217,6 +2217,33 @@ static int group_can_go_on(struct perf_event *event,
|
||||
return can_add_hw;
|
||||
}
|
||||
|
||||
/*
|
||||
* Complement to update_event_times(). This computes the tstamp_* values to
|
||||
* continue 'enabled' state from @now, and effectively discards the time
|
||||
* between the prior tstamp_stopped and now (as we were in the OFF state, or
|
||||
* just switched (context) time base).
|
||||
*
|
||||
* This further assumes '@event->state == INACTIVE' (we just came from OFF) and
|
||||
* cannot have been scheduled in yet. And going into INACTIVE state means
|
||||
* '@event->tstamp_stopped = @now'.
|
||||
*
|
||||
* Thus given the rules of update_event_times():
|
||||
*
|
||||
* total_time_enabled = tstamp_stopped - tstamp_enabled
|
||||
* total_time_running = tstamp_stopped - tstamp_running
|
||||
*
|
||||
* We can insert 'tstamp_stopped == now' and reverse them to compute new
|
||||
* tstamp_* values.
|
||||
*/
|
||||
static void __perf_event_enable_time(struct perf_event *event, u64 now)
|
||||
{
|
||||
WARN_ON_ONCE(event->state != PERF_EVENT_STATE_INACTIVE);
|
||||
|
||||
event->tstamp_stopped = now;
|
||||
event->tstamp_enabled = now - event->total_time_enabled;
|
||||
event->tstamp_running = now - event->total_time_running;
|
||||
}
|
||||
|
||||
static void add_event_to_ctx(struct perf_event *event,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
@ -2224,9 +2251,12 @@ static void add_event_to_ctx(struct perf_event *event,
|
||||
|
||||
list_add_event(event, ctx);
|
||||
perf_group_attach(event);
|
||||
event->tstamp_enabled = tstamp;
|
||||
event->tstamp_running = tstamp;
|
||||
event->tstamp_stopped = tstamp;
|
||||
/*
|
||||
* We can be called with event->state == STATE_OFF when we create with
|
||||
* .disabled = 1. In that case the IOC_ENABLE will call this function.
|
||||
*/
|
||||
if (event->state == PERF_EVENT_STATE_INACTIVE)
|
||||
__perf_event_enable_time(event, tstamp);
|
||||
}
|
||||
|
||||
static void ctx_sched_out(struct perf_event_context *ctx,
|
||||
@ -2471,10 +2501,11 @@ static void __perf_event_mark_enabled(struct perf_event *event)
|
||||
u64 tstamp = perf_event_time(event);
|
||||
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
event->tstamp_enabled = tstamp - event->total_time_enabled;
|
||||
__perf_event_enable_time(event, tstamp);
|
||||
list_for_each_entry(sub, &event->sibling_list, group_entry) {
|
||||
/* XXX should not be > INACTIVE if event isn't */
|
||||
if (sub->state >= PERF_EVENT_STATE_INACTIVE)
|
||||
sub->tstamp_enabled = tstamp - sub->total_time_enabled;
|
||||
__perf_event_enable_time(sub, tstamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user