aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq/cpufreq.c
diff options
context:
space:
mode:
authorSrivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>2013-07-29 18:55:10 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-07 17:02:49 -0400
commit8414809c6a1e8479e331e09254adb58b33a36d25 (patch)
tree50e203a7db4ef1ee6b32407137883df88ce17312 /drivers/cpufreq/cpufreq.c
parenta82fab292898f88ea9ca99dd10c1773dcada08b6 (diff)
cpufreq: Preserve policy structure across suspend/resume
To perform light-weight cpu-init and teardown in the cpufreq subsystem during suspend/resume, we need to separate out the 2 main functionalities of the cpufreq CPU hotplug callbacks, as outlined below: 1. Init/tear-down of core cpufreq and CPU-specific components, which are critical to the correct functioning of the cpufreq subsystem. 2. Init/tear-down of cpufreq sysfs files during suspend/resume. The first part requires accurate updates to the policy structure such as its ->cpus and ->related_cpus masks, whereas the second part requires that the policy->kobj structure is not released or re-initialized during suspend/resume. To handle both these requirements, we need to allow updates to the policy structure throughout suspend/resume, but prevent the structure from getting freed up. Also, we must have a mechanism by which the cpu-up callbacks can restore the policy structure, without allocating things afresh. (That also helps avoid memory leaks). To achieve this, we use 2 schemes: a. Use a fallback per-cpu storage area for preserving the policy structures during suspend, so that they can be restored during resume appropriately. b. Use the 'frozen' flag to determine when to free or allocate the policy structure vs when to restore the policy from the saved fallback storage. Thus we can successfully preserve the structure across suspend/resume. Effectively, this helps us complete the separation of the 'light-weight' and the 'full' init/tear-down sequences in the cpufreq subsystem, so that this can be made use of in the suspend/resume scenario. Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r--drivers/cpufreq/cpufreq.c69
1 files changed, 53 insertions, 16 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 1d041aff4d48..e0ace3d9382c 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -44,6 +44,7 @@
44 */ 44 */
45static struct cpufreq_driver *cpufreq_driver; 45static struct cpufreq_driver *cpufreq_driver;
46static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); 46static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
47static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
47static DEFINE_RWLOCK(cpufreq_driver_lock); 48static DEFINE_RWLOCK(cpufreq_driver_lock);
48static DEFINE_MUTEX(cpufreq_governor_lock); 49static DEFINE_MUTEX(cpufreq_governor_lock);
49 50
@@ -946,6 +947,20 @@ static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,
946} 947}
947#endif 948#endif
948 949
950static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
951{
952 struct cpufreq_policy *policy;
953 unsigned long flags;
954
955 write_lock_irqsave(&cpufreq_driver_lock, flags);
956
957 policy = per_cpu(cpufreq_cpu_data_fallback, cpu);
958
959 write_unlock_irqrestore(&cpufreq_driver_lock, flags);
960
961 return policy;
962}
963
949static struct cpufreq_policy *cpufreq_policy_alloc(void) 964static struct cpufreq_policy *cpufreq_policy_alloc(void)
950{ 965{
951 struct cpufreq_policy *policy; 966 struct cpufreq_policy *policy;
@@ -1023,7 +1038,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
1023 goto module_out; 1038 goto module_out;
1024 } 1039 }
1025 1040
1026 policy = cpufreq_policy_alloc(); 1041 if (frozen)
1042 /* Restore the saved policy when doing light-weight init */
1043 policy = cpufreq_policy_restore(cpu);
1044 else
1045 policy = cpufreq_policy_alloc();
1046
1027 if (!policy) 1047 if (!policy)
1028 goto nomem_out; 1048 goto nomem_out;
1029 1049
@@ -1204,6 +1224,10 @@ static int __cpufreq_remove_dev(struct device *dev,
1204 data = per_cpu(cpufreq_cpu_data, cpu); 1224 data = per_cpu(cpufreq_cpu_data, cpu);
1205 per_cpu(cpufreq_cpu_data, cpu) = NULL; 1225 per_cpu(cpufreq_cpu_data, cpu) = NULL;
1206 1226
1227 /* Save the policy somewhere when doing a light-weight tear-down */
1228 if (frozen)
1229 per_cpu(cpufreq_cpu_data_fallback, cpu) = data;
1230
1207 write_unlock_irqrestore(&cpufreq_driver_lock, flags); 1231 write_unlock_irqrestore(&cpufreq_driver_lock, flags);
1208 1232
1209 if (!data) { 1233 if (!data) {
@@ -1249,27 +1273,40 @@ static int __cpufreq_remove_dev(struct device *dev,
1249 if (cpufreq_driver->target) 1273 if (cpufreq_driver->target)
1250 __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT); 1274 __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
1251 1275
1252 lock_policy_rwsem_read(cpu); 1276 if (!frozen) {
1253 kobj = &data->kobj; 1277 lock_policy_rwsem_read(cpu);
1254 cmp = &data->kobj_unregister; 1278 kobj = &data->kobj;
1255 unlock_policy_rwsem_read(cpu); 1279 cmp = &data->kobj_unregister;
1256 kobject_put(kobj); 1280 unlock_policy_rwsem_read(cpu);
1281 kobject_put(kobj);
1282
1283 /*
1284 * We need to make sure that the underlying kobj is
1285 * actually not referenced anymore by anybody before we
1286 * proceed with unloading.
1287 */
1288 pr_debug("waiting for dropping of refcount\n");
1289 wait_for_completion(cmp);
1290 pr_debug("wait complete\n");
1291 }
1257 1292
1258 /* we need to make sure that the underlying kobj is actually 1293 /*
1259 * not referenced anymore by anybody before we proceed with 1294 * Perform the ->exit() even during light-weight tear-down,
1260 * unloading. 1295 * since this is a core component, and is essential for the
1296 * subsequent light-weight ->init() to succeed.
1261 */ 1297 */
1262 pr_debug("waiting for dropping of refcount\n");
1263 wait_for_completion(cmp);
1264 pr_debug("wait complete\n");
1265
1266 if (cpufreq_driver->exit) 1298 if (cpufreq_driver->exit)
1267 cpufreq_driver->exit(data); 1299 cpufreq_driver->exit(data);
1268 1300
1269 cpufreq_policy_free(data); 1301 if (!frozen)
1302 cpufreq_policy_free(data);
1270 } else { 1303 } else {
1271 pr_debug("%s: removing link, cpu: %d\n", __func__, cpu); 1304
1272 cpufreq_cpu_put(data); 1305 if (!frozen) {
1306 pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
1307 cpufreq_cpu_put(data);
1308 }
1309
1273 if (cpufreq_driver->target) { 1310 if (cpufreq_driver->target) {
1274 __cpufreq_governor(data, CPUFREQ_GOV_START); 1311 __cpufreq_governor(data, CPUFREQ_GOV_START);
1275 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); 1312 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);