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