The usage_count value can be modified from several cpus concurrently if
!CPUFREQ_HAVE_GOVERNOR_PER_POLICY.  This leads to a variety of panics in
which the usage_count is negative or exceeds the number of cpus in the
system.  It must be switched to atomic_t and protected with a mutex to make sure
that future read/writes obtain the correct data.

Cc: "Rafael J. Wysocki" <[email protected]>
Cc: Viresh Kumar <[email protected]>
Cc: [email protected]
Signed-off-by: Prarit Bhargava <[email protected]>
---
 drivers/cpufreq/cpufreq_governor.c |   16 ++++++++++++----
 drivers/cpufreq/cpufreq_governor.h |    3 ++-
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/cpufreq/cpufreq_governor.c 
b/drivers/cpufreq/cpufreq_governor.c
index 1b44496..32ad9db 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -265,7 +265,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                if (have_governor_per_policy()) {
                        WARN_ON(dbs_data);
                } else if (dbs_data) {
-                       dbs_data->usage_count++;
+                       mutex_lock(&dbs_data->usage_count_mutex);
+                       atomic_inc(&dbs_data->usage_count);
+                       mutex_unlock(&dbs_data->usage_count_mutex);
                        policy->governor_data = dbs_data;
                        return 0;
                }
@@ -277,7 +279,10 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                }
 
                dbs_data->cdata = cdata;
-               dbs_data->usage_count = 1;
+               mutex_init(&dbs_data->usage_count_mutex);
+               mutex_lock(&dbs_data->usage_count_mutex);
+               atomic_set(&dbs_data->usage_count, 1);
+               mutex_unlock(&dbs_data->usage_count_mutex);
                rc = cdata->init(dbs_data);
                if (rc) {
                        pr_err("%s: POLICY_INIT: init() failed\n", __func__);
@@ -322,7 +327,8 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 
                return 0;
        case CPUFREQ_GOV_POLICY_EXIT:
-               if (!--dbs_data->usage_count) {
+               mutex_lock(&dbs_data->usage_count_mutex);
+               if (atomic_dec_and_test(&dbs_data->usage_count)) {
                        sysfs_remove_group(get_governor_parent_kobj(policy),
                                        get_sysfs_attr(dbs_data));
 
@@ -338,9 +344,11 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                        }
 
                        cdata->exit(dbs_data);
+                       mutex_unlock(&dbs_data->usage_count_mutex);
                        kfree(dbs_data);
                        cdata->gdbs_data = NULL;
-               }
+               } else
+                       mutex_unlock(&dbs_data->usage_count_mutex);
 
                policy->governor_data = NULL;
                return 0;
diff --git a/drivers/cpufreq/cpufreq_governor.h 
b/drivers/cpufreq/cpufreq_governor.h
index cc401d1..53ca449 100644
--- a/drivers/cpufreq/cpufreq_governor.h
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -219,7 +219,8 @@ struct common_dbs_data {
 struct dbs_data {
        struct common_dbs_data *cdata;
        unsigned int min_sampling_rate;
-       int usage_count;
+       struct mutex usage_count_mutex;
+       atomic_t usage_count;
        void *tuners;
 
        /* dbs_mutex protects dbs_enable in governor start/stop */
-- 
1.7.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to