[PATCH 5.4 240/270] cpufreq: Fix locking issues with governors

From: Greg Kroah-Hartman
Date: Mon Aug 17 2020 - 13:06:28 EST


From: Viresh Kumar <viresh.kumar@xxxxxxxxxx>

commit 8cc46ae565c393f77417cb9530b1265eb50f5d2e upstream.

The locking around governors handling isn't adequate currently.

The list of governors should never be traversed without the locking
in place. Also governor modules must not be removed while the code
in them is still in use.

Reported-by: Quentin Perret <qperret@xxxxxxxxxx>
Signed-off-by: Viresh Kumar <viresh.kumar@xxxxxxxxxx>
Cc: All applicable <stable@xxxxxxxxxxxxxxx>
[ rjw: Changelog ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
drivers/cpufreq/cpufreq.c | 58 +++++++++++++++++++++++++++-------------------
1 file changed, 35 insertions(+), 23 deletions(-)

--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -616,6 +616,24 @@ static struct cpufreq_governor *find_gov
return NULL;
}

+static struct cpufreq_governor *get_governor(const char *str_governor)
+{
+ struct cpufreq_governor *t;
+
+ mutex_lock(&cpufreq_governor_mutex);
+ t = find_governor(str_governor);
+ if (!t)
+ goto unlock;
+
+ if (!try_module_get(t->owner))
+ t = NULL;
+
+unlock:
+ mutex_unlock(&cpufreq_governor_mutex);
+
+ return t;
+}
+
static unsigned int cpufreq_parse_policy(char *str_governor)
{
if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN))
@@ -635,28 +653,14 @@ static struct cpufreq_governor *cpufreq_
{
struct cpufreq_governor *t;

- mutex_lock(&cpufreq_governor_mutex);
-
- t = find_governor(str_governor);
- if (!t) {
- int ret;
+ t = get_governor(str_governor);
+ if (t)
+ return t;

- mutex_unlock(&cpufreq_governor_mutex);
+ if (request_module("cpufreq_%s", str_governor))
+ return NULL;

- ret = request_module("cpufreq_%s", str_governor);
- if (ret)
- return NULL;
-
- mutex_lock(&cpufreq_governor_mutex);
-
- t = find_governor(str_governor);
- }
- if (t && !try_module_get(t->owner))
- t = NULL;
-
- mutex_unlock(&cpufreq_governor_mutex);
-
- return t;
+ return get_governor(str_governor);
}

/**
@@ -810,12 +814,14 @@ static ssize_t show_scaling_available_go
goto out;
}

+ mutex_lock(&cpufreq_governor_mutex);
for_each_governor(t) {
if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char))
- (CPUFREQ_NAME_LEN + 2)))
- goto out;
+ break;
i += scnprintf(&buf[i], CPUFREQ_NAME_PLEN, "%s ", t->name);
}
+ mutex_unlock(&cpufreq_governor_mutex);
out:
i += sprintf(&buf[i], "\n");
return i;
@@ -1053,15 +1059,17 @@ static int cpufreq_init_policy(struct cp
struct cpufreq_governor *def_gov = cpufreq_default_governor();
struct cpufreq_governor *gov = NULL;
unsigned int pol = CPUFREQ_POLICY_UNKNOWN;
+ int ret;

if (has_target()) {
/* Update policy governor to the one used before hotplug. */
- gov = find_governor(policy->last_governor);
+ gov = get_governor(policy->last_governor);
if (gov) {
pr_debug("Restoring governor %s for cpu %d\n",
policy->governor->name, policy->cpu);
} else if (def_gov) {
gov = def_gov;
+ __module_get(gov->owner);
} else {
return -ENODATA;
}
@@ -1084,7 +1092,11 @@ static int cpufreq_init_policy(struct cp
return -ENODATA;
}

- return cpufreq_set_policy(policy, gov, pol);
+ ret = cpufreq_set_policy(policy, gov, pol);
+ if (gov)
+ module_put(gov->owner);
+
+ return ret;
}

static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)