aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXiaoguang Chen <chenxg@marvell.com>2013-06-19 03:00:07 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-04-14 09:42:19 -0400
commitba17ca46b968001df16f672ffe694fd0a12512f2 (patch)
tree41df62a0206dc89db6b46b979f9eeb53576736b1
parent87f93ce8004df49c51813ac113047ac22bced3c9 (diff)
cpufreq: Fix governor start/stop race condition
commit 95731ebb114c5f0c028459388560fc2a72fe5049 upstream. Cpufreq governors' stop and start operations should be carried out in sequence. Otherwise, there will be unexpected behavior, like in the example below. Suppose there are 4 CPUs and policy->cpu=CPU0, CPU1/2/3 are linked to CPU0. The normal sequence is: 1) Current governor is userspace. An application tries to set the governor to ondemand. It will call __cpufreq_set_policy() in which it will stop the userspace governor and then start the ondemand governor. 2) Current governor is userspace. The online of CPU3 runs on CPU0. It will call cpufreq_add_policy_cpu() in which it will first stop the userspace governor, and then start it again. If the sequence of the above two cases interleaves, it becomes: 1) Application stops userspace governor 2) Hotplug stops userspace governor which is a problem, because the governor shouldn't be stopped twice in a row. What happens next is: 3) Application starts ondemand governor 4) Hotplug starts a governor In step 4, the hotplug is supposed to start the userspace governor, but now the governor has been changed by the application to ondemand, so the ondemand governor is started once again, which is incorrect. The solution is to prevent policy governors from being stopped multiple times in a row. A governor should only be stopped once for one policy. After it has been stopped, no more governor stop operations should be executed. Also add a mutex to serialize governor operations. [rjw: Changelog. And you owe me a beverage of my choice.] Signed-off-by: Xiaoguang Chen <chenxg@marvell.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--include/linux/cpufreq.h1
2 files changed, 25 insertions, 0 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 648554742a99..66f6cf5064ec 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -46,6 +46,7 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
46static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); 46static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
47#endif 47#endif
48static DEFINE_RWLOCK(cpufreq_driver_lock); 48static DEFINE_RWLOCK(cpufreq_driver_lock);
49static DEFINE_MUTEX(cpufreq_governor_lock);
49 50
50/* 51/*
51 * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure 52 * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure
@@ -1563,6 +1564,21 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
1563 1564
1564 pr_debug("__cpufreq_governor for CPU %u, event %u\n", 1565 pr_debug("__cpufreq_governor for CPU %u, event %u\n",
1565 policy->cpu, event); 1566 policy->cpu, event);
1567
1568 mutex_lock(&cpufreq_governor_lock);
1569 if ((!policy->governor_enabled && (event == CPUFREQ_GOV_STOP)) ||
1570 (policy->governor_enabled && (event == CPUFREQ_GOV_START))) {
1571 mutex_unlock(&cpufreq_governor_lock);
1572 return -EBUSY;
1573 }
1574
1575 if (event == CPUFREQ_GOV_STOP)
1576 policy->governor_enabled = false;
1577 else if (event == CPUFREQ_GOV_START)
1578 policy->governor_enabled = true;
1579
1580 mutex_unlock(&cpufreq_governor_lock);
1581
1566 ret = policy->governor->governor(policy, event); 1582 ret = policy->governor->governor(policy, event);
1567 1583
1568 if (!ret) { 1584 if (!ret) {
@@ -1570,6 +1586,14 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
1570 policy->governor->initialized++; 1586 policy->governor->initialized++;
1571 else if (event == CPUFREQ_GOV_POLICY_EXIT) 1587 else if (event == CPUFREQ_GOV_POLICY_EXIT)
1572 policy->governor->initialized--; 1588 policy->governor->initialized--;
1589 } else {
1590 /* Restore original values */
1591 mutex_lock(&cpufreq_governor_lock);
1592 if (event == CPUFREQ_GOV_STOP)
1593 policy->governor_enabled = true;
1594 else if (event == CPUFREQ_GOV_START)
1595 policy->governor_enabled = false;
1596 mutex_unlock(&cpufreq_governor_lock);
1573 } 1597 }
1574 1598
1575 /* we keep one module reference alive for 1599 /* we keep one module reference alive for
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 037d36ae63e5..1a81b7470bc4 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -107,6 +107,7 @@ struct cpufreq_policy {
107 unsigned int policy; /* see above */ 107 unsigned int policy; /* see above */
108 struct cpufreq_governor *governor; /* see below */ 108 struct cpufreq_governor *governor; /* see below */
109 void *governor_data; 109 void *governor_data;
110 bool governor_enabled; /* governor start/stop flag */
110 111
111 struct work_struct update; /* if update_policy() needs to be 112 struct work_struct update; /* if update_policy() needs to be
112 * called, but you're in IRQ context */ 113 * called, but you're in IRQ context */