diff options
author | Geert Uytterhoeven <geert+renesas@glider.be> | 2015-06-26 05:14:14 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-07-06 19:13:07 -0400 |
commit | 93af5e93544328285a6f65f7d47bbea8979b28fb (patch) | |
tree | 663bee85700a74fd773f2cf3a4860a0107af0160 /drivers/base | |
parent | d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754 (diff) |
PM / Domains: Avoid infinite loops in attach/detach code
If pm_genpd_{add,remove}_device() keeps on failing with -EAGAIN, we end
up with an infinite loop in genpd_dev_pm_{at,de}tach().
This may happen due to a genpd.prepared_count imbalance. This is a bug
elsewhere, but it will result in a system lock up, possibly during
reboot of an otherwise functioning system.
To avoid this, put a limit on the maximum number of loop iterations,
using an exponential back-off mechanism. If the limit is reached, the
operation will just fail. An error message is already printed.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/domain.c | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index cdd547bd67df..0ee43c1056e0 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * This file is released under the GPLv2. | 6 | * This file is released under the GPLv2. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/delay.h> | ||
9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
10 | #include <linux/io.h> | 11 | #include <linux/io.h> |
11 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
@@ -19,6 +20,8 @@ | |||
19 | #include <linux/suspend.h> | 20 | #include <linux/suspend.h> |
20 | #include <linux/export.h> | 21 | #include <linux/export.h> |
21 | 22 | ||
23 | #define GENPD_RETRY_MAX_MS 250 /* Approximate */ | ||
24 | |||
22 | #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ | 25 | #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ |
23 | ({ \ | 26 | ({ \ |
24 | type (*__routine)(struct device *__d); \ | 27 | type (*__routine)(struct device *__d); \ |
@@ -2131,6 +2134,7 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); | |||
2131 | static void genpd_dev_pm_detach(struct device *dev, bool power_off) | 2134 | static void genpd_dev_pm_detach(struct device *dev, bool power_off) |
2132 | { | 2135 | { |
2133 | struct generic_pm_domain *pd; | 2136 | struct generic_pm_domain *pd; |
2137 | unsigned int i; | ||
2134 | int ret = 0; | 2138 | int ret = 0; |
2135 | 2139 | ||
2136 | pd = pm_genpd_lookup_dev(dev); | 2140 | pd = pm_genpd_lookup_dev(dev); |
@@ -2139,10 +2143,12 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off) | |||
2139 | 2143 | ||
2140 | dev_dbg(dev, "removing from PM domain %s\n", pd->name); | 2144 | dev_dbg(dev, "removing from PM domain %s\n", pd->name); |
2141 | 2145 | ||
2142 | while (1) { | 2146 | for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { |
2143 | ret = pm_genpd_remove_device(pd, dev); | 2147 | ret = pm_genpd_remove_device(pd, dev); |
2144 | if (ret != -EAGAIN) | 2148 | if (ret != -EAGAIN) |
2145 | break; | 2149 | break; |
2150 | |||
2151 | mdelay(i); | ||
2146 | cond_resched(); | 2152 | cond_resched(); |
2147 | } | 2153 | } |
2148 | 2154 | ||
@@ -2183,6 +2189,7 @@ int genpd_dev_pm_attach(struct device *dev) | |||
2183 | { | 2189 | { |
2184 | struct of_phandle_args pd_args; | 2190 | struct of_phandle_args pd_args; |
2185 | struct generic_pm_domain *pd; | 2191 | struct generic_pm_domain *pd; |
2192 | unsigned int i; | ||
2186 | int ret; | 2193 | int ret; |
2187 | 2194 | ||
2188 | if (!dev->of_node) | 2195 | if (!dev->of_node) |
@@ -2218,10 +2225,12 @@ int genpd_dev_pm_attach(struct device *dev) | |||
2218 | 2225 | ||
2219 | dev_dbg(dev, "adding to PM domain %s\n", pd->name); | 2226 | dev_dbg(dev, "adding to PM domain %s\n", pd->name); |
2220 | 2227 | ||
2221 | while (1) { | 2228 | for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { |
2222 | ret = pm_genpd_add_device(pd, dev); | 2229 | ret = pm_genpd_add_device(pd, dev); |
2223 | if (ret != -EAGAIN) | 2230 | if (ret != -EAGAIN) |
2224 | break; | 2231 | break; |
2232 | |||
2233 | mdelay(i); | ||
2225 | cond_resched(); | 2234 | cond_resched(); |
2226 | } | 2235 | } |
2227 | 2236 | ||