Commit 5a87182a authored by Viresh Kumar's avatar Viresh Kumar Committed by Rafael J. Wysocki

cpufreq: suspend governors on system suspend/hibernate

This patch adds cpufreq suspend/resume calls to dpm_{suspend|resume}_noirq()
for handling suspend/resume of cpufreq governors.

Lan Tianyu (Intel) & Jinhyuk Choi (Broadcom) found anr issue where
tunables configuration for clusters/sockets with non-boot CPUs was
getting lost after suspend/resume, as we were notifying governors
with CPUFREQ_GOV_POLICY_EXIT on removal of the last cpu for that
policy and so deallocating memory for tunables. This is fixed by
this patch as we don't allow any operation on governors after
device suspend and before device resume now.
Reported-and-tested-by: default avatarLan Tianyu <tianyu.lan@intel.com>
Reported-by: default avatarJinhyuk Choi <jinchoi@broadcom.com>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
[rjw: Changelog, minor cleanups]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 3d3c6f54
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/async.h> #include <linux/async.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <linux/cpufreq.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include "../base.h" #include "../base.h"
#include "power.h" #include "power.h"
...@@ -473,6 +474,7 @@ static void dpm_resume_noirq(pm_message_t state) ...@@ -473,6 +474,7 @@ static void dpm_resume_noirq(pm_message_t state)
dpm_show_time(starttime, state, "noirq"); dpm_show_time(starttime, state, "noirq");
resume_device_irqs(); resume_device_irqs();
cpuidle_resume(); cpuidle_resume();
cpufreq_resume();
} }
/** /**
...@@ -885,6 +887,7 @@ static int dpm_suspend_noirq(pm_message_t state) ...@@ -885,6 +887,7 @@ static int dpm_suspend_noirq(pm_message_t state)
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
int error = 0; int error = 0;
cpufreq_suspend();
cpuidle_pause(); cpuidle_pause();
suspend_device_irqs(); suspend_device_irqs();
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <trace/events/power.h> #include <trace/events/power.h>
...@@ -47,6 +48,9 @@ static LIST_HEAD(cpufreq_policy_list); ...@@ -47,6 +48,9 @@ static LIST_HEAD(cpufreq_policy_list);
static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
#endif #endif
/* Flag to suspend/resume CPUFreq governors */
static bool cpufreq_suspended;
static inline bool has_target(void) static inline bool has_target(void)
{ {
return cpufreq_driver->target_index || cpufreq_driver->target; return cpufreq_driver->target_index || cpufreq_driver->target;
...@@ -1462,6 +1466,41 @@ static struct subsys_interface cpufreq_interface = { ...@@ -1462,6 +1466,41 @@ static struct subsys_interface cpufreq_interface = {
.remove_dev = cpufreq_remove_dev, .remove_dev = cpufreq_remove_dev,
}; };
void cpufreq_suspend(void)
{
struct cpufreq_policy *policy;
if (!has_target())
return;
pr_debug("%s: Suspending Governors\n", __func__);
list_for_each_entry(policy, &cpufreq_policy_list, policy_list)
if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP))
pr_err("%s: Failed to stop governor for policy: %p\n",
__func__, policy);
cpufreq_suspended = true;
}
void cpufreq_resume(void)
{
struct cpufreq_policy *policy;
if (!has_target())
return;
pr_debug("%s: Resuming Governors\n", __func__);
cpufreq_suspended = false;
list_for_each_entry(policy, &cpufreq_policy_list, policy_list)
if (__cpufreq_governor(policy, CPUFREQ_GOV_START)
|| __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))
pr_err("%s: Failed to start governor for policy: %p\n",
__func__, policy);
}
/** /**
* cpufreq_bp_suspend - Prepare the boot CPU for system suspend. * cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
* *
...@@ -1764,6 +1803,10 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, ...@@ -1764,6 +1803,10 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
struct cpufreq_governor *gov = NULL; struct cpufreq_governor *gov = NULL;
#endif #endif
/* Don't start any governor operations if we are entering suspend */
if (cpufreq_suspended)
return 0;
if (policy->governor->max_transition_latency && if (policy->governor->max_transition_latency &&
policy->cpuinfo.transition_latency > policy->cpuinfo.transition_latency >
policy->governor->max_transition_latency) { policy->governor->max_transition_latency) {
......
...@@ -272,6 +272,14 @@ cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy) ...@@ -272,6 +272,14 @@ cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
policy->cpuinfo.max_freq); policy->cpuinfo.max_freq);
} }
#ifdef CONFIG_CPU_FREQ
void cpufreq_suspend(void);
void cpufreq_resume(void);
#else
static inline void cpufreq_suspend(void) {}
static inline void cpufreq_resume(void) {}
#endif
/********************************************************************* /*********************************************************************
* CPUFREQ NOTIFIER INTERFACE * * CPUFREQ NOTIFIER INTERFACE *
*********************************************************************/ *********************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment