CPUFREQ: Fix two racing issues during cpu hotplug

Move cpufreq_del_cpu() call into __cpu_disable to eliminate racing with dbs timer handler on policy & drv_data. Put it after local_irq_enable because xmalloc/xfree in cpufreq_del_cpu assert for this.

Change access to cpufreq_statistic_lock from spin_lock to spin_lock_irqsave to avoid statistic data access racing between cpufreq_statistic_exit and dbs timer handler.

Signed-off-by: Wei Gang <gang.wei@intel.com>

diff -r adce8bc43fcc xen/arch/x86/smpboot.c
--- a/xen/arch/x86/smpboot.c	Tue Apr 06 07:16:47 2010 +0100
+++ b/xen/arch/x86/smpboot.c	Fri Apr 09 11:54:42 2010 +0800
@@ -1293,6 +1293,7 @@ int __cpu_disable(void)
 	clear_local_APIC();
 	/* Allow any queued timer interrupts to get serviced */
 	local_irq_enable();
+	cpufreq_del_cpu(cpu);
 	mdelay(1);
 	local_irq_disable();
 
@@ -1362,8 +1363,6 @@ int cpu_down(unsigned int cpu)
 	}
 
 	printk("Prepare to bring CPU%d down...\n", cpu);
-
-	cpufreq_del_cpu(cpu);
 
 	err = stop_machine_run(take_cpu_down, NULL, cpu);
 	if (err < 0)
diff -r adce8bc43fcc xen/drivers/acpi/pmstat.c
--- a/xen/drivers/acpi/pmstat.c	Tue Apr 06 07:16:47 2010 +0100
+++ b/xen/drivers/acpi/pmstat.c	Fri Apr 09 11:39:27 2010 +0800
@@ -86,15 +86,17 @@ int do_get_pm_info(struct xen_sysctl_get
     case PMSTAT_get_pxstat:
     {
         uint32_t ct;
-        struct pm_px *pxpt = cpufreq_statistic_data[op->cpuid];
+        struct pm_px *pxpt;
         spinlock_t *cpufreq_statistic_lock = 
                    &per_cpu(cpufreq_statistic_lock, op->cpuid);
-
-        spin_lock(cpufreq_statistic_lock);
-
+        unsigned long flags;
+
+        spin_lock_irqsave(cpufreq_statistic_lock, flags);
+
+        pxpt = cpufreq_statistic_data[op->cpuid];
         if ( !pxpt || !pxpt->u.pt || !pxpt->u.trans_pt )
         {
-            spin_unlock(cpufreq_statistic_lock);
+            spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
             return -ENODATA;
         }
 
@@ -105,14 +107,14 @@ int do_get_pm_info(struct xen_sysctl_get
         ct = pmpt->perf.state_count;
         if ( copy_to_guest(op->u.getpx.trans_pt, pxpt->u.trans_pt, ct*ct) )
         {
-            spin_unlock(cpufreq_statistic_lock);
+            spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
             ret = -EFAULT;
             break;
         }
 
         if ( copy_to_guest(op->u.getpx.pt, pxpt->u.pt, ct) )
         {
-            spin_unlock(cpufreq_statistic_lock);
+            spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
             ret = -EFAULT;
             break;
         }
@@ -122,7 +124,7 @@ int do_get_pm_info(struct xen_sysctl_get
         op->u.getpx.last = pxpt->u.last;
         op->u.getpx.cur = pxpt->u.cur;
 
-        spin_unlock(cpufreq_statistic_lock);
+        spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
 
         break;
     }
diff -r adce8bc43fcc xen/drivers/cpufreq/utility.c
--- a/xen/drivers/cpufreq/utility.c	Tue Apr 06 07:16:47 2010 +0100
+++ b/xen/drivers/cpufreq/utility.c	Fri Apr 09 11:39:27 2010 +0800
@@ -67,12 +67,13 @@ void cpufreq_statistic_update(unsigned i
     struct processor_pminfo *pmpt = processor_pminfo[cpu];
     spinlock_t *cpufreq_statistic_lock = 
                &per_cpu(cpufreq_statistic_lock, cpu);
-
-    spin_lock(cpufreq_statistic_lock);
+    unsigned long flags;
+
+    spin_lock_irqsave(cpufreq_statistic_lock, flags);
 
     pxpt = cpufreq_statistic_data[cpu];
     if ( !pxpt || !pmpt ) {
-        spin_unlock(cpufreq_statistic_lock);
+        spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
         return;
     }
 
@@ -84,7 +85,7 @@ void cpufreq_statistic_update(unsigned i
 
     (*(pxpt->u.trans_pt + from * pmpt->perf.state_count + to))++;
 
-    spin_unlock(cpufreq_statistic_lock);
+    spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
 }
 
 int cpufreq_statistic_init(unsigned int cpuid)
@@ -94,15 +95,16 @@ int cpufreq_statistic_init(unsigned int 
     const struct processor_pminfo *pmpt = processor_pminfo[cpuid];
     spinlock_t *cpufreq_statistic_lock = 
                           &per_cpu(cpufreq_statistic_lock, cpuid);
+    unsigned long flags;
 
     if ( !pmpt )
         return -EINVAL;
 
-    spin_lock(cpufreq_statistic_lock);
-
+    spin_lock_irqsave(cpufreq_statistic_lock, flags);
     pxpt = cpufreq_statistic_data[cpuid];
+    spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
+
     if ( pxpt ) {
-        spin_unlock(cpufreq_statistic_lock);
         return 0;
     }
 
@@ -110,16 +112,13 @@ int cpufreq_statistic_init(unsigned int 
 
     pxpt = xmalloc(struct pm_px);
     if ( !pxpt ) {
-        spin_unlock(cpufreq_statistic_lock);
         return -ENOMEM;
     }
     memset(pxpt, 0, sizeof(*pxpt));
-    cpufreq_statistic_data[cpuid] = pxpt;
 
     pxpt->u.trans_pt = xmalloc_array(uint64_t, count * count);
     if (!pxpt->u.trans_pt) {
         xfree(pxpt);
-        spin_unlock(cpufreq_statistic_lock);
         return -ENOMEM;
     }
 
@@ -127,7 +126,6 @@ int cpufreq_statistic_init(unsigned int 
     if (!pxpt->u.pt) {
         xfree(pxpt->u.trans_pt);
         xfree(pxpt);
-        spin_unlock(cpufreq_statistic_lock);
         return -ENOMEM;
     }
 
@@ -143,8 +141,18 @@ int cpufreq_statistic_init(unsigned int 
     pxpt->prev_state_wall = NOW();
     pxpt->prev_idle_wall = get_cpu_idle_time(cpuid);
 
-    spin_unlock(cpufreq_statistic_lock);
-
+    spin_lock_irqsave(cpufreq_statistic_lock, flags);
+    if ( !cpufreq_statistic_data[cpuid] ) {
+        cpufreq_statistic_data[cpuid] = pxpt;
+        pxpt = NULL;
+    }
+    spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
+
+    if (pxpt) {
+        xfree(pxpt->u.trans_pt);
+        xfree(pxpt->u.pt);
+        xfree(pxpt);
+    }
     return 0;
 }
 
@@ -153,21 +161,18 @@ void cpufreq_statistic_exit(unsigned int
     struct pm_px *pxpt;
     spinlock_t *cpufreq_statistic_lock = 
                &per_cpu(cpufreq_statistic_lock, cpuid);
-
-    spin_lock(cpufreq_statistic_lock);
-
+    unsigned long flags;
+
+    spin_lock_irqsave(cpufreq_statistic_lock, flags);
     pxpt = cpufreq_statistic_data[cpuid];
-    if (!pxpt) {
-        spin_unlock(cpufreq_statistic_lock);
-        return;
-    }
-
-    xfree(pxpt->u.trans_pt);
-    xfree(pxpt->u.pt);
-    xfree(pxpt);
     cpufreq_statistic_data[cpuid] = NULL;
-
-    spin_unlock(cpufreq_statistic_lock);
+    spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
+
+    if (pxpt) {
+        xfree(pxpt->u.trans_pt);
+        xfree(pxpt->u.pt);
+        xfree(pxpt);
+    }
 }
 
 void cpufreq_statistic_reset(unsigned int cpuid)
@@ -177,12 +182,13 @@ void cpufreq_statistic_reset(unsigned in
     const struct processor_pminfo *pmpt = processor_pminfo[cpuid];
     spinlock_t *cpufreq_statistic_lock = 
                &per_cpu(cpufreq_statistic_lock, cpuid);
-
-    spin_lock(cpufreq_statistic_lock);
+    unsigned long flags;
+
+    spin_lock_irqsave(cpufreq_statistic_lock, flags);
 
     pxpt = cpufreq_statistic_data[cpuid];
     if ( !pmpt || !pxpt || !pxpt->u.pt || !pxpt->u.trans_pt ) {
-        spin_unlock(cpufreq_statistic_lock);
+        spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
         return;
     }
 
@@ -199,7 +205,7 @@ void cpufreq_statistic_reset(unsigned in
     pxpt->prev_state_wall = NOW();
     pxpt->prev_idle_wall = get_cpu_idle_time(cpuid);
 
-    spin_unlock(cpufreq_statistic_lock);
+    spin_unlock_irqrestore(cpufreq_statistic_lock, flags);
 }
 
 
