This patch changes the call of smp_processor_id to get_cpu()/put_cpu() pairs to avoid a race condition where a cpu switches out from under perfmon2 after it has called smp_processor_id but before it has used the value.

--
Corey Ashford
Software Engineer
IBM Linux Technology Center, Linux Toolchain
Beaverton, OR
503-578-3507
[EMAIL PROTECTED]
This patch fixes a problem with accessing the array of cpu-specific PMU
counters, where there was a race condition that could occur where we
acquire the cpu number, and then have the cpu switch out from underneath
us.  Using get_cpu()/put_cpu() fixes this problem because get_cpu()
disables preemption until put_cpu() is called again.

Index: linux-2.6/arch/powerpc/perfmon/perfmon_power6.c
===================================================================
--- linux-2.6.orig/arch/powerpc/perfmon/perfmon_power6.c        2008-07-03 
16:25:19.000000000 -0400
+++ linux-2.6/arch/powerpc/perfmon/perfmon_power6.c     2008-07-03 
17:17:11.000000000 -0400
@@ -183,7 +183,8 @@
 static u64 pfm_power6_sread(struct pfm_context *ctx, unsigned int cnum)
 {
        struct pfm_arch_context *ctx_arch = pfm_ctx_arch(ctx);
-       int cpu_num = smp_processor_id();
+       u64 temp;
+       int cpu_num;
 
        /* On POWER 6 PMC5 and PMC6 are implemented as
         * virtual counters.  See comment in pfm_power6_pmd_desc
@@ -192,24 +193,26 @@
 
        switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
        case SPRN_PMC5:
-               return ctx_arch->powergs_pmc5 + (u64)((u32)mfspr(SPRN_PMC5) - 
pmc5_start_save[cpu_num]);
-               break;
+               cpu_num = get_cpu();
+                       temp = ctx_arch->powergs_pmc5 + 
(u64)((u32)mfspr(SPRN_PMC5) - pmc5_start_save[cpu_num]);
+               put_cpu();
+               return temp;
 
        case SPRN_PMC6:
-               return ctx_arch->powergs_pmc6 + (u64)((u32)mfspr(SPRN_PMC6) - 
pmc6_start_save[cpu_num]);
-               break;
+               cpu_num = get_cpu();
+                       temp = ctx_arch->powergs_pmc6 + 
(u64)((u32)mfspr(SPRN_PMC6) - pmc6_start_save[cpu_num]);
+               put_cpu();
+               return temp;
 
        case PFM_DELTA_TB:
                return ctx_arch->delta_tb
                        + (((u64)mfspr(SPRN_TBRU) << 32) | mfspr(SPRN_TBRL))
                        - ctx_arch->delta_tb_start;
-               break;
 
        case PFM_DELTA_PURR:
                return ctx_arch->delta_purr
                        + mfspr(SPRN_PURR)
                        - ctx_arch->delta_purr_start;
-               break;
 
        default:
                BUG();
@@ -220,29 +223,33 @@
        u64 val)
 {
        struct pfm_arch_context *ctx_arch = pfm_ctx_arch(ctx);
-       int cpu_num = smp_processor_id();
+       int cpu_num;
 
        switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
        case SPRN_PMC5:
-               pmc5_start_save[cpu_num] = mfspr(SPRN_PMC5);
+               cpu_num = get_cpu();
+                       pmc5_start_save[cpu_num] = mfspr(SPRN_PMC5);
+               put_cpu();
                ctx_arch->powergs_pmc5 = val;
-               break;
+               return;
 
        case SPRN_PMC6:
-               pmc6_start_save[cpu_num] = mfspr(SPRN_PMC6);
+               cpu_num = get_cpu();
+                       pmc6_start_save[cpu_num] = mfspr(SPRN_PMC6);
+               put_cpu();
                ctx_arch->powergs_pmc6 = val;
-               break;
+               return;
 
        case PFM_DELTA_TB:
                ctx_arch->delta_tb_start =
                        (((u64)mfspr(SPRN_TBRU) << 32) | mfspr(SPRN_TBRL));
                ctx_arch->delta_tb = val;
-               break;
+               return;
 
        case PFM_DELTA_PURR:
                ctx_arch->delta_purr_start = mfspr(SPRN_PURR);
                ctx_arch->delta_purr = val;
-               break;
+               return;
 
        default:
                BUG();
@@ -282,7 +289,7 @@
 {
 
        unsigned int i, max_pmc;
-       int cpu_num = smp_processor_id();
+       int cpu_num;
        struct pfm_arch_context *ctx_arch;
 
        enable_cntrs_cnt++;
@@ -298,33 +305,28 @@
                        pfm_power6_write_pmc(i - 1, set->pmcs[i - 1]);
 
        /* save current free running HW event count */
-       pmc5_start_save[cpu_num] = mfspr(SPRN_PMC5);
-       pmc6_start_save[cpu_num] = mfspr(SPRN_PMC6);
+       cpu_num = get_cpu();
+               pmc5_start_save[cpu_num] = mfspr(SPRN_PMC5);
+               pmc6_start_save[cpu_num] = mfspr(SPRN_PMC6);
 
-       ctx_arch->delta_purr_start = mfspr(SPRN_PURR);
+               ctx_arch->delta_purr_start = mfspr(SPRN_PURR);
 
-       if (cpu_has_feature(CPU_FTR_PURR))
-               ctx_arch->delta_tb_start =
-                       ((u64)mfspr(SPRN_TBRU) << 32) | mfspr(SPRN_TBRL);
-       else
-               ctx_arch->delta_tb_start = 0;
+               if (cpu_has_feature(CPU_FTR_PURR))
+                       ctx_arch->delta_tb_start =
+                               ((u64)mfspr(SPRN_TBRU) << 32) | 
mfspr(SPRN_TBRL);
+               else
+                       ctx_arch->delta_tb_start = 0;
 
-       /* Start kernel timer for this cpu to periodically update
-        * the virtual counters.
-        */
-       init_timer(&pmc5_6_update[cpu_num]);
-       pmc5_6_update[cpu_num].function = pmc5_6_updater;
-       pmc5_6_update[cpu_num].data = (unsigned long) cpu_num;
-       pmc5_6_update[cpu_num].expires = jiffies + update_time;
-       /* context for this timer, timer will be removed if context
-        * is switched because the counters will be stopped first.
-        * NEEDS WORK, I think this is all ok, a little concerned about a
-        * race between the kernel timer going off right as the counters
-        * are being stopped and the context switching.  Need to think
-        * about this.
-        */
-       pmc5_6_ctx_arch[cpu_num] = ctx_arch;
-       add_timer(&pmc5_6_update[cpu_num]);
+               /* Start kernel timer for this cpu to periodically update
+                * the virtual counters.
+                */
+               init_timer(&pmc5_6_update[cpu_num]);
+               pmc5_6_update[cpu_num].function = pmc5_6_updater;
+               pmc5_6_update[cpu_num].data = (unsigned long) cpu_num;
+               pmc5_6_update[cpu_num].expires = jiffies + update_time;
+               pmc5_6_ctx_arch[cpu_num] = ctx_arch;
+               add_timer(&pmc5_6_update[cpu_num]);
+       put_cpu();
 }
 
 /**
@@ -335,7 +337,7 @@
                                        struct pfm_event_set *set)
 {
        struct pfm_arch_context *ctx_arch;
-       int cpu_num = smp_processor_id();
+       int cpu_num;
 
        disable_cntrs_cnt++;
 
@@ -344,13 +346,15 @@
        asm volatile ("sync");
 
        /* delete kernel update timer */
-       del_timer_sync(&pmc5_6_update[cpu_num]);
+       cpu_num = get_cpu();
+               del_timer_sync(&pmc5_6_update[cpu_num]);
 
-       /* Update the virtual pmd 5 and 6 counters from the free running
-        * HW counters
-        */
-       ctx_arch = pfm_ctx_arch(ctx);
-       delta(cpu_num, ctx_arch);
+               /* Update the virtual pmd 5 and 6 counters from the free running
+                * HW counters
+                */
+               ctx_arch = pfm_ctx_arch(ctx);
+               delta(cpu_num, ctx_arch);
+       put_cpu();
 
        ctx_arch->delta_tb +=
                (((u64)mfspr(SPRN_TBRU) << 32) | mfspr(SPRN_TBRL))
-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08
_______________________________________________
perfmon2-devel mailing list
perfmon2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/perfmon2-devel

Reply via email to