diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-29 15:46:33 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-29 15:46:33 -0500 |
commit | 26ab93438fbacc653c60ea4d148b6ca16919ab1b (patch) | |
tree | 7f95470de4a37770b80f510b5fb4e807b494c0ab | |
parent | 9ee71f513c698b05f67e74f7ce66ba4f23f9073f (diff) | |
parent | ea83f81b489be3be268ed7fabfe8dd94bdc45a29 (diff) |
Merge branch 'pm-opp'
* pm-opp:
PM / OPP: using kfree_rcu() to simplify the code
PM / OPP: predictable fail results for opp_find* functions, v2
PM / OPP: Export symbols for module usage.
PM / OPP: RCU reclaim
-rw-r--r-- | drivers/base/power/opp.c | 44 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 4 |
2 files changed, 33 insertions, 15 deletions
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d9468642fc41..50b2831e027d 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/rcupdate.h> | 23 | #include <linux/rcupdate.h> |
24 | #include <linux/opp.h> | 24 | #include <linux/opp.h> |
25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
26 | #include <linux/export.h> | ||
26 | 27 | ||
27 | /* | 28 | /* |
28 | * Internal data structure organization with the OPP layer library is as | 29 | * Internal data structure organization with the OPP layer library is as |
@@ -65,6 +66,7 @@ struct opp { | |||
65 | unsigned long u_volt; | 66 | unsigned long u_volt; |
66 | 67 | ||
67 | struct device_opp *dev_opp; | 68 | struct device_opp *dev_opp; |
69 | struct rcu_head head; | ||
68 | }; | 70 | }; |
69 | 71 | ||
70 | /** | 72 | /** |
@@ -160,6 +162,7 @@ unsigned long opp_get_voltage(struct opp *opp) | |||
160 | 162 | ||
161 | return v; | 163 | return v; |
162 | } | 164 | } |
165 | EXPORT_SYMBOL(opp_get_voltage); | ||
163 | 166 | ||
164 | /** | 167 | /** |
165 | * opp_get_freq() - Gets the frequency corresponding to an available opp | 168 | * opp_get_freq() - Gets the frequency corresponding to an available opp |
@@ -189,6 +192,7 @@ unsigned long opp_get_freq(struct opp *opp) | |||
189 | 192 | ||
190 | return f; | 193 | return f; |
191 | } | 194 | } |
195 | EXPORT_SYMBOL(opp_get_freq); | ||
192 | 196 | ||
193 | /** | 197 | /** |
194 | * opp_get_opp_count() - Get number of opps available in the opp list | 198 | * opp_get_opp_count() - Get number of opps available in the opp list |
@@ -221,6 +225,7 @@ int opp_get_opp_count(struct device *dev) | |||
221 | 225 | ||
222 | return count; | 226 | return count; |
223 | } | 227 | } |
228 | EXPORT_SYMBOL(opp_get_opp_count); | ||
224 | 229 | ||
225 | /** | 230 | /** |
226 | * opp_find_freq_exact() - search for an exact frequency | 231 | * opp_find_freq_exact() - search for an exact frequency |
@@ -230,7 +235,10 @@ int opp_get_opp_count(struct device *dev) | |||
230 | * | 235 | * |
231 | * Searches for exact match in the opp list and returns pointer to the matching | 236 | * Searches for exact match in the opp list and returns pointer to the matching |
232 | * opp if found, else returns ERR_PTR in case of error and should be handled | 237 | * opp if found, else returns ERR_PTR in case of error and should be handled |
233 | * using IS_ERR. | 238 | * using IS_ERR. Error return values can be: |
239 | * EINVAL: for bad pointer | ||
240 | * ERANGE: no match found for search | ||
241 | * ENODEV: if device not found in list of registered devices | ||
234 | * | 242 | * |
235 | * Note: available is a modifier for the search. if available=true, then the | 243 | * Note: available is a modifier for the search. if available=true, then the |
236 | * match is for exact matching frequency and is available in the stored OPP | 244 | * match is for exact matching frequency and is available in the stored OPP |
@@ -249,7 +257,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, | |||
249 | bool available) | 257 | bool available) |
250 | { | 258 | { |
251 | struct device_opp *dev_opp; | 259 | struct device_opp *dev_opp; |
252 | struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); | 260 | struct opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
253 | 261 | ||
254 | dev_opp = find_device_opp(dev); | 262 | dev_opp = find_device_opp(dev); |
255 | if (IS_ERR(dev_opp)) { | 263 | if (IS_ERR(dev_opp)) { |
@@ -268,6 +276,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, | |||
268 | 276 | ||
269 | return opp; | 277 | return opp; |
270 | } | 278 | } |
279 | EXPORT_SYMBOL(opp_find_freq_exact); | ||
271 | 280 | ||
272 | /** | 281 | /** |
273 | * opp_find_freq_ceil() - Search for an rounded ceil freq | 282 | * opp_find_freq_ceil() - Search for an rounded ceil freq |
@@ -278,7 +287,11 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, | |||
278 | * for a device. | 287 | * for a device. |
279 | * | 288 | * |
280 | * Returns matching *opp and refreshes *freq accordingly, else returns | 289 | * Returns matching *opp and refreshes *freq accordingly, else returns |
281 | * ERR_PTR in case of error and should be handled using IS_ERR. | 290 | * ERR_PTR in case of error and should be handled using IS_ERR. Error return |
291 | * values can be: | ||
292 | * EINVAL: for bad pointer | ||
293 | * ERANGE: no match found for search | ||
294 | * ENODEV: if device not found in list of registered devices | ||
282 | * | 295 | * |
283 | * Locking: This function must be called under rcu_read_lock(). opp is a rcu | 296 | * Locking: This function must be called under rcu_read_lock(). opp is a rcu |
284 | * protected pointer. The reason for the same is that the opp pointer which is | 297 | * protected pointer. The reason for the same is that the opp pointer which is |
@@ -289,7 +302,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, | |||
289 | struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) | 302 | struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) |
290 | { | 303 | { |
291 | struct device_opp *dev_opp; | 304 | struct device_opp *dev_opp; |
292 | struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); | 305 | struct opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
293 | 306 | ||
294 | if (!dev || !freq) { | 307 | if (!dev || !freq) { |
295 | dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); | 308 | dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); |
@@ -298,7 +311,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) | |||
298 | 311 | ||
299 | dev_opp = find_device_opp(dev); | 312 | dev_opp = find_device_opp(dev); |
300 | if (IS_ERR(dev_opp)) | 313 | if (IS_ERR(dev_opp)) |
301 | return opp; | 314 | return ERR_CAST(dev_opp); |
302 | 315 | ||
303 | list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { | 316 | list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { |
304 | if (temp_opp->available && temp_opp->rate >= *freq) { | 317 | if (temp_opp->available && temp_opp->rate >= *freq) { |
@@ -310,6 +323,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) | |||
310 | 323 | ||
311 | return opp; | 324 | return opp; |
312 | } | 325 | } |
326 | EXPORT_SYMBOL(opp_find_freq_ceil); | ||
313 | 327 | ||
314 | /** | 328 | /** |
315 | * opp_find_freq_floor() - Search for a rounded floor freq | 329 | * opp_find_freq_floor() - Search for a rounded floor freq |
@@ -320,7 +334,11 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) | |||
320 | * for a device. | 334 | * for a device. |
321 | * | 335 | * |
322 | * Returns matching *opp and refreshes *freq accordingly, else returns | 336 | * Returns matching *opp and refreshes *freq accordingly, else returns |
323 | * ERR_PTR in case of error and should be handled using IS_ERR. | 337 | * ERR_PTR in case of error and should be handled using IS_ERR. Error return |
338 | * values can be: | ||
339 | * EINVAL: for bad pointer | ||
340 | * ERANGE: no match found for search | ||
341 | * ENODEV: if device not found in list of registered devices | ||
324 | * | 342 | * |
325 | * Locking: This function must be called under rcu_read_lock(). opp is a rcu | 343 | * Locking: This function must be called under rcu_read_lock(). opp is a rcu |
326 | * protected pointer. The reason for the same is that the opp pointer which is | 344 | * protected pointer. The reason for the same is that the opp pointer which is |
@@ -331,7 +349,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) | |||
331 | struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) | 349 | struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) |
332 | { | 350 | { |
333 | struct device_opp *dev_opp; | 351 | struct device_opp *dev_opp; |
334 | struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); | 352 | struct opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
335 | 353 | ||
336 | if (!dev || !freq) { | 354 | if (!dev || !freq) { |
337 | dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); | 355 | dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); |
@@ -340,7 +358,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) | |||
340 | 358 | ||
341 | dev_opp = find_device_opp(dev); | 359 | dev_opp = find_device_opp(dev); |
342 | if (IS_ERR(dev_opp)) | 360 | if (IS_ERR(dev_opp)) |
343 | return opp; | 361 | return ERR_CAST(dev_opp); |
344 | 362 | ||
345 | list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { | 363 | list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { |
346 | if (temp_opp->available) { | 364 | if (temp_opp->available) { |
@@ -356,6 +374,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) | |||
356 | 374 | ||
357 | return opp; | 375 | return opp; |
358 | } | 376 | } |
377 | EXPORT_SYMBOL(opp_find_freq_floor); | ||
359 | 378 | ||
360 | /** | 379 | /** |
361 | * opp_add() - Add an OPP table from a table definitions | 380 | * opp_add() - Add an OPP table from a table definitions |
@@ -512,7 +531,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq, | |||
512 | 531 | ||
513 | list_replace_rcu(&opp->node, &new_opp->node); | 532 | list_replace_rcu(&opp->node, &new_opp->node); |
514 | mutex_unlock(&dev_opp_list_lock); | 533 | mutex_unlock(&dev_opp_list_lock); |
515 | synchronize_rcu(); | 534 | kfree_rcu(opp, head); |
516 | 535 | ||
517 | /* Notify the change of the OPP availability */ | 536 | /* Notify the change of the OPP availability */ |
518 | if (availability_req) | 537 | if (availability_req) |
@@ -522,13 +541,10 @@ static int opp_set_availability(struct device *dev, unsigned long freq, | |||
522 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE, | 541 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE, |
523 | new_opp); | 542 | new_opp); |
524 | 543 | ||
525 | /* clean up old opp */ | 544 | return 0; |
526 | new_opp = opp; | ||
527 | goto out; | ||
528 | 545 | ||
529 | unlock: | 546 | unlock: |
530 | mutex_unlock(&dev_opp_list_lock); | 547 | mutex_unlock(&dev_opp_list_lock); |
531 | out: | ||
532 | kfree(new_opp); | 548 | kfree(new_opp); |
533 | return r; | 549 | return r; |
534 | } | 550 | } |
@@ -552,6 +568,7 @@ int opp_enable(struct device *dev, unsigned long freq) | |||
552 | { | 568 | { |
553 | return opp_set_availability(dev, freq, true); | 569 | return opp_set_availability(dev, freq, true); |
554 | } | 570 | } |
571 | EXPORT_SYMBOL(opp_enable); | ||
555 | 572 | ||
556 | /** | 573 | /** |
557 | * opp_disable() - Disable a specific OPP | 574 | * opp_disable() - Disable a specific OPP |
@@ -573,6 +590,7 @@ int opp_disable(struct device *dev, unsigned long freq) | |||
573 | { | 590 | { |
574 | return opp_set_availability(dev, freq, false); | 591 | return opp_set_availability(dev, freq, false); |
575 | } | 592 | } |
593 | EXPORT_SYMBOL(opp_disable); | ||
576 | 594 | ||
577 | #ifdef CONFIG_CPU_FREQ | 595 | #ifdef CONFIG_CPU_FREQ |
578 | /** | 596 | /** |
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b146d76f04cf..4fa1a22c55ea 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -656,14 +656,14 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, | |||
656 | opp = opp_find_freq_floor(dev, freq); | 656 | opp = opp_find_freq_floor(dev, freq); |
657 | 657 | ||
658 | /* If not available, use the closest opp */ | 658 | /* If not available, use the closest opp */ |
659 | if (opp == ERR_PTR(-ENODEV)) | 659 | if (opp == ERR_PTR(-ERANGE)) |
660 | opp = opp_find_freq_ceil(dev, freq); | 660 | opp = opp_find_freq_ceil(dev, freq); |
661 | } else { | 661 | } else { |
662 | /* The freq is an lower bound. opp should be higher */ | 662 | /* The freq is an lower bound. opp should be higher */ |
663 | opp = opp_find_freq_ceil(dev, freq); | 663 | opp = opp_find_freq_ceil(dev, freq); |
664 | 664 | ||
665 | /* If not available, use the closest opp */ | 665 | /* If not available, use the closest opp */ |
666 | if (opp == ERR_PTR(-ENODEV)) | 666 | if (opp == ERR_PTR(-ERANGE)) |
667 | opp = opp_find_freq_floor(dev, freq); | 667 | opp = opp_find_freq_floor(dev, freq); |
668 | } | 668 | } |
669 | 669 | ||