diff mbox series

cpufreq: Fix locking issues with governors

Message ID 49c7d64460cdb39b006991e5251260eb0eea9f2a.1593082448.git.viresh.kumar@linaro.org
State Superseded
Headers show
Series cpufreq: Fix locking issues with governors | expand

Commit Message

Viresh Kumar June 25, 2020, 10:54 a.m. UTC
The locking around governors handling isn't adequate currently. The list
of governors should never be traversed without locking in place. Also we
must make sure the governor isn't removed while it is still referenced
by code.

Reported-by: Quentin Perret <qperret@google.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

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

-- 
2.25.0.rc1.19.g042ed3e048af

Comments

Quentin Perret June 25, 2020, 11:14 a.m. UTC | #1
Hey Viresh

On Thursday 25 Jun 2020 at 16:24:16 (+0530), Viresh Kumar wrote:
> The locking around governors handling isn't adequate currently. The list

> of governors should never be traversed without locking in place. Also we

> must make sure the governor isn't removed while it is still referenced

> by code.


Thanks for having a look at this!

This solves the issue for the reference to policy->last_governor, but
given that your patch is based on top of
20200623142138.209513-3-qperret@google.com, 'default_governor' needs a
similar treatment I think.

Perhaps something along the lines of the (completely untested) snippet
below?

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index dad6b85f4c89..9d7cf2ce2768 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1062,6 +1062,17 @@ __weak struct cpufreq_governor *cpufreq_default_governor(void)
 	return NULL;
 }
 
+static bool get_default_governor(void)
+{
+	bool ret;
+
+	mutex_lock(&cpufreq_governor_mutex);
+	ret = default_governor && !try_module_get(default_governor->owner);
+	mutex_unlock(&cpufreq_governor_mutex);
+
+	return ret;
+}
+
 static int cpufreq_init_policy(struct cpufreq_policy *policy)
 {
 	struct cpufreq_governor *gov = NULL;
@@ -1073,20 +1084,21 @@ static int cpufreq_init_policy(struct cpufreq_policy *policy)
 		/* Update policy governor to the one used before hotplug. */
 		gov = get_governor(policy->last_governor);
 		if (gov) {
-			put_governor = true;
 			pr_debug("Restoring governor %s for cpu %d\n",
 				 policy->governor->name, policy->cpu);
-		} else if (default_governor) {
+		} else if (get_default_governor()) {
 			gov = default_governor;
 		} else {
 			return -ENODATA;
 		}
+		put_governor = true;
 	} else {
 		/* Use the default policy if there is no last_policy. */
 		if (policy->last_policy) {
 			pol = policy->last_policy;
-		} else if (default_governor) {
+		} else if (get_default_governor()) {
 			pol = cpufreq_parse_policy(default_governor->name);
+			module_put(default_governor->owner);
 			/*
 			 * In case the default governor is neiter "performance"
 			 * nor "powersave", fall back to the initial policy
Rafael J. Wysocki June 25, 2020, 1:32 p.m. UTC | #2
On Thu, Jun 25, 2020 at 1:14 PM Quentin Perret <qperret@google.com> wrote:
>

> Hey Viresh

>

> On Thursday 25 Jun 2020 at 16:24:16 (+0530), Viresh Kumar wrote:

> > The locking around governors handling isn't adequate currently. The list

> > of governors should never be traversed without locking in place. Also we

> > must make sure the governor isn't removed while it is still referenced

> > by code.

>

> Thanks for having a look at this!

>

> This solves the issue for the reference to policy->last_governor, but

> given that your patch is based on top of

> 20200623142138.209513-3-qperret@google.com, 'default_governor' needs a

> similar treatment I think.


So I would prefer to rebase the $subject patch from Viresh on top of
the current mainline, apply it first and rebase the "default governor"
series on top of it - and include the changes needed for the default
governor handling in there.

Thanks!
Quentin Perret June 25, 2020, 1:53 p.m. UTC | #3
On Thursday 25 Jun 2020 at 15:32:43 (+0200), Rafael J. Wysocki wrote:
> On Thu, Jun 25, 2020 at 1:14 PM Quentin Perret <qperret@google.com> wrote:

> >

> > Hey Viresh

> >

> > On Thursday 25 Jun 2020 at 16:24:16 (+0530), Viresh Kumar wrote:

> > > The locking around governors handling isn't adequate currently. The list

> > > of governors should never be traversed without locking in place. Also we

> > > must make sure the governor isn't removed while it is still referenced

> > > by code.

> >

> > Thanks for having a look at this!

> >

> > This solves the issue for the reference to policy->last_governor, but

> > given that your patch is based on top of

> > 20200623142138.209513-3-qperret@google.com, 'default_governor' needs a

> > similar treatment I think.

> 

> So I would prefer to rebase the $subject patch from Viresh on top of

> the current mainline, apply it first and rebase the "default governor"

> series on top of it - and include the changes needed for the default

> governor handling in there.


Right, and Viresh's patch might be -stable material too? In any case,
making it standalone makes a lot of sense.

Thanks,
Quentin
diff mbox series

Patch

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 4b1a5c0173cf..dad6b85f4c89 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -624,6 +624,24 @@  static struct cpufreq_governor *find_governor(const char *str_governor)
 	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))
@@ -643,28 +661,14 @@  static struct cpufreq_governor *cpufreq_parse_governor(char *str_governor)
 {
 	struct cpufreq_governor *t;
 
-	mutex_lock(&cpufreq_governor_mutex);
-
-	t = find_governor(str_governor);
-	if (!t) {
-		int ret;
-
-		mutex_unlock(&cpufreq_governor_mutex);
-
-		ret = request_module("cpufreq_%s", str_governor);
-		if (ret)
-			return NULL;
-
-		mutex_lock(&cpufreq_governor_mutex);
+	t = get_governor(str_governor);
+	if (t)
+		return t;
 
-		t = find_governor(str_governor);
-	}
-	if (t && !try_module_get(t->owner))
-		t = NULL;
-
-	mutex_unlock(&cpufreq_governor_mutex);
+	if (request_module("cpufreq_%s", str_governor))
+		return NULL;
 
-	return t;
+	return get_governor(str_governor);
 }
 
 /**
@@ -818,12 +822,14 @@  static ssize_t show_scaling_available_governors(struct cpufreq_policy *policy,
 		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;
@@ -1060,11 +1066,14 @@  static int cpufreq_init_policy(struct cpufreq_policy *policy)
 {
 	struct cpufreq_governor *gov = NULL;
 	unsigned int pol = CPUFREQ_POLICY_UNKNOWN;
+	bool put_governor = false;
+	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) {
+			put_governor = true;
 			pr_debug("Restoring governor %s for cpu %d\n",
 				 policy->governor->name, policy->cpu);
 		} else if (default_governor) {
@@ -1091,7 +1100,11 @@  static int cpufreq_init_policy(struct cpufreq_policy *policy)
 			return -ENODATA;
 	}
 
-	return cpufreq_set_policy(policy, gov, pol);
+	ret = cpufreq_set_policy(policy, gov, pol);
+	if (put_governor)
+		module_put(gov->owner);
+
+	return ret;
 }
 
 static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)