Re: [RFC][PATCH] perf: Rewrite enabled/running timekeeping

From: Peter Zijlstra
Date: Mon Sep 04 2017 - 11:58:43 EST


On Mon, Sep 04, 2017 at 05:41:45PM +0200, Peter Zijlstra wrote:

> > >> U - allocation, A - ACTIVE, I - INACTIVE, O - OFF,
> > >> E - ERROR, X - EXIT, D - DEAD,
> > >
> > > Not sure we care about the different <0 values, they're all effectively
> > > OFF.
> >
> > We still need to care about proper initial state of timings when moving above >=0 state.
>
> Very true. I'm not sure I fully covered that, let me see if there's
> something sensible to do for that.


So given this:

static __always_inline enum perf_event_state
__perf_effective_state(struct perf_event *event)
{
struct perf_event *leader = event->group_leader;

if (leader->state <= PERF_EVENT_STATE_OFF)
return leader->state;

return event->state;
}

static __always_inline void
__perf_update_times(struct perf_event *event, u64 now, u64 *enabled, u64 *running)
{
enum perf_event_state state = __perf_effective_state(event);
u64 delta = now - event->tstamp;

*enabled = event->total_time_enabled;
if (state >= PERF_EVENT_STATE_INACTIVE)
*enabled += delta;

*running = event->total_time_running;
if (state >= PERF_EVENT_STATE_ACTIVE)
*running += delta;
}

static void perf_event_update_time(struct perf_event *event)
{
u64 now = perf_event_time(event);

__perf_update_times(event, now, &event->total_time_enabled,
&event->total_time_running);
event->tstamp = now;
}

static void perf_event_update_sibling_time(struct perf_event *leader)
{
struct perf_event *sibling;

list_for_each_entry(sibling, &leader->sibling_list, group_entry)
perf_event_update_time(sibling);
}

static void
perf_event_set_state(struct perf_event *event, enum perf_event_state state)
{
if (event->state == state)
return;

perf_event_update_time(event);
/*
* If a group leader gets enabled/disabled all its siblings
* are affected too.
*/
if ((event->state < 0) ^ (state < 0))
perf_event_update_sibling_time(event);

WRITE_ONCE(event->state, state);
}


If event->state < 0, and we do perf_event_set_state(event, INACTIVE)
then perf_event_update_time() will not add to enabled, not add to
running, but set ->tstamp = now.

So I think it DTRT.