x86: Add cpufreq logic to S3 suspend/resume
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 27 Jun 2008 15:16:47 +0000 (16:16 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 27 Jun 2008 15:16:47 +0000 (16:16 +0100)
When suspend to S3, stop the cpufreq dbs governor. When resume from
S3, firstly sync cpu state and freq at the 1st dbs timer; from 2nd dbs
timer on, cpufreq dbs governor control cpu px transfer according to
its workload algorithm. Px statistic is also handled.

Signed-off-by: Liu Jinsong <jinsong.liu@intel.com>
xen/arch/x86/acpi/cpufreq/cpufreq.c
xen/arch/x86/acpi/cpufreq/cpufreq_ondemand.c
xen/arch/x86/acpi/cpufreq/utility.c
xen/arch/x86/acpi/power.c
xen/include/acpi/cpufreq/cpufreq.h
xen/include/acpi/cpufreq/processor_perf.h

index 53b7f0cd457e11a3d94d404128e304daa9e2cc7c..b3c02279d08cc928ff63b8be370ab8609500dc05 100644 (file)
 struct processor_pminfo processor_pminfo[NR_CPUS];
 struct cpufreq_policy xen_px_policy[NR_CPUS];
 
+static cpumask_t *cpufreq_dom_pt;
+static cpumask_t cpufreq_dom_mask;
+static unsigned int cpufreq_dom_max;
+
 enum {
     UNDEFINED_CAPABLE = 0,
     SYSTEM_INTEL_MSR_CAPABLE,
@@ -60,7 +64,6 @@ struct acpi_cpufreq_data {
     struct processor_performance *acpi_data;
     struct cpufreq_frequency_table *freq_table;
     unsigned int max_freq;
-    unsigned int resume;
     unsigned int cpu_feature;
 };
 
@@ -328,14 +331,16 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
 
     next_perf_state = data->freq_table[next_state].index;
     if (perf->state == next_perf_state) {
-        if (unlikely(data->resume)) {
-            printk("xen_pminfo: @acpi_cpufreq_target, "
-                "Called after resume, resetting to P%d\n", 
+        if (unlikely(policy->resume)) {
+            printk(KERN_INFO "Called after resume, resetting to P%d\n", 
                 next_perf_state);
-            data->resume = 0;
+            policy->resume = 0;
         }
-        else
+        else {
+            printk(KERN_INFO "Already at target state (P%d)\n", 
+                next_perf_state);
             return 0;
+        }
     }
 
     switch (data->cpu_feature) {
@@ -531,7 +536,7 @@ acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
      * the first call to ->target() should result in us actually
      * writing something to the appropriate registers.
      */
-    data->resume = 1;
+    policy->resume = 1;
 
     return result;
 
@@ -549,61 +554,101 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
     .init   = acpi_cpufreq_cpu_init,
 };
 
-int acpi_cpufreq_init(void)
+void cpufreq_dom_exit(void)
 {
-    unsigned int i, ret = 0;
-    unsigned int dom, max_dom = 0;
-    cpumask_t *pt, dom_mask;
+    cpufreq_dom_max = 0;
+    cpus_clear(cpufreq_dom_mask);
+    if (cpufreq_dom_pt)
+        xfree(cpufreq_dom_pt);
+}
+
+int cpufreq_dom_init(void)
+{
+    unsigned int i;
 
-    cpus_clear(dom_mask);
+    cpufreq_dom_max = 0;
+    cpus_clear(cpufreq_dom_mask);
 
     for_each_online_cpu(i) {
-        cpu_set(processor_pminfo[i].perf.domain_info.domain, dom_mask);
-        if (max_dom < processor_pminfo[i].perf.domain_info.domain)
-            max_dom = processor_pminfo[i].perf.domain_info.domain;
+        cpu_set(processor_pminfo[i].perf.domain_info.domain, cpufreq_dom_mask);
+        if (cpufreq_dom_max < processor_pminfo[i].perf.domain_info.domain)
+            cpufreq_dom_max = processor_pminfo[i].perf.domain_info.domain;
     }
-    max_dom++;
+    cpufreq_dom_max++;
 
-    pt = xmalloc_array(cpumask_t, max_dom);
-    if (!pt)
+    cpufreq_dom_pt = xmalloc_array(cpumask_t, cpufreq_dom_max);
+    if (!cpufreq_dom_pt)
         return -ENOMEM;
-    memset(pt, 0, max_dom * sizeof(cpumask_t));
+    memset(cpufreq_dom_pt, 0, cpufreq_dom_max * sizeof(cpumask_t));
 
-    /* get cpumask of each psd domain */
     for_each_online_cpu(i)
-        cpu_set(i, pt[processor_pminfo[i].perf.domain_info.domain]);
+        cpu_set(i, cpufreq_dom_pt[processor_pminfo[i].perf.domain_info.domain]);
 
     for_each_online_cpu(i)
-        processor_pminfo[i].perf.shared_cpu_map = 
-            pt[processor_pminfo[i].perf.domain_info.domain];
+        processor_pminfo[i].perf.shared_cpu_map =
+            cpufreq_dom_pt[processor_pminfo[i].perf.domain_info.domain];
 
-    cpufreq_driver = &acpi_cpufreq_driver;
+    return 0;
+}
+
+static int cpufreq_cpu_init(void)
+{
+    int i, ret = 0;
 
-    /* setup cpufreq infrastructure */
     for_each_online_cpu(i) {
         xen_px_policy[i].cpu = i;
 
         ret = px_statistic_init(i);
         if (ret)
-            goto out;
+            return ret;
 
         ret = acpi_cpufreq_cpu_init(&xen_px_policy[i]);
         if (ret)
-            goto out;
+            return ret;
     }
+    return ret;
+}
+
+int cpufreq_dom_dbs(unsigned int event)
+{
+    int cpu, dom, ret = 0;
 
-    /* setup ondemand cpufreq */
-    for (dom=0; dom<max_dom; dom++) {
-        if (!cpu_isset(dom, dom_mask))
+    for (dom=0; dom<cpufreq_dom_max; dom++) {
+        if (!cpu_isset(dom, cpufreq_dom_mask))
             continue;
-        i = first_cpu(pt[dom]);
-        ret = cpufreq_governor_dbs(&xen_px_policy[i], CPUFREQ_GOV_START);
+        cpu = first_cpu(cpufreq_dom_pt[dom]);
+        ret = cpufreq_governor_dbs(&xen_px_policy[cpu], event);
         if (ret)
-            goto out;
+            return ret;
     }
+    return ret;
+}
+
+int acpi_cpufreq_init(void)
+{
+    int ret = 0;
+    
+    /* setup cpumask of psd dom and shared cpu map of cpu */
+    ret = cpufreq_dom_init();
+    if (ret)
+        goto err;
+
+    /* setup cpufreq driver */
+    cpufreq_driver = &acpi_cpufreq_driver;
+
+    /* setup cpufreq infrastructure */
+    ret = cpufreq_cpu_init();
+    if (ret)
+        goto err;
+
+    /* setup cpufreq dbs according to dom coordiation */
+    ret = cpufreq_dom_dbs(CPUFREQ_GOV_START);
+    if (ret)
+        goto err;
+
+    return ret;
 
-out:
-    xfree(pt);
-   
+err:
+    cpufreq_dom_exit();
     return ret;
 }
index 408b943650061add13466aae6f886eafd55c4ce4..dbd39b5a65a9233f39a3e349f2aa4d394940a101 100644 (file)
@@ -79,6 +79,12 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
         return;
 
     policy = this_dbs_info->cur_policy;
+
+    if (unlikely(policy->resume)) {
+        __cpufreq_driver_target(policy, policy->max,CPUFREQ_RELATION_H);
+        return;
+    }
+
     cur_ns = NOW();
     total_ns = cur_ns - this_dbs_info->prev_cpu_wall;
     this_dbs_info->prev_cpu_wall = NOW();
@@ -217,8 +223,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event)
         break;
 
     case CPUFREQ_GOV_STOP:
-        if (this_dbs_info->enable)
-            dbs_timer_exit(this_dbs_info);
+        dbs_timer_exit(this_dbs_info);
         dbs_enable--;
 
         break;
@@ -233,5 +238,4 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event)
         break;
     }
     return 0;
-}
-             
+} 
index 89934b320b08aa8c44c40126dc4674eea947ee87..966217828ba10d61c5b9ba9ee515aba0b4e36375 100644 (file)
@@ -37,6 +37,33 @@ struct cpufreq_driver *cpufreq_driver;
  *                    Px STATISTIC INFO                              *
  *********************************************************************/
 
