diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-12-01 05:25:40 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-12-07 19:46:06 -0500 |
commit | c8ce82b9b9c40d66709cce588f6281dd47cc3922 (patch) | |
tree | 8734984b6eb9c2043e519d95e4e6906b96943953 /drivers/devfreq/exynos-bus.c | |
parent | c261e4c7153f307cd5f78ac6492da21ce7004310 (diff) |
devfreq: exynos: Don't use OPP structures outside of RCU locks
The OPP structures are abused to the best here, without understanding
how the OPP core and RCU locks work.
In short, the OPP pointer saved 'struct exynos_bus' can become invalid
under your nose, as the OPP core may free it.
Fix various abuses around OPP structures and calls.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/devfreq/exynos-bus.c')
-rw-r--r-- | drivers/devfreq/exynos-bus.c | 29 |
1 files changed, 14 insertions, 15 deletions
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index 29866f7e6d7e..a8ed7792ece2 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c | |||
@@ -35,7 +35,7 @@ struct exynos_bus { | |||
35 | unsigned int edev_count; | 35 | unsigned int edev_count; |
36 | struct mutex lock; | 36 | struct mutex lock; |
37 | 37 | ||
38 | struct dev_pm_opp *curr_opp; | 38 | unsigned long curr_freq; |
39 | 39 | ||
40 | struct regulator *regulator; | 40 | struct regulator *regulator; |
41 | struct clk *clk; | 41 | struct clk *clk; |
@@ -99,7 +99,7 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) | |||
99 | { | 99 | { |
100 | struct exynos_bus *bus = dev_get_drvdata(dev); | 100 | struct exynos_bus *bus = dev_get_drvdata(dev); |
101 | struct dev_pm_opp *new_opp; | 101 | struct dev_pm_opp *new_opp; |
102 | unsigned long old_freq, new_freq, old_volt, new_volt, tol; | 102 | unsigned long old_freq, new_freq, new_volt, tol; |
103 | int ret = 0; | 103 | int ret = 0; |
104 | 104 | ||
105 | /* Get new opp-bus instance according to new bus clock */ | 105 | /* Get new opp-bus instance according to new bus clock */ |
@@ -113,8 +113,7 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) | |||
113 | 113 | ||
114 | new_freq = dev_pm_opp_get_freq(new_opp); | 114 | new_freq = dev_pm_opp_get_freq(new_opp); |
115 | new_volt = dev_pm_opp_get_voltage(new_opp); | 115 | new_volt = dev_pm_opp_get_voltage(new_opp); |
116 | old_freq = dev_pm_opp_get_freq(bus->curr_opp); | 116 | old_freq = bus->curr_freq; |
117 | old_volt = dev_pm_opp_get_voltage(bus->curr_opp); | ||
118 | rcu_read_unlock(); | 117 | rcu_read_unlock(); |
119 | 118 | ||
120 | if (old_freq == new_freq) | 119 | if (old_freq == new_freq) |
@@ -146,7 +145,7 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) | |||
146 | goto out; | 145 | goto out; |
147 | } | 146 | } |
148 | } | 147 | } |
149 | bus->curr_opp = new_opp; | 148 | bus->curr_freq = new_freq; |
150 | 149 | ||
151 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", | 150 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", |
152 | old_freq/1000, new_freq/1000); | 151 | old_freq/1000, new_freq/1000); |
@@ -163,9 +162,7 @@ static int exynos_bus_get_dev_status(struct device *dev, | |||
163 | struct devfreq_event_data edata; | 162 | struct devfreq_event_data edata; |
164 | int ret; | 163 | int ret; |
165 | 164 | ||
166 | rcu_read_lock(); | 165 | stat->current_frequency = bus->curr_freq; |
167 | stat->current_frequency = dev_pm_opp_get_freq(bus->curr_opp); | ||
168 | rcu_read_unlock(); | ||
169 | 166 | ||
170 | ret = exynos_bus_get_event(bus, &edata); | 167 | ret = exynos_bus_get_event(bus, &edata); |
171 | if (ret < 0) { | 168 | if (ret < 0) { |
@@ -226,7 +223,7 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq, | |||
226 | } | 223 | } |
227 | 224 | ||
228 | new_freq = dev_pm_opp_get_freq(new_opp); | 225 | new_freq = dev_pm_opp_get_freq(new_opp); |
229 | old_freq = dev_pm_opp_get_freq(bus->curr_opp); | 226 | old_freq = bus->curr_freq; |
230 | rcu_read_unlock(); | 227 | rcu_read_unlock(); |
231 | 228 | ||
232 | if (old_freq == new_freq) | 229 | if (old_freq == new_freq) |
@@ -242,7 +239,7 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq, | |||
242 | } | 239 | } |
243 | 240 | ||
244 | *freq = new_freq; | 241 | *freq = new_freq; |
245 | bus->curr_opp = new_opp; | 242 | bus->curr_freq = new_freq; |
246 | 243 | ||
247 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", | 244 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", |
248 | old_freq/1000, new_freq/1000); | 245 | old_freq/1000, new_freq/1000); |
@@ -335,6 +332,7 @@ static int exynos_bus_parse_of(struct device_node *np, | |||
335 | struct exynos_bus *bus) | 332 | struct exynos_bus *bus) |
336 | { | 333 | { |
337 | struct device *dev = bus->dev; | 334 | struct device *dev = bus->dev; |
335 | struct dev_pm_opp *opp; | ||
338 | unsigned long rate; | 336 | unsigned long rate; |
339 | int ret; | 337 | int ret; |
340 | 338 | ||
@@ -352,22 +350,23 @@ static int exynos_bus_parse_of(struct device_node *np, | |||
352 | } | 350 | } |
353 | 351 | ||
354 | /* Get the freq and voltage from OPP table to scale the bus freq */ | 352 | /* Get the freq and voltage from OPP table to scale the bus freq */ |
355 | rcu_read_lock(); | ||
356 | ret = dev_pm_opp_of_add_table(dev); | 353 | ret = dev_pm_opp_of_add_table(dev); |
357 | if (ret < 0) { | 354 | if (ret < 0) { |
358 | dev_err(dev, "failed to get OPP table\n"); | 355 | dev_err(dev, "failed to get OPP table\n"); |
359 | rcu_read_unlock(); | ||
360 | goto err_clk; | 356 | goto err_clk; |
361 | } | 357 | } |
362 | 358 | ||
363 | rate = clk_get_rate(bus->clk); | 359 | rate = clk_get_rate(bus->clk); |
364 | bus->curr_opp = devfreq_recommended_opp(dev, &rate, 0); | 360 | |
365 | if (IS_ERR(bus->curr_opp)) { | 361 | rcu_read_lock(); |
362 | opp = devfreq_recommended_opp(dev, &rate, 0); | ||
363 | if (IS_ERR(opp)) { | ||
366 | dev_err(dev, "failed to find dev_pm_opp\n"); | 364 | dev_err(dev, "failed to find dev_pm_opp\n"); |
367 | rcu_read_unlock(); | 365 | rcu_read_unlock(); |
368 | ret = PTR_ERR(bus->curr_opp); | 366 | ret = PTR_ERR(opp); |
369 | goto err_opp; | 367 | goto err_opp; |
370 | } | 368 | } |
369 | bus->curr_freq = dev_pm_opp_get_freq(opp); | ||
371 | rcu_read_unlock(); | 370 | rcu_read_unlock(); |
372 | 371 | ||
373 | return 0; | 372 | return 0; |