|
@@ -469,7 +469,8 @@ static void update_counter_times(struct perf_counter *counter)
|
|
|
struct perf_counter_context *ctx = counter->ctx;
|
|
|
u64 run_end;
|
|
|
|
|
|
- if (counter->state < PERF_COUNTER_STATE_INACTIVE)
|
|
|
+ if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
|
|
|
+ counter->group_leader->state < PERF_COUNTER_STATE_INACTIVE)
|
|
|
return;
|
|
|
|
|
|
counter->total_time_enabled = ctx->time - counter->tstamp_enabled;
|
|
@@ -518,7 +519,7 @@ static void __perf_counter_disable(void *info)
|
|
|
*/
|
|
|
if (counter->state >= PERF_COUNTER_STATE_INACTIVE) {
|
|
|
update_context_time(ctx);
|
|
|
- update_counter_times(counter);
|
|
|
+ update_group_times(counter);
|
|
|
if (counter == counter->group_leader)
|
|
|
group_sched_out(counter, cpuctx, ctx);
|
|
|
else
|
|
@@ -573,7 +574,7 @@ static void perf_counter_disable(struct perf_counter *counter)
|
|
|
* in, so we can change the state safely.
|
|
|
*/
|
|
|
if (counter->state == PERF_COUNTER_STATE_INACTIVE) {
|
|
|
- update_counter_times(counter);
|
|
|
+ update_group_times(counter);
|
|
|
counter->state = PERF_COUNTER_STATE_OFF;
|
|
|
}
|
|
|
|
|
@@ -850,6 +851,27 @@ retry:
|
|
|
spin_unlock_irq(&ctx->lock);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Put a counter into inactive state and update time fields.
|
|
|
+ * Enabling the leader of a group effectively enables all
|
|
|
+ * the group members that aren't explicitly disabled, so we
|
|
|
+ * have to update their ->tstamp_enabled also.
|
|
|
+ * Note: this works for group members as well as group leaders
|
|
|
+ * since the non-leader members' sibling_lists will be empty.
|
|
|
+ */
|
|
|
+static void __perf_counter_mark_enabled(struct perf_counter *counter,
|
|
|
+ struct perf_counter_context *ctx)
|
|
|
+{
|
|
|
+ struct perf_counter *sub;
|
|
|
+
|
|
|
+ counter->state = PERF_COUNTER_STATE_INACTIVE;
|
|
|
+ counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
|
|
|
+ list_for_each_entry(sub, &counter->sibling_list, list_entry)
|
|
|
+ if (sub->state >= PERF_COUNTER_STATE_INACTIVE)
|
|
|
+ sub->tstamp_enabled =
|
|
|
+ ctx->time - sub->total_time_enabled;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Cross CPU call to enable a performance counter
|
|
|
*/
|
|
@@ -877,8 +899,7 @@ static void __perf_counter_enable(void *info)
|
|
|
|
|
|
if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
|
|
|
goto unlock;
|
|
|
- counter->state = PERF_COUNTER_STATE_INACTIVE;
|
|
|
- counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
|
|
|
+ __perf_counter_mark_enabled(counter, ctx);
|
|
|
|
|
|
/*
|
|
|
* If the counter is in a group and isn't the group leader,
|
|
@@ -971,11 +992,9 @@ static void perf_counter_enable(struct perf_counter *counter)
|
|
|
* Since we have the lock this context can't be scheduled
|
|
|
* in, so we can change the state safely.
|
|
|
*/
|
|
|
- if (counter->state == PERF_COUNTER_STATE_OFF) {
|
|
|
- counter->state = PERF_COUNTER_STATE_INACTIVE;
|
|
|
- counter->tstamp_enabled =
|
|
|
- ctx->time - counter->total_time_enabled;
|
|
|
- }
|
|
|
+ if (counter->state == PERF_COUNTER_STATE_OFF)
|
|
|
+ __perf_counter_mark_enabled(counter, ctx);
|
|
|
+
|
|
|
out:
|
|
|
spin_unlock_irq(&ctx->lock);
|
|
|
}
|
|
@@ -1479,9 +1498,7 @@ static void perf_counter_enable_on_exec(struct task_struct *task)
|
|
|
counter->attr.enable_on_exec = 0;
|
|
|
if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
|
|
|
continue;
|
|
|
- counter->state = PERF_COUNTER_STATE_INACTIVE;
|
|
|
- counter->tstamp_enabled =
|
|
|
- ctx->time - counter->total_time_enabled;
|
|
|
+ __perf_counter_mark_enabled(counter, ctx);
|
|
|
enabled = 1;
|
|
|
}
|
|
|
|