|
@@ -17,24 +17,17 @@
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
|
-#include <asm/cputime.h>
|
|
|
-#include <linux/kernel.h>
|
|
|
-#include <linux/kernel_stat.h>
|
|
|
-#include <linux/module.h>
|
|
|
-#include <linux/init.h>
|
|
|
-#include <linux/notifier.h>
|
|
|
+#include <linux/cpu.h>
|
|
|
#include <linux/cpufreq.h>
|
|
|
#include <linux/delay.h>
|
|
|
-#include <linux/interrupt.h>
|
|
|
-#include <linux/spinlock.h>
|
|
|
-#include <linux/tick.h>
|
|
|
#include <linux/device.h>
|
|
|
-#include <linux/slab.h>
|
|
|
-#include <linux/cpu.h>
|
|
|
-#include <linux/completion.h>
|
|
|
+#include <linux/init.h>
|
|
|
+#include <linux/kernel_stat.h>
|
|
|
+#include <linux/module.h>
|
|
|
#include <linux/mutex.h>
|
|
|
+#include <linux/slab.h>
|
|
|
#include <linux/syscore_ops.h>
|
|
|
-
|
|
|
+#include <linux/tick.h>
|
|
|
#include <trace/events/power.h>
|
|
|
|
|
|
/**
|
|
@@ -44,8 +37,10 @@
|
|
|
*/
|
|
|
static struct cpufreq_driver *cpufreq_driver;
|
|
|
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
|
|
|
+static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
|
|
|
static DEFINE_RWLOCK(cpufreq_driver_lock);
|
|
|
static DEFINE_MUTEX(cpufreq_governor_lock);
|
|
|
+static LIST_HEAD(cpufreq_policy_list);
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
/* This one keeps track of the previously set governor of a removed CPU */
|
|
@@ -69,15 +64,14 @@ static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
|
|
|
* - Lock should not be held across
|
|
|
* __cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
*/
|
|
|
-static DEFINE_PER_CPU(int, cpufreq_policy_cpu);
|
|
|
static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);
|
|
|
|
|
|
#define lock_policy_rwsem(mode, cpu) \
|
|
|
static int lock_policy_rwsem_##mode(int cpu) \
|
|
|
{ \
|
|
|
- int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \
|
|
|
- BUG_ON(policy_cpu == -1); \
|
|
|
- down_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \
|
|
|
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); \
|
|
|
+ BUG_ON(!policy); \
|
|
|
+ down_##mode(&per_cpu(cpu_policy_rwsem, policy->cpu)); \
|
|
|
\
|
|
|
return 0; \
|
|
|
}
|
|
@@ -88,14 +82,20 @@ lock_policy_rwsem(write, cpu);
|
|
|
#define unlock_policy_rwsem(mode, cpu) \
|
|
|
static void unlock_policy_rwsem_##mode(int cpu) \
|
|
|
{ \
|
|
|
- int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \
|
|
|
- BUG_ON(policy_cpu == -1); \
|
|
|
- up_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \
|
|
|
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); \
|
|
|
+ BUG_ON(!policy); \
|
|
|
+ up_##mode(&per_cpu(cpu_policy_rwsem, policy->cpu)); \
|
|
|
}
|
|
|
|
|
|
unlock_policy_rwsem(read, cpu);
|
|
|
unlock_policy_rwsem(write, cpu);
|
|
|
|
|
|
+/*
|
|
|
+ * rwsem to guarantee that cpufreq driver module doesn't unload during critical
|
|
|
+ * sections
|
|
|
+ */
|
|
|
+static DECLARE_RWSEM(cpufreq_rwsem);
|
|
|
+
|
|
|
/* internal prototypes */
|
|
|
static int __cpufreq_governor(struct cpufreq_policy *policy,
|
|
|
unsigned int event);
|
|
@@ -183,78 +183,46 @@ u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(get_cpu_idle_time);
|
|
|
|
|
|
-static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)
|
|
|
+struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
|
|
|
{
|
|
|
- struct cpufreq_policy *data;
|
|
|
+ struct cpufreq_policy *policy = NULL;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- if (cpu >= nr_cpu_ids)
|
|
|
- goto err_out;
|
|
|
+ if (cpufreq_disabled() || (cpu >= nr_cpu_ids))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (!down_read_trylock(&cpufreq_rwsem))
|
|
|
+ return NULL;
|
|
|
|
|
|
/* get the cpufreq driver */
|
|
|
read_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- if (!cpufreq_driver)
|
|
|
- goto err_out_unlock;
|
|
|
-
|
|
|
- if (!try_module_get(cpufreq_driver->owner))
|
|
|
- goto err_out_unlock;
|
|
|
-
|
|
|
- /* get the CPU */
|
|
|
- data = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
-
|
|
|
- if (!data)
|
|
|
- goto err_out_put_module;
|
|
|
-
|
|
|
- if (!sysfs && !kobject_get(&data->kobj))
|
|
|
- goto err_out_put_module;
|
|
|
+ if (cpufreq_driver) {
|
|
|
+ /* get the CPU */
|
|
|
+ policy = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
+ if (policy)
|
|
|
+ kobject_get(&policy->kobj);
|
|
|
+ }
|
|
|
|
|
|
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
- return data;
|
|
|
|
|
|
-err_out_put_module:
|
|
|
- module_put(cpufreq_driver->owner);
|
|
|
-err_out_unlock:
|
|
|
- read_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-err_out:
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
|
|
|
-{
|
|
|
- if (cpufreq_disabled())
|
|
|
- return NULL;
|
|
|
+ if (!policy)
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
|
|
|
- return __cpufreq_cpu_get(cpu, false);
|
|
|
+ return policy;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
|
|
|
|
|
|
-static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu)
|
|
|
-{
|
|
|
- return __cpufreq_cpu_get(cpu, true);
|
|
|
-}
|
|
|
-
|
|
|
-static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs)
|
|
|
-{
|
|
|
- if (!sysfs)
|
|
|
- kobject_put(&data->kobj);
|
|
|
- module_put(cpufreq_driver->owner);
|
|
|
-}
|
|
|
-
|
|
|
-void cpufreq_cpu_put(struct cpufreq_policy *data)
|
|
|
+void cpufreq_cpu_put(struct cpufreq_policy *policy)
|
|
|
{
|
|
|
if (cpufreq_disabled())
|
|
|
return;
|
|
|
|
|
|
- __cpufreq_cpu_put(data, false);
|
|
|
+ kobject_put(&policy->kobj);
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
|
|
|
|
|
|
-static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data)
|
|
|
-{
|
|
|
- __cpufreq_cpu_put(data, true);
|
|
|
-}
|
|
|
-
|
|
|
/*********************************************************************
|
|
|
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
|
|
|
*********************************************************************/
|
|
@@ -459,8 +427,8 @@ show_one(scaling_min_freq, min);
|
|
|
show_one(scaling_max_freq, max);
|
|
|
show_one(scaling_cur_freq, cur);
|
|
|
|
|
|
-static int __cpufreq_set_policy(struct cpufreq_policy *data,
|
|
|
- struct cpufreq_policy *policy);
|
|
|
+static int __cpufreq_set_policy(struct cpufreq_policy *policy,
|
|
|
+ struct cpufreq_policy *new_policy);
|
|
|
|
|
|
/**
|
|
|
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
|
|
@@ -699,12 +667,12 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
|
|
|
struct cpufreq_policy *policy = to_policy(kobj);
|
|
|
struct freq_attr *fattr = to_attr(attr);
|
|
|
ssize_t ret = -EINVAL;
|
|
|
- policy = cpufreq_cpu_get_sysfs(policy->cpu);
|
|
|
- if (!policy)
|
|
|
- goto no_policy;
|
|
|
+
|
|
|
+ if (!down_read_trylock(&cpufreq_rwsem))
|
|
|
+ goto exit;
|
|
|
|
|
|
if (lock_policy_rwsem_read(policy->cpu) < 0)
|
|
|
- goto fail;
|
|
|
+ goto up_read;
|
|
|
|
|
|
if (fattr->show)
|
|
|
ret = fattr->show(policy, buf);
|
|
@@ -712,9 +680,10 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
|
|
|
ret = -EIO;
|
|
|
|
|
|
unlock_policy_rwsem_read(policy->cpu);
|
|
|
-fail:
|
|
|
- cpufreq_cpu_put_sysfs(policy);
|
|
|
-no_policy:
|
|
|
+
|
|
|
+up_read:
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+exit:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -724,12 +693,12 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
|
|
|
struct cpufreq_policy *policy = to_policy(kobj);
|
|
|
struct freq_attr *fattr = to_attr(attr);
|
|
|
ssize_t ret = -EINVAL;
|
|
|
- policy = cpufreq_cpu_get_sysfs(policy->cpu);
|
|
|
- if (!policy)
|
|
|
- goto no_policy;
|
|
|
+
|
|
|
+ if (!down_read_trylock(&cpufreq_rwsem))
|
|
|
+ goto exit;
|
|
|
|
|
|
if (lock_policy_rwsem_write(policy->cpu) < 0)
|
|
|
- goto fail;
|
|
|
+ goto up_read;
|
|
|
|
|
|
if (fattr->store)
|
|
|
ret = fattr->store(policy, buf, count);
|
|
@@ -737,9 +706,10 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
|
|
|
ret = -EIO;
|
|
|
|
|
|
unlock_policy_rwsem_write(policy->cpu);
|
|
|
-fail:
|
|
|
- cpufreq_cpu_put_sysfs(policy);
|
|
|
-no_policy:
|
|
|
+
|
|
|
+up_read:
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+exit:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -805,41 +775,32 @@ void cpufreq_sysfs_remove_file(const struct attribute *attr)
|
|
|
EXPORT_SYMBOL(cpufreq_sysfs_remove_file);
|
|
|
|
|
|
/* symlink affected CPUs */
|
|
|
-static int cpufreq_add_dev_symlink(unsigned int cpu,
|
|
|
- struct cpufreq_policy *policy)
|
|
|
+static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy)
|
|
|
{
|
|
|
unsigned int j;
|
|
|
int ret = 0;
|
|
|
|
|
|
for_each_cpu(j, policy->cpus) {
|
|
|
- struct cpufreq_policy *managed_policy;
|
|
|
struct device *cpu_dev;
|
|
|
|
|
|
- if (j == cpu)
|
|
|
+ if (j == policy->cpu)
|
|
|
continue;
|
|
|
|
|
|
- pr_debug("CPU %u already managed, adding link\n", j);
|
|
|
- managed_policy = cpufreq_cpu_get(cpu);
|
|
|
+ pr_debug("Adding link for CPU: %u\n", j);
|
|
|
cpu_dev = get_cpu_device(j);
|
|
|
ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
|
|
|
"cpufreq");
|
|
|
- if (ret) {
|
|
|
- cpufreq_cpu_put(managed_policy);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
}
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int cpufreq_add_dev_interface(unsigned int cpu,
|
|
|
- struct cpufreq_policy *policy,
|
|
|
+static int cpufreq_add_dev_interface(struct cpufreq_policy *policy,
|
|
|
struct device *dev)
|
|
|
{
|
|
|
- struct cpufreq_policy new_policy;
|
|
|
struct freq_attr **drv_attr;
|
|
|
- unsigned long flags;
|
|
|
int ret = 0;
|
|
|
- unsigned int j;
|
|
|
|
|
|
/* prepare interface data */
|
|
|
ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
|
|
@@ -871,18 +832,24 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
|
|
|
goto err_out_kobj_put;
|
|
|
}
|
|
|
|
|
|
- write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
- for_each_cpu(j, policy->cpus) {
|
|
|
- per_cpu(cpufreq_cpu_data, j) = policy;
|
|
|
- per_cpu(cpufreq_policy_cpu, j) = policy->cpu;
|
|
|
- }
|
|
|
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-
|
|
|
- ret = cpufreq_add_dev_symlink(cpu, policy);
|
|
|
+ ret = cpufreq_add_dev_symlink(policy);
|
|
|
if (ret)
|
|
|
goto err_out_kobj_put;
|
|
|
|
|
|
- memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
|
|
|
+ return ret;
|
|
|
+
|
|
|
+err_out_kobj_put:
|
|
|
+ kobject_put(&policy->kobj);
|
|
|
+ wait_for_completion(&policy->kobj_unregister);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void cpufreq_init_policy(struct cpufreq_policy *policy)
|
|
|
+{
|
|
|
+ struct cpufreq_policy new_policy;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ memcpy(&new_policy, policy, sizeof(*policy));
|
|
|
/* assure that the starting sequence is run in __cpufreq_set_policy */
|
|
|
policy->governor = NULL;
|
|
|
|
|
@@ -896,72 +863,106 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
|
|
|
if (cpufreq_driver->exit)
|
|
|
cpufreq_driver->exit(policy);
|
|
|
}
|
|
|
- return ret;
|
|
|
-
|
|
|
-err_out_kobj_put:
|
|
|
- kobject_put(&policy->kobj);
|
|
|
- wait_for_completion(&policy->kobj_unregister);
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
-static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,
|
|
|
- struct device *dev)
|
|
|
+static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
|
|
|
+ unsigned int cpu, struct device *dev,
|
|
|
+ bool frozen)
|
|
|
{
|
|
|
- struct cpufreq_policy *policy;
|
|
|
int ret = 0, has_target = !!cpufreq_driver->target;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- policy = cpufreq_cpu_get(sibling);
|
|
|
- WARN_ON(!policy);
|
|
|
-
|
|
|
- if (has_target)
|
|
|
- __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
|
|
|
+ if (has_target) {
|
|
|
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to stop governor\n", __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- lock_policy_rwsem_write(sibling);
|
|
|
+ lock_policy_rwsem_write(policy->cpu);
|
|
|
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
cpumask_set_cpu(cpu, policy->cpus);
|
|
|
- per_cpu(cpufreq_policy_cpu, cpu) = policy->cpu;
|
|
|
per_cpu(cpufreq_cpu_data, cpu) = policy;
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- unlock_policy_rwsem_write(sibling);
|
|
|
+ unlock_policy_rwsem_write(policy->cpu);
|
|
|
|
|
|
if (has_target) {
|
|
|
- __cpufreq_governor(policy, CPUFREQ_GOV_START);
|
|
|
- __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
|
|
|
+ if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) ||
|
|
|
+ (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) {
|
|
|
+ pr_err("%s: Failed to start governor\n", __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
|
|
|
- if (ret) {
|
|
|
- cpufreq_cpu_put(policy);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ /* Don't touch sysfs links during light-weight init */
|
|
|
+ if (!frozen)
|
|
|
+ ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-/**
|
|
|
- * cpufreq_add_dev - add a CPU device
|
|
|
- *
|
|
|
- * Adds the cpufreq interface for a CPU device.
|
|
|
- *
|
|
|
- * The Oracle says: try running cpufreq registration/unregistration concurrently
|
|
|
- * with with cpu hotplugging and all hell will break loose. Tried to clean this
|
|
|
- * mess up, but more thorough testing is needed. - Mathieu
|
|
|
- */
|
|
|
-static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
+static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
|
|
|
+{
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ policy = per_cpu(cpufreq_cpu_data_fallback, cpu);
|
|
|
+
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ return policy;
|
|
|
+}
|
|
|
+
|
|
|
+static struct cpufreq_policy *cpufreq_policy_alloc(void)
|
|
|
+{
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
+
|
|
|
+ policy = kzalloc(sizeof(*policy), GFP_KERNEL);
|
|
|
+ if (!policy)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
|
|
|
+ goto err_free_policy;
|
|
|
+
|
|
|
+ if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
|
|
|
+ goto err_free_cpumask;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&policy->policy_list);
|
|
|
+ return policy;
|
|
|
+
|
|
|
+err_free_cpumask:
|
|
|
+ free_cpumask_var(policy->cpus);
|
|
|
+err_free_policy:
|
|
|
+ kfree(policy);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void cpufreq_policy_free(struct cpufreq_policy *policy)
|
|
|
+{
|
|
|
+ free_cpumask_var(policy->related_cpus);
|
|
|
+ free_cpumask_var(policy->cpus);
|
|
|
+ kfree(policy);
|
|
|
+}
|
|
|
+
|
|
|
+static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
|
|
|
+ bool frozen)
|
|
|
{
|
|
|
unsigned int j, cpu = dev->id;
|
|
|
int ret = -ENOMEM;
|
|
|
struct cpufreq_policy *policy;
|
|
|
unsigned long flags;
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
+ struct cpufreq_policy *tpolicy;
|
|
|
struct cpufreq_governor *gov;
|
|
|
- int sibling;
|
|
|
#endif
|
|
|
|
|
|
if (cpu_is_offline(cpu))
|
|
@@ -977,43 +978,38 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
cpufreq_cpu_put(policy);
|
|
|
return 0;
|
|
|
}
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (!down_read_trylock(&cpufreq_rwsem))
|
|
|
+ return 0;
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
/* Check if this cpu was hot-unplugged earlier and has siblings */
|
|
|
read_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
- for_each_online_cpu(sibling) {
|
|
|
- struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling);
|
|
|
- if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) {
|
|
|
+ list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) {
|
|
|
+ if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) {
|
|
|
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
- return cpufreq_add_policy_cpu(cpu, sibling, dev);
|
|
|
+ ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev, frozen);
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-#endif
|
|
|
#endif
|
|
|
|
|
|
- if (!try_module_get(cpufreq_driver->owner)) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto module_out;
|
|
|
- }
|
|
|
+ if (frozen)
|
|
|
+ /* Restore the saved policy when doing light-weight init */
|
|
|
+ policy = cpufreq_policy_restore(cpu);
|
|
|
+ else
|
|
|
+ policy = cpufreq_policy_alloc();
|
|
|
|
|
|
- policy = kzalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
|
|
|
if (!policy)
|
|
|
goto nomem_out;
|
|
|
|
|
|
- if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
|
|
|
- goto err_free_policy;
|
|
|
-
|
|
|
- if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
|
|
|
- goto err_free_cpumask;
|
|
|
-
|
|
|
policy->cpu = cpu;
|
|
|
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
|
|
cpumask_copy(policy->cpus, cpumask_of(cpu));
|
|
|
|
|
|
- /* Initially set CPU itself as the policy_cpu */
|
|
|
- per_cpu(cpufreq_policy_cpu, cpu) = cpu;
|
|
|
-
|
|
|
init_completion(&policy->kobj_unregister);
|
|
|
INIT_WORK(&policy->update, handle_update);
|
|
|
|
|
@@ -1050,12 +1046,26 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- ret = cpufreq_add_dev_interface(cpu, policy, dev);
|
|
|
- if (ret)
|
|
|
- goto err_out_unregister;
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ for_each_cpu(j, policy->cpus)
|
|
|
+ per_cpu(cpufreq_cpu_data, j) = policy;
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ if (!frozen) {
|
|
|
+ ret = cpufreq_add_dev_interface(policy, dev);
|
|
|
+ if (ret)
|
|
|
+ goto err_out_unregister;
|
|
|
+ }
|
|
|
+
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ list_add(&policy->policy_list, &cpufreq_policy_list);
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ cpufreq_init_policy(policy);
|
|
|
|
|
|
kobject_uevent(&policy->kobj, KOBJ_ADD);
|
|
|
- module_put(cpufreq_driver->owner);
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+
|
|
|
pr_debug("initialization complete\n");
|
|
|
|
|
|
return 0;
|
|
@@ -1066,32 +1076,33 @@ err_out_unregister:
|
|
|
per_cpu(cpufreq_cpu_data, j) = NULL;
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- kobject_put(&policy->kobj);
|
|
|
- wait_for_completion(&policy->kobj_unregister);
|
|
|
-
|
|
|
err_set_policy_cpu:
|
|
|
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
|
|
|
- free_cpumask_var(policy->related_cpus);
|
|
|
-err_free_cpumask:
|
|
|
- free_cpumask_var(policy->cpus);
|
|
|
-err_free_policy:
|
|
|
- kfree(policy);
|
|
|
+ cpufreq_policy_free(policy);
|
|
|
nomem_out:
|
|
|
- module_put(cpufreq_driver->owner);
|
|
|
-module_out:
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
+/**
|
|
|
+ * cpufreq_add_dev - add a CPU device
|
|
|
+ *
|
|
|
+ * Adds the cpufreq interface for a CPU device.
|
|
|
+ *
|
|
|
+ * The Oracle says: try running cpufreq registration/unregistration concurrently
|
|
|
+ * with with cpu hotplugging and all hell will break loose. Tried to clean this
|
|
|
+ * mess up, but more thorough testing is needed. - Mathieu
|
|
|
+ */
|
|
|
+static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
{
|
|
|
- int j;
|
|
|
+ return __cpufreq_add_dev(dev, sif, false);
|
|
|
+}
|
|
|
|
|
|
+static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
+{
|
|
|
policy->last_cpu = policy->cpu;
|
|
|
policy->cpu = cpu;
|
|
|
|
|
|
- for_each_cpu(j, policy->cpus)
|
|
|
- per_cpu(cpufreq_policy_cpu, j) = cpu;
|
|
|
-
|
|
|
#ifdef CONFIG_CPU_FREQ_TABLE
|
|
|
cpufreq_frequency_table_update_policy_cpu(policy);
|
|
|
#endif
|
|
@@ -1099,6 +1110,37 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
CPUFREQ_UPDATE_POLICY_CPU, policy);
|
|
|
}
|
|
|
|
|
|
+static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
|
|
|
+ unsigned int old_cpu, bool frozen)
|
|
|
+{
|
|
|
+ struct device *cpu_dev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* first sibling now owns the new sysfs dir */
|
|
|
+ cpu_dev = get_cpu_device(cpumask_first(policy->cpus));
|
|
|
+
|
|
|
+ /* Don't touch sysfs files during light-weight tear-down */
|
|
|
+ if (frozen)
|
|
|
+ return cpu_dev->id;
|
|
|
+
|
|
|
+ sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
|
|
|
+ ret = kobject_move(&policy->kobj, &cpu_dev->kobj);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to move kobj: %d", __func__, ret);
|
|
|
+
|
|
|
+ WARN_ON(lock_policy_rwsem_write(old_cpu));
|
|
|
+ cpumask_set_cpu(old_cpu, policy->cpus);
|
|
|
+ unlock_policy_rwsem_write(old_cpu);
|
|
|
+
|
|
|
+ ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
|
|
|
+ "cpufreq");
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return cpu_dev->id;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __cpufreq_remove_dev - remove a CPU device
|
|
|
*
|
|
@@ -1107,111 +1149,126 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
* This routine frees the rwsem before returning.
|
|
|
*/
|
|
|
static int __cpufreq_remove_dev(struct device *dev,
|
|
|
- struct subsys_interface *sif)
|
|
|
+ struct subsys_interface *sif, bool frozen)
|
|
|
{
|
|
|
- unsigned int cpu = dev->id, ret, cpus;
|
|
|
+ unsigned int cpu = dev->id, cpus;
|
|
|
+ int new_cpu, ret;
|
|
|
unsigned long flags;
|
|
|
- struct cpufreq_policy *data;
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
struct kobject *kobj;
|
|
|
struct completion *cmp;
|
|
|
- struct device *cpu_dev;
|
|
|
|
|
|
pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
|
|
|
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- data = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
- per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
|
|
+ policy = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
+
|
|
|
+ /* Save the policy somewhere when doing a light-weight tear-down */
|
|
|
+ if (frozen)
|
|
|
+ per_cpu(cpufreq_cpu_data_fallback, cpu) = policy;
|
|
|
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- if (!data) {
|
|
|
+ if (!policy) {
|
|
|
pr_debug("%s: No cpu_data found\n", __func__);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (cpufreq_driver->target)
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
+ if (cpufreq_driver->target) {
|
|
|
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to stop governor\n", __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
if (!cpufreq_driver->setpolicy)
|
|
|
strncpy(per_cpu(cpufreq_cpu_governor, cpu),
|
|
|
- data->governor->name, CPUFREQ_NAME_LEN);
|
|
|
+ policy->governor->name, CPUFREQ_NAME_LEN);
|
|
|
#endif
|
|
|
|
|
|
WARN_ON(lock_policy_rwsem_write(cpu));
|
|
|
- cpus = cpumask_weight(data->cpus);
|
|
|
+ cpus = cpumask_weight(policy->cpus);
|
|
|
|
|
|
if (cpus > 1)
|
|
|
- cpumask_clear_cpu(cpu, data->cpus);
|
|
|
+ cpumask_clear_cpu(cpu, policy->cpus);
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
|
|
|
- if (cpu != data->cpu) {
|
|
|
+ if (cpu != policy->cpu && !frozen) {
|
|
|
sysfs_remove_link(&dev->kobj, "cpufreq");
|
|
|
} else if (cpus > 1) {
|
|
|
- /* first sibling now owns the new sysfs dir */
|
|
|
- cpu_dev = get_cpu_device(cpumask_first(data->cpus));
|
|
|
- sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
|
|
|
- ret = kobject_move(&data->kobj, &cpu_dev->kobj);
|
|
|
- if (ret) {
|
|
|
- pr_err("%s: Failed to move kobj: %d", __func__, ret);
|
|
|
|
|
|
+ new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen);
|
|
|
+ if (new_cpu >= 0) {
|
|
|
WARN_ON(lock_policy_rwsem_write(cpu));
|
|
|
- cpumask_set_cpu(cpu, data->cpus);
|
|
|
-
|
|
|
- write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
- per_cpu(cpufreq_cpu_data, cpu) = data;
|
|
|
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-
|
|
|
+ update_policy_cpu(policy, new_cpu);
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
|
|
|
- ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj,
|
|
|
- "cpufreq");
|
|
|
- return -EINVAL;
|
|
|
+ if (!frozen) {
|
|
|
+ pr_debug("%s: policy Kobject moved to cpu: %d "
|
|
|
+ "from: %d\n",__func__, new_cpu, cpu);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- WARN_ON(lock_policy_rwsem_write(cpu));
|
|
|
- update_policy_cpu(data, cpu_dev->id);
|
|
|
- unlock_policy_rwsem_write(cpu);
|
|
|
- pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
|
|
|
- __func__, cpu_dev->id, cpu);
|
|
|
}
|
|
|
|
|
|
/* If cpu is last user of policy, free policy */
|
|
|
if (cpus == 1) {
|
|
|
- if (cpufreq_driver->target)
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
|
|
|
-
|
|
|
- lock_policy_rwsem_read(cpu);
|
|
|
- kobj = &data->kobj;
|
|
|
- cmp = &data->kobj_unregister;
|
|
|
- unlock_policy_rwsem_read(cpu);
|
|
|
- kobject_put(kobj);
|
|
|
-
|
|
|
- /* we need to make sure that the underlying kobj is actually
|
|
|
- * not referenced anymore by anybody before we proceed with
|
|
|
- * unloading.
|
|
|
- */
|
|
|
- pr_debug("waiting for dropping of refcount\n");
|
|
|
- wait_for_completion(cmp);
|
|
|
- pr_debug("wait complete\n");
|
|
|
+ if (cpufreq_driver->target) {
|
|
|
+ ret = __cpufreq_governor(policy,
|
|
|
+ CPUFREQ_GOV_POLICY_EXIT);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to exit governor\n",
|
|
|
+ __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!frozen) {
|
|
|
+ lock_policy_rwsem_read(cpu);
|
|
|
+ kobj = &policy->kobj;
|
|
|
+ cmp = &policy->kobj_unregister;
|
|
|
+ unlock_policy_rwsem_read(cpu);
|
|
|
+ kobject_put(kobj);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We need to make sure that the underlying kobj is
|
|
|
+ * actually not referenced anymore by anybody before we
|
|
|
+ * proceed with unloading.
|
|
|
+ */
|
|
|
+ pr_debug("waiting for dropping of refcount\n");
|
|
|
+ wait_for_completion(cmp);
|
|
|
+ pr_debug("wait complete\n");
|
|
|
+ }
|
|
|
|
|
|
+ /*
|
|
|
+ * Perform the ->exit() even during light-weight tear-down,
|
|
|
+ * since this is a core component, and is essential for the
|
|
|
+ * subsequent light-weight ->init() to succeed.
|
|
|
+ */
|
|
|
if (cpufreq_driver->exit)
|
|
|
- cpufreq_driver->exit(data);
|
|
|
+ cpufreq_driver->exit(policy);
|
|
|
|
|
|
- free_cpumask_var(data->related_cpus);
|
|
|
- free_cpumask_var(data->cpus);
|
|
|
- kfree(data);
|
|
|
+ /* Remove policy from list of active policies */
|
|
|
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+ list_del(&policy->policy_list);
|
|
|
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
+ if (!frozen)
|
|
|
+ cpufreq_policy_free(policy);
|
|
|
} else {
|
|
|
- pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
|
|
|
- cpufreq_cpu_put(data);
|
|
|
if (cpufreq_driver->target) {
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_START);
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
|
|
|
+ if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) ||
|
|
|
+ (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) {
|
|
|
+ pr_err("%s: Failed to start governor\n",
|
|
|
+ __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
|
|
|
+ per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1223,7 +1280,7 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
if (cpu_is_offline(cpu))
|
|
|
return 0;
|
|
|
|
|
|
- retval = __cpufreq_remove_dev(dev, sif);
|
|
|
+ retval = __cpufreq_remove_dev(dev, sif, false);
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
@@ -1344,10 +1401,9 @@ static unsigned int __cpufreq_get(unsigned int cpu)
|
|
|
unsigned int cpufreq_get(unsigned int cpu)
|
|
|
{
|
|
|
unsigned int ret_freq = 0;
|
|
|
- struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
|
|
|
|
|
|
- if (!policy)
|
|
|
- goto out;
|
|
|
+ if (!down_read_trylock(&cpufreq_rwsem))
|
|
|
+ return 0;
|
|
|
|
|
|
if (unlikely(lock_policy_rwsem_read(cpu)))
|
|
|
goto out_policy;
|
|
@@ -1357,8 +1413,8 @@ unsigned int cpufreq_get(unsigned int cpu)
|
|
|
unlock_policy_rwsem_read(cpu);
|
|
|
|
|
|
out_policy:
|
|
|
- cpufreq_cpu_put(policy);
|
|
|
-out:
|
|
|
+ up_read(&cpufreq_rwsem);
|
|
|
+
|
|
|
return ret_freq;
|
|
|
}
|
|
|
EXPORT_SYMBOL(cpufreq_get);
|
|
@@ -1381,23 +1437,23 @@ static int cpufreq_bp_suspend(void)
|
|
|
int ret = 0;
|
|
|
|
|
|
int cpu = smp_processor_id();
|
|
|
- struct cpufreq_policy *cpu_policy;
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
|
|
|
pr_debug("suspending cpu %u\n", cpu);
|
|
|
|
|
|
/* If there's no policy for the boot CPU, we have nothing to do. */
|
|
|
- cpu_policy = cpufreq_cpu_get(cpu);
|
|
|
- if (!cpu_policy)
|
|
|
+ policy = cpufreq_cpu_get(cpu);
|
|
|
+ if (!policy)
|
|
|
return 0;
|
|
|
|
|
|
if (cpufreq_driver->suspend) {
|
|
|
- ret = cpufreq_driver->suspend(cpu_policy);
|
|
|
+ ret = cpufreq_driver->suspend(policy);
|
|
|
if (ret)
|
|
|
printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
|
|
|
- "step on CPU %u\n", cpu_policy->cpu);
|
|
|
+ "step on CPU %u\n", policy->cpu);
|
|
|
}
|
|
|
|
|
|
- cpufreq_cpu_put(cpu_policy);
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1419,28 +1475,28 @@ static void cpufreq_bp_resume(void)
|
|
|
int ret = 0;
|
|
|
|
|
|
int cpu = smp_processor_id();
|
|
|
- struct cpufreq_policy *cpu_policy;
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
|
|
|
pr_debug("resuming cpu %u\n", cpu);
|
|
|
|
|
|
/* If there's no policy for the boot CPU, we have nothing to do. */
|
|
|
- cpu_policy = cpufreq_cpu_get(cpu);
|
|
|
- if (!cpu_policy)
|
|
|
+ policy = cpufreq_cpu_get(cpu);
|
|
|
+ if (!policy)
|
|
|
return;
|
|
|
|
|
|
if (cpufreq_driver->resume) {
|
|
|
- ret = cpufreq_driver->resume(cpu_policy);
|
|
|
+ ret = cpufreq_driver->resume(policy);
|
|
|
if (ret) {
|
|
|
printk(KERN_ERR "cpufreq: resume failed in ->resume "
|
|
|
- "step on CPU %u\n", cpu_policy->cpu);
|
|
|
+ "step on CPU %u\n", policy->cpu);
|
|
|
goto fail;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- schedule_work(&cpu_policy->update);
|
|
|
+ schedule_work(&policy->update);
|
|
|
|
|
|
fail:
|
|
|
- cpufreq_cpu_put(cpu_policy);
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
}
|
|
|
|
|
|
static struct syscore_ops cpufreq_syscore_ops = {
|
|
@@ -1594,18 +1650,6 @@ fail:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(cpufreq_driver_target);
|
|
|
|
|
|
-int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
-{
|
|
|
- if (cpufreq_disabled())
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (!cpufreq_driver->getavg)
|
|
|
- return 0;
|
|
|
-
|
|
|
- return cpufreq_driver->getavg(policy, cpu);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(__cpufreq_driver_getavg);
|
|
|
-
|
|
|
/*
|
|
|
* when "event" is CPUFREQ_GOV_LIMITS
|
|
|
*/
|
|
@@ -1640,8 +1684,9 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (!try_module_get(policy->governor->owner))
|
|
|
- return -EINVAL;
|
|
|
+ if (event == CPUFREQ_GOV_POLICY_INIT)
|
|
|
+ if (!try_module_get(policy->governor->owner))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
pr_debug("__cpufreq_governor for CPU %u, event %u\n",
|
|
|
policy->cpu, event);
|
|
@@ -1677,11 +1722,8 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
|
|
|
mutex_unlock(&cpufreq_governor_lock);
|
|
|
}
|
|
|
|
|
|
- /* we keep one module reference alive for
|
|
|
- each CPU governed by this CPU */
|
|
|
- if ((event != CPUFREQ_GOV_START) || ret)
|
|
|
- module_put(policy->governor->owner);
|
|
|
- if ((event == CPUFREQ_GOV_STOP) && !ret)
|
|
|
+ if (((event == CPUFREQ_GOV_POLICY_INIT) && ret) ||
|
|
|
+ ((event == CPUFREQ_GOV_POLICY_EXIT) && !ret))
|
|
|
module_put(policy->governor->owner);
|
|
|
|
|
|
return ret;
|
|
@@ -1761,7 +1803,7 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
if (!cpu_policy)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy));
|
|
|
+ memcpy(policy, cpu_policy, sizeof(*policy));
|
|
|
|
|
|
cpufreq_cpu_put(cpu_policy);
|
|
|
return 0;
|
|
@@ -1772,95 +1814,94 @@ EXPORT_SYMBOL(cpufreq_get_policy);
|
|
|
* data : current policy.
|
|
|
* policy : policy to be set.
|
|
|
*/
|
|
|
-static int __cpufreq_set_policy(struct cpufreq_policy *data,
|
|
|
- struct cpufreq_policy *policy)
|
|
|
+static int __cpufreq_set_policy(struct cpufreq_policy *policy,
|
|
|
+ struct cpufreq_policy *new_policy)
|
|
|
{
|
|
|
int ret = 0, failed = 1;
|
|
|
|
|
|
- pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
|
|
|
- policy->min, policy->max);
|
|
|
+ pr_debug("setting new policy for CPU %u: %u - %u kHz\n", new_policy->cpu,
|
|
|
+ new_policy->min, new_policy->max);
|
|
|
|
|
|
- memcpy(&policy->cpuinfo, &data->cpuinfo,
|
|
|
- sizeof(struct cpufreq_cpuinfo));
|
|
|
+ memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
|
|
|
|
|
|
- if (policy->min > data->max || policy->max < data->min) {
|
|
|
+ if (new_policy->min > policy->max || new_policy->max < policy->min) {
|
|
|
ret = -EINVAL;
|
|
|
goto error_out;
|
|
|
}
|
|
|
|
|
|
/* verify the cpu speed can be set within this limit */
|
|
|
- ret = cpufreq_driver->verify(policy);
|
|
|
+ ret = cpufreq_driver->verify(new_policy);
|
|
|
if (ret)
|
|
|
goto error_out;
|
|
|
|
|
|
/* adjust if necessary - all reasons */
|
|
|
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
- CPUFREQ_ADJUST, policy);
|
|
|
+ CPUFREQ_ADJUST, new_policy);
|
|
|
|
|
|
/* adjust if necessary - hardware incompatibility*/
|
|
|
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
- CPUFREQ_INCOMPATIBLE, policy);
|
|
|
+ CPUFREQ_INCOMPATIBLE, new_policy);
|
|
|
|
|
|
/*
|
|
|
* verify the cpu speed can be set within this limit, which might be
|
|
|
* different to the first one
|
|
|
*/
|
|
|
- ret = cpufreq_driver->verify(policy);
|
|
|
+ ret = cpufreq_driver->verify(new_policy);
|
|
|
if (ret)
|
|
|
goto error_out;
|
|
|
|
|
|
/* notification of the new policy */
|
|
|
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
- CPUFREQ_NOTIFY, policy);
|
|
|
+ CPUFREQ_NOTIFY, new_policy);
|
|
|
|
|
|
- data->min = policy->min;
|
|
|
- data->max = policy->max;
|
|
|
+ policy->min = new_policy->min;
|
|
|
+ policy->max = new_policy->max;
|
|
|
|
|
|
pr_debug("new min and max freqs are %u - %u kHz\n",
|
|
|
- data->min, data->max);
|
|
|
+ policy->min, policy->max);
|
|
|
|
|
|
if (cpufreq_driver->setpolicy) {
|
|
|
- data->policy = policy->policy;
|
|
|
+ policy->policy = new_policy->policy;
|
|
|
pr_debug("setting range\n");
|
|
|
- ret = cpufreq_driver->setpolicy(policy);
|
|
|
+ ret = cpufreq_driver->setpolicy(new_policy);
|
|
|
} else {
|
|
|
- if (policy->governor != data->governor) {
|
|
|
+ if (new_policy->governor != policy->governor) {
|
|
|
/* save old, working values */
|
|
|
- struct cpufreq_governor *old_gov = data->governor;
|
|
|
+ struct cpufreq_governor *old_gov = policy->governor;
|
|
|
|
|
|
pr_debug("governor switch\n");
|
|
|
|
|
|
/* end old governor */
|
|
|
- if (data->governor) {
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
- unlock_policy_rwsem_write(policy->cpu);
|
|
|
- __cpufreq_governor(data,
|
|
|
+ if (policy->governor) {
|
|
|
+ __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
|
|
|
+ unlock_policy_rwsem_write(new_policy->cpu);
|
|
|
+ __cpufreq_governor(policy,
|
|
|
CPUFREQ_GOV_POLICY_EXIT);
|
|
|
- lock_policy_rwsem_write(policy->cpu);
|
|
|
+ lock_policy_rwsem_write(new_policy->cpu);
|
|
|
}
|
|
|
|
|
|
/* start new governor */
|
|
|
- data->governor = policy->governor;
|
|
|
- if (!__cpufreq_governor(data, CPUFREQ_GOV_POLICY_INIT)) {
|
|
|
- if (!__cpufreq_governor(data, CPUFREQ_GOV_START)) {
|
|
|
+ policy->governor = new_policy->governor;
|
|
|
+ if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) {
|
|
|
+ if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) {
|
|
|
failed = 0;
|
|
|
} else {
|
|
|
- unlock_policy_rwsem_write(policy->cpu);
|
|
|
- __cpufreq_governor(data,
|
|
|
+ unlock_policy_rwsem_write(new_policy->cpu);
|
|
|
+ __cpufreq_governor(policy,
|
|
|
CPUFREQ_GOV_POLICY_EXIT);
|
|
|
- lock_policy_rwsem_write(policy->cpu);
|
|
|
+ lock_policy_rwsem_write(new_policy->cpu);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (failed) {
|
|
|
/* new governor failed, so re-start old one */
|
|
|
pr_debug("starting governor %s failed\n",
|
|
|
- data->governor->name);
|
|
|
+ policy->governor->name);
|
|
|
if (old_gov) {
|
|
|
- data->governor = old_gov;
|
|
|
- __cpufreq_governor(data,
|
|
|
+ policy->governor = old_gov;
|
|
|
+ __cpufreq_governor(policy,
|
|
|
CPUFREQ_GOV_POLICY_INIT);
|
|
|
- __cpufreq_governor(data,
|
|
|
+ __cpufreq_governor(policy,
|
|
|
CPUFREQ_GOV_START);
|
|
|
}
|
|
|
ret = -EINVAL;
|
|
@@ -1869,7 +1910,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
|
|
|
/* might be a policy change, too, so fall through */
|
|
|
}
|
|
|
pr_debug("governor: change or update limits\n");
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
|
|
|
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
|
|
|
}
|
|
|
|
|
|
error_out:
|
|
@@ -1885,11 +1926,11 @@ error_out:
|
|
|
*/
|
|
|
int cpufreq_update_policy(unsigned int cpu)
|
|
|
{
|
|
|
- struct cpufreq_policy *data = cpufreq_cpu_get(cpu);
|
|
|
- struct cpufreq_policy policy;
|
|
|
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
|
|
|
+ struct cpufreq_policy new_policy;
|
|
|
int ret;
|
|
|
|
|
|
- if (!data) {
|
|
|
+ if (!policy) {
|
|
|
ret = -ENODEV;
|
|
|
goto no_policy;
|
|
|
}
|
|
@@ -1900,34 +1941,34 @@ int cpufreq_update_policy(unsigned int cpu)
|
|
|
}
|
|
|
|
|
|
pr_debug("updating policy for CPU %u\n", cpu);
|
|
|
- memcpy(&policy, data, sizeof(struct cpufreq_policy));
|
|
|
- policy.min = data->user_policy.min;
|
|
|
- policy.max = data->user_policy.max;
|
|
|
- policy.policy = data->user_policy.policy;
|
|
|
- policy.governor = data->user_policy.governor;
|
|
|
+ memcpy(&new_policy, policy, sizeof(*policy));
|
|
|
+ new_policy.min = policy->user_policy.min;
|
|
|
+ new_policy.max = policy->user_policy.max;
|
|
|
+ new_policy.policy = policy->user_policy.policy;
|
|
|
+ new_policy.governor = policy->user_policy.governor;
|
|
|
|
|
|
/*
|
|
|
* BIOS might change freq behind our back
|
|
|
* -> ask driver for current freq and notify governors about a change
|
|
|
*/
|
|
|
if (cpufreq_driver->get) {
|
|
|
- policy.cur = cpufreq_driver->get(cpu);
|
|
|
- if (!data->cur) {
|
|
|
+ new_policy.cur = cpufreq_driver->get(cpu);
|
|
|
+ if (!policy->cur) {
|
|
|
pr_debug("Driver did not initialize current freq");
|
|
|
- data->cur = policy.cur;
|
|
|
+ policy->cur = new_policy.cur;
|
|
|
} else {
|
|
|
- if (data->cur != policy.cur && cpufreq_driver->target)
|
|
|
- cpufreq_out_of_sync(cpu, data->cur,
|
|
|
- policy.cur);
|
|
|
+ if (policy->cur != new_policy.cur && cpufreq_driver->target)
|
|
|
+ cpufreq_out_of_sync(cpu, policy->cur,
|
|
|
+ new_policy.cur);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- ret = __cpufreq_set_policy(data, &policy);
|
|
|
+ ret = __cpufreq_set_policy(policy, &new_policy);
|
|
|
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
|
|
|
fail:
|
|
|
- cpufreq_cpu_put(data);
|
|
|
+ cpufreq_cpu_put(policy);
|
|
|
no_policy:
|
|
|
return ret;
|
|
|
}
|
|
@@ -1938,21 +1979,26 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
|
|
|
{
|
|
|
unsigned int cpu = (unsigned long)hcpu;
|
|
|
struct device *dev;
|
|
|
+ bool frozen = false;
|
|
|
|
|
|
dev = get_cpu_device(cpu);
|
|
|
if (dev) {
|
|
|
- switch (action) {
|
|
|
+
|
|
|
+ if (action & CPU_TASKS_FROZEN)
|
|
|
+ frozen = true;
|
|
|
+
|
|
|
+ switch (action & ~CPU_TASKS_FROZEN) {
|
|
|
case CPU_ONLINE:
|
|
|
- case CPU_ONLINE_FROZEN:
|
|
|
- cpufreq_add_dev(dev, NULL);
|
|
|
+ __cpufreq_add_dev(dev, NULL, frozen);
|
|
|
+ cpufreq_update_policy(cpu);
|
|
|
break;
|
|
|
+
|
|
|
case CPU_DOWN_PREPARE:
|
|
|
- case CPU_DOWN_PREPARE_FROZEN:
|
|
|
- __cpufreq_remove_dev(dev, NULL);
|
|
|
+ __cpufreq_remove_dev(dev, NULL, frozen);
|
|
|
break;
|
|
|
+
|
|
|
case CPU_DOWN_FAILED:
|
|
|
- case CPU_DOWN_FAILED_FROZEN:
|
|
|
- cpufreq_add_dev(dev, NULL);
|
|
|
+ __cpufreq_add_dev(dev, NULL, frozen);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -2059,9 +2105,13 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
|
|
|
subsys_interface_unregister(&cpufreq_interface);
|
|
|
unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
|
|
|
|
|
|
+ down_write(&cpufreq_rwsem);
|
|
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
+
|
|
|
cpufreq_driver = NULL;
|
|
|
+
|
|
|
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+ up_write(&cpufreq_rwsem);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -2074,10 +2124,8 @@ static int __init cpufreq_core_init(void)
|
|
|
if (cpufreq_disabled())
|
|
|
return -ENODEV;
|
|
|
|
|
|
- for_each_possible_cpu(cpu) {
|
|
|
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
|
|
|
+ for_each_possible_cpu(cpu)
|
|
|
init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
|
|
|
- }
|
|
|
|
|
|
cpufreq_global_kobject = kobject_create();
|
|
|
BUG_ON(!cpufreq_global_kobject);
|