perf_counter: fix counter inheritance race
authorIngo Molnar <mingo@elte.hu>
Sun, 17 May 2009 09:24:08 +0000 (11:24 +0200)
committerIngo Molnar <mingo@elte.hu>
Tue, 19 May 2009 22:22:30 +0000 (00:22 +0200)
Context rotation should not occur when we are in the middle of
walking the counter list when inheriting counters ...

[ Impact: fix occasionally incorrect perf stat results ]

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
include/linux/perf_counter.h
kernel/perf_counter.c

index c8c1dfc..13cb2fb 100644 (file)
@@ -508,6 +508,7 @@ struct perf_counter_context {
        int                     nr_counters;
        int                     nr_active;
        int                     is_active;
+       int                     rr_allowed;
        struct task_struct      *task;
 
        /*
index 7af16d1..4d8f973 100644 (file)
@@ -1120,7 +1120,8 @@ void perf_counter_task_tick(struct task_struct *curr, int cpu)
        __perf_counter_task_sched_out(ctx);
 
        rotate_ctx(&cpuctx->ctx);
-       rotate_ctx(ctx);
+       if (ctx->rr_allowed)
+               rotate_ctx(ctx);
 
        perf_counter_cpu_sched_in(cpuctx, cpu);
        perf_counter_task_sched_in(curr, cpu);
@@ -3108,6 +3109,7 @@ __perf_counter_init_context(struct perf_counter_context *ctx,
        mutex_init(&ctx->mutex);
        INIT_LIST_HEAD(&ctx->counter_list);
        INIT_LIST_HEAD(&ctx->event_list);
+       ctx->rr_allowed = 1;
        ctx->task = task;
 }
 
@@ -3348,6 +3350,9 @@ void perf_counter_init_task(struct task_struct *child)
         */
        mutex_lock(&parent_ctx->mutex);
 
+       parent_ctx->rr_allowed = 0;
+       barrier(); /* irqs */
+
        /*
         * We dont have to disable NMIs - we are only looking at
         * the list, not manipulating it:
@@ -3361,6 +3366,9 @@ void perf_counter_init_task(struct task_struct *child)
                        break;
        }
 
+       barrier(); /* irqs */
+       parent_ctx->rr_allowed = 1;
+
        mutex_unlock(&parent_ctx->mutex);
 }