+void px_statistic_suspend(void)
+{
+    int cpu;
+    uint64_t now;
+
+    now = NOW();
+
+    for_each_online_cpu(cpu) {
+        struct pm_px *pxpt = &px_statistic_data[cpu];
+        pxpt->u.pt[pxpt->u.cur].residency +=
+                    now - pxpt->prev_state_wall;
+    }
+}
+
+void px_statistic_resume(void)
+{
+    int cpu;
+    uint64_t now;
+
+    now = NOW();
+
+    for_each_online_cpu(cpu) {
+        struct pm_px *pxpt = &px_statistic_data[cpu];
+        pxpt->prev_state_wall = now;
+    }
+}
+
 void px_statistic_update(cpumask_t cpumask, uint8_t from, uint8_t to)
 {
     uint32_t i;
@@ -242,3 +269,62 @@ int __cpufreq_driver_getavg(struct cpufreq_policy *policy)
 
     return ret;
 }
+
+
+/*********************************************************************
+ *               CPUFREQ SUSPEND/RESUME                              *
+ *********************************************************************/
+
+void cpufreq_suspend(void)
+{
+    int cpu;
+
+    /* to protect the case when Px was controlled by dom0-kernel */
+    /* or when CPU_FREQ not set in which case ACPI Px objects not parsed */
+    for_each_online_cpu(cpu) {
+        struct processor_performance *perf = &processor_pminfo[cpu].perf;
+
+        if (!perf->init)
+            return;
+    }
+
+    cpufreq_dom_dbs(CPUFREQ_GOV_STOP);
+
+    cpufreq_dom_exit();
+
+    px_statistic_suspend();
+}
+
+int cpufreq_resume(void)
+{
+    int cpu, ret = 0;
+
+    /* 1. to protect the case when Px was controlled by dom0-kernel */
+    /* or when CPU_FREQ not set in which case ACPI Px objects not parsed */
+    /* 2. set state and resume flag to sync cpu to right state and freq */
+    for_each_online_cpu(cpu) {
+        struct processor_performance *perf = &processor_pminfo[cpu].perf;
+        struct cpufreq_policy *policy = &xen_px_policy[cpu];
+
+        if (!perf->init)
+            goto err;
+        perf->state = 0;
+        policy->resume = 1;
+    }
+
+    px_statistic_resume();
+
+    ret = cpufreq_dom_init();
+    if (ret)
+        goto err;
+
+    ret = cpufreq_dom_dbs(CPUFREQ_GOV_START);
+    if (ret)
+        goto err;
+
+    return ret;
+
+err:
+    cpufreq_dom_exit();
+    return ret;
+}
index b5f10f5c32ca283ab0a7c526814821e5b0c68d0e..0083ac364a314e90fcf0e6abe7120e547ce0d0ac 100644 (file)
@@ -27,6 +27,8 @@
 #include <public/platform.h>
 #include <asm/tboot.h>
 
+#include <acpi/cpufreq/cpufreq.h>
+
 static char opt_acpi_sleep[20];
 string_param("acpi_sleep", opt_acpi_sleep);
 
@@ -126,6 +128,8 @@ static int enter_state(u32 state)
 
     freeze_domains();
 
+    cpufreq_suspend();
+
     disable_nonboot_cpus();
     if ( num_online_cpus() != 1 )
     {
@@ -181,6 +185,7 @@ static int enter_state(u32 state)
 
  enable_cpu:
     enable_nonboot_cpus();
+    cpufreq_resume();
     thaw_domains();
     spin_unlock(&pm_lock);
     return error;
index 27f2e94446a858ce797f4f94ef6b35be7c7fae04..c82ad5b886941bf158c8019951832932b066d012 100644 (file)
@@ -36,7 +36,10 @@ struct cpufreq_policy {
     unsigned int        max;    /* in kHz */
     unsigned int        cur;    /* in kHz, only needed if cpufreq
                                  * governors are used */
+    unsigned int        resume; /* flag for cpufreq 1st run
+                                 * S3 wakeup, hotplug cpu, etc */
 };
+extern struct cpufreq_policy xen_px_policy[NR_CPUS];
 
 #define CPUFREQ_SHARED_TYPE_NONE (0) /* None */
 #define CPUFREQ_SHARED_TYPE_HW   (1) /* HW does needed coordination */
index c412f160e7f1937507c58ffb21639f3fe3dd8d5f..e27869c1ac6b5fc97d45030387bdcc100a391f02 100644 (file)
@@ -10,6 +10,13 @@ int powernow_cpufreq_init(void);
 void px_statistic_update(cpumask_t, uint8_t, uint8_t);
 int  px_statistic_init(int);
 void px_statistic_reset(int);
+void px_statistic_suspend(void);
+void px_statistic_resume(void);
+void cpufreq_dom_exit(void);
+int  cpufreq_dom_init(void);
+int  cpufreq_dom_dbs(unsigned int);
+void cpufreq_suspend(void);
+int  cpufreq_resume(void);
 
 struct processor_performance {
     uint32_t state;