aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-04 08:22:57 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-04 08:23:12 -0500
commit37530f2bda039774bd65aea14cc1d1dd26a82b9e (patch)
tree43a1e1e8f2189b200cf7a6df1bd22e1321c31a91
parentb81ea1b5ac4d3c6a628158b736dd4a98c46c29d9 (diff)
PM / QoS: Remove device PM QoS sysfs attributes at the right place
Device PM QoS sysfs attributes, if present during device removal, are removed from within device_pm_remove(), which is too late, since dpm_sysfs_remove() has already removed the whole attribute group they belonged to. However, moving the removal of those attributes to dpm_sysfs_remove() alone is not sufficient, because in theory they still can be re-added right after being removed by it (the device's driver is still bound to it at that point). For this reason, move the entire desctruction of device PM QoS constraints to dpm_sysfs_remove() and make it prevent any new constraints from being added after it has run. Also, move the initialization of the power.qos field in struct device to device_pm_init_common() and drop the no longer needed dev_pm_qos_constraints_init(). Reported-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/power/main.c2
-rw-r--r--drivers/base/power/power.h8
-rw-r--r--drivers/base/power/qos.c120
-rw-r--r--drivers/base/power/sysfs.c1
4 files changed, 55 insertions, 76 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 2b7f77d3fcb0..15beb500a4e4 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -99,7 +99,6 @@ void device_pm_add(struct device *dev)
99 dev_warn(dev, "parent %s should not be sleeping\n", 99 dev_warn(dev, "parent %s should not be sleeping\n",
100 dev_name(dev->parent)); 100 dev_name(dev->parent));
101 list_add_tail(&dev->power.entry, &dpm_list); 101 list_add_tail(&dev->power.entry, &dpm_list);
102 dev_pm_qos_constraints_init(dev);
103 mutex_unlock(&dpm_list_mtx); 102 mutex_unlock(&dpm_list_mtx);
104} 103}
105 104
@@ -113,7 +112,6 @@ void device_pm_remove(struct device *dev)
113 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 112 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
114 complete_all(&dev->power.completion); 113 complete_all(&dev->power.completion);
115 mutex_lock(&dpm_list_mtx); 114 mutex_lock(&dpm_list_mtx);
116 dev_pm_qos_constraints_destroy(dev);
117 list_del_init(&dev->power.entry); 115 list_del_init(&dev->power.entry);
118 mutex_unlock(&dpm_list_mtx); 116 mutex_unlock(&dpm_list_mtx);
119 device_wakeup_disable(dev); 117 device_wakeup_disable(dev);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b16686a0a5a2..cfc3226ec492 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -4,7 +4,7 @@ static inline void device_pm_init_common(struct device *dev)
4{ 4{
5 if (!dev->power.early_init) { 5 if (!dev->power.early_init) {
6 spin_lock_init(&dev->power.lock); 6 spin_lock_init(&dev->power.lock);
7 dev->power.power_state = PMSG_INVALID; 7 dev->power.qos = NULL;
8 dev->power.early_init = true; 8 dev->power.early_init = true;
9 } 9 }
10} 10}
@@ -56,14 +56,10 @@ extern void device_pm_move_last(struct device *);
56 56
57static inline void device_pm_sleep_init(struct device *dev) {} 57static inline void device_pm_sleep_init(struct device *dev) {}
58 58
59static inline void device_pm_add(struct device *dev) 59static inline void device_pm_add(struct device *dev) {}
60{
61 dev_pm_qos_constraints_init(dev);
62}
63 60
64static inline void device_pm_remove(struct device *dev) 61static inline void device_pm_remove(struct device *dev)
65{ 62{
66 dev_pm_qos_constraints_destroy(dev);
67 pm_runtime_remove(dev); 63 pm_runtime_remove(dev);
68} 64}
69 65
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 2159d62c858a..5f74587ef258 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -41,6 +41,7 @@
41#include <linux/mutex.h> 41#include <linux/mutex.h>
42#include <linux/export.h> 42#include <linux/export.h>
43#include <linux/pm_runtime.h> 43#include <linux/pm_runtime.h>
44#include <linux/err.h>
44 45
45#include "power.h" 46#include "power.h"
46 47
@@ -61,7 +62,7 @@ enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask)
61 struct pm_qos_flags *pqf; 62 struct pm_qos_flags *pqf;
62 s32 val; 63 s32 val;
63 64
64 if (!qos) 65 if (IS_ERR_OR_NULL(qos))
65 return PM_QOS_FLAGS_UNDEFINED; 66 return PM_QOS_FLAGS_UNDEFINED;
66 67
67 pqf = &qos->flags; 68 pqf = &qos->flags;
@@ -101,7 +102,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags);
101 */ 102 */
102s32 __dev_pm_qos_read_value(struct device *dev) 103s32 __dev_pm_qos_read_value(struct device *dev)
103{ 104{
104 return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; 105 return IS_ERR_OR_NULL(dev->power.qos) ?
106 0 : pm_qos_read_value(&dev->power.qos->latency);
105} 107}
106 108
107/** 109/**
@@ -198,20 +200,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
198 return 0; 200 return 0;
199} 201}
200 202
201/** 203static void __dev_pm_qos_hide_latency_limit(struct device *dev);
202 * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer. 204static void __dev_pm_qos_hide_flags(struct device *dev);
203 * @dev: target device
204 *
205 * Called from the device PM subsystem during device insertion under
206 * device_pm_lock().
207 */
208void dev_pm_qos_constraints_init(struct device *dev)
209{
210 mutex_lock(&dev_pm_qos_mtx);
211 dev->power.qos = NULL;
212 dev->power.power_state = PMSG_ON;
213 mutex_unlock(&dev_pm_qos_mtx);
214}
215 205
216/** 206/**
217 * dev_pm_qos_constraints_destroy 207 * dev_pm_qos_constraints_destroy
@@ -226,16 +216,15 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
226 struct pm_qos_constraints *c; 216 struct pm_qos_constraints *c;
227 struct pm_qos_flags *f; 217 struct pm_qos_flags *f;
228 218
219 mutex_lock(&dev_pm_qos_mtx);
220
229 /* 221 /*
230 * If the device's PM QoS resume latency limit or PM QoS flags have been 222 * If the device's PM QoS resume latency limit or PM QoS flags have been
231 * exposed to user space, they have to be hidden at this point. 223 * exposed to user space, they have to be hidden at this point.
232 */ 224 */
233 dev_pm_qos_hide_latency_limit(dev); 225 __dev_pm_qos_hide_latency_limit(dev);
234 dev_pm_qos_hide_flags(dev); 226 __dev_pm_qos_hide_flags(dev);
235
236 mutex_lock(&dev_pm_qos_mtx);
237 227
238 dev->power.power_state = PMSG_INVALID;
239 qos = dev->power.qos; 228 qos = dev->power.qos;
240 if (!qos) 229 if (!qos)
241 goto out; 230 goto out;
@@ -257,7 +246,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
257 } 246 }
258 247
259 spin_lock_irq(&dev->power.lock); 248 spin_lock_irq(&dev->power.lock);
260 dev->power.qos = NULL; 249 dev->power.qos = ERR_PTR(-ENODEV);
261 spin_unlock_irq(&dev->power.lock); 250 spin_unlock_irq(&dev->power.lock);
262 251
263 kfree(c->notifiers); 252 kfree(c->notifiers);
@@ -301,32 +290,19 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
301 "%s() called for already added request\n", __func__)) 290 "%s() called for already added request\n", __func__))
302 return -EINVAL; 291 return -EINVAL;
303 292
304 req->dev = dev;
305
306 mutex_lock(&dev_pm_qos_mtx); 293 mutex_lock(&dev_pm_qos_mtx);
307 294
308 if (!dev->power.qos) { 295 if (IS_ERR(dev->power.qos))
309 if (dev->power.power_state.event == PM_EVENT_INVALID) { 296 ret = -ENODEV;
310 /* The device has been removed from the system. */ 297 else if (!dev->power.qos)
311 req->dev = NULL; 298 ret = dev_pm_qos_constraints_allocate(dev);
312 ret = -ENODEV;
313 goto out;
314 } else {
315 /*
316 * Allocate the constraints data on the first call to
317 * add_request, i.e. only if the data is not already
318 * allocated and if the device has not been removed.
319 */
320 ret = dev_pm_qos_constraints_allocate(dev);
321 }
322 }
323 299
324 if (!ret) { 300 if (!ret) {
301 req->dev = dev;
325 req->type = type; 302 req->type = type;
326 ret = apply_constraint(req, PM_QOS_ADD_REQ, value); 303 ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
327 } 304 }
328 305
329 out:
330 mutex_unlock(&dev_pm_qos_mtx); 306 mutex_unlock(&dev_pm_qos_mtx);
331 307
332 return ret; 308 return ret;
@@ -351,7 +327,7 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
351 "%s() called for unknown object\n", __func__)) 327 "%s() called for unknown object\n", __func__))
352 return -EINVAL; 328 return -EINVAL;
353 329
354 if (!req->dev->power.qos) 330 if (IS_ERR_OR_NULL(req->dev->power.qos))
355 return -ENODEV; 331 return -ENODEV;
356 332
357 switch(req->type) { 333 switch(req->type) {
@@ -402,7 +378,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
402 378
403static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) 379static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
404{ 380{
405 int ret = 0; 381 int ret;
406 382
407 if (!req) /*guard against callers passing in null */ 383 if (!req) /*guard against callers passing in null */
408 return -EINVAL; 384 return -EINVAL;
@@ -411,13 +387,11 @@ static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
411 "%s() called for unknown object\n", __func__)) 387 "%s() called for unknown object\n", __func__))
412 return -EINVAL; 388 return -EINVAL;
413 389
414 if (req->dev->power.qos) { 390 if (IS_ERR_OR_NULL(req->dev->power.qos))
415 ret = apply_constraint(req, PM_QOS_REMOVE_REQ, 391 return -ENODEV;
416 PM_QOS_DEFAULT_VALUE); 392
417 memset(req, 0, sizeof(*req)); 393 ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
418 } else { 394 memset(req, 0, sizeof(*req));
419 ret = -ENODEV;
420 }
421 return ret; 395 return ret;
422} 396}
423 397
@@ -466,9 +440,10 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
466 440
467 mutex_lock(&dev_pm_qos_mtx); 441 mutex_lock(&dev_pm_qos_mtx);
468 442
469 if (!dev->power.qos) 443 if (IS_ERR(dev->power.qos))
470 ret = dev->power.power_state.event != PM_EVENT_INVALID ? 444 ret = -ENODEV;
471 dev_pm_qos_constraints_allocate(dev) : -ENODEV; 445 else if (!dev->power.qos)
446 ret = dev_pm_qos_constraints_allocate(dev);
472 447
473 if (!ret) 448 if (!ret)
474 ret = blocking_notifier_chain_register( 449 ret = blocking_notifier_chain_register(
@@ -497,7 +472,7 @@ int dev_pm_qos_remove_notifier(struct device *dev,
497 mutex_lock(&dev_pm_qos_mtx); 472 mutex_lock(&dev_pm_qos_mtx);
498 473
499 /* Silently return if the constraints object is not present. */ 474 /* Silently return if the constraints object is not present. */
500 if (dev->power.qos) 475 if (!IS_ERR_OR_NULL(dev->power.qos))
501 retval = blocking_notifier_chain_unregister( 476 retval = blocking_notifier_chain_unregister(
502 dev->power.qos->latency.notifiers, 477 dev->power.qos->latency.notifiers,
503 notifier); 478 notifier);
@@ -608,7 +583,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
608 583
609 mutex_lock(&dev_pm_qos_mtx); 584 mutex_lock(&dev_pm_qos_mtx);
610 585
611 if (!dev->power.qos) 586 if (IS_ERR_OR_NULL(dev->power.qos))
612 ret = -ENODEV; 587 ret = -ENODEV;
613 else if (dev->power.qos->latency_req) 588 else if (dev->power.qos->latency_req)
614 ret = -EEXIST; 589 ret = -EEXIST;
@@ -630,6 +605,14 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
630} 605}
631EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); 606EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);
632 607
608static void __dev_pm_qos_hide_latency_limit(struct device *dev)
609{
610 if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->latency_req) {
611 pm_qos_sysfs_remove_latency(dev);
612 __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY);
613 }
614}
615
633/** 616/**
634 * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. 617 * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space.
635 * @dev: Device whose PM QoS latency limit is to be hidden from user space. 618 * @dev: Device whose PM QoS latency limit is to be hidden from user space.
@@ -637,12 +620,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);
637void dev_pm_qos_hide_latency_limit(struct device *dev) 620void dev_pm_qos_hide_latency_limit(struct device *dev)
638{ 621{
639 mutex_lock(&dev_pm_qos_mtx); 622 mutex_lock(&dev_pm_qos_mtx);
640 623 __dev_pm_qos_hide_latency_limit(dev);
641 if (dev->power.qos && dev->power.qos->latency_req) {
642 pm_qos_sysfs_remove_latency(dev);
643 __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY);
644 }
645
646 mutex_unlock(&dev_pm_qos_mtx); 624 mutex_unlock(&dev_pm_qos_mtx);
647} 625}
648EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); 626EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit);
@@ -673,7 +651,7 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val)
673 pm_runtime_get_sync(dev); 651 pm_runtime_get_sync(dev);
674 mutex_lock(&dev_pm_qos_mtx); 652 mutex_lock(&dev_pm_qos_mtx);
675 653
676 if (!dev->power.qos) 654 if (IS_ERR_OR_NULL(dev->power.qos))
677 ret = -ENODEV; 655 ret = -ENODEV;
678 else if (dev->power.qos->flags_req) 656 else if (dev->power.qos->flags_req)
679 ret = -EEXIST; 657 ret = -EEXIST;
@@ -696,6 +674,14 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val)
696} 674}
697EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); 675EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags);
698 676
677static void __dev_pm_qos_hide_flags(struct device *dev)
678{
679 if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req) {
680 pm_qos_sysfs_remove_flags(dev);
681 __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
682 }
683}
684
699/** 685/**
700 * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. 686 * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space.
701 * @dev: Device whose PM QoS flags are to be hidden from user space. 687 * @dev: Device whose PM QoS flags are to be hidden from user space.
@@ -704,12 +690,7 @@ void dev_pm_qos_hide_flags(struct device *dev)
704{ 690{
705 pm_runtime_get_sync(dev); 691 pm_runtime_get_sync(dev);
706 mutex_lock(&dev_pm_qos_mtx); 692 mutex_lock(&dev_pm_qos_mtx);
707 693 __dev_pm_qos_hide_flags(dev);
708 if (dev->power.qos && dev->power.qos->flags_req) {
709 pm_qos_sysfs_remove_flags(dev);
710 __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
711 }
712
713 mutex_unlock(&dev_pm_qos_mtx); 694 mutex_unlock(&dev_pm_qos_mtx);
714 pm_runtime_put(dev); 695 pm_runtime_put(dev);
715} 696}
@@ -729,7 +710,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set)
729 pm_runtime_get_sync(dev); 710 pm_runtime_get_sync(dev);
730 mutex_lock(&dev_pm_qos_mtx); 711 mutex_lock(&dev_pm_qos_mtx);
731 712
732 if (!dev->power.qos || !dev->power.qos->flags_req) { 713 if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) {
733 ret = -EINVAL; 714 ret = -EINVAL;
734 goto out; 715 goto out;
735 } 716 }
@@ -747,4 +728,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set)
747 pm_runtime_put(dev); 728 pm_runtime_put(dev);
748 return ret; 729 return ret;
749} 730}
731#else /* !CONFIG_PM_RUNTIME */
732static void __dev_pm_qos_hide_latency_limit(struct device *dev) {}
733static void __dev_pm_qos_hide_flags(struct device *dev) {}
750#endif /* CONFIG_PM_RUNTIME */ 734#endif /* CONFIG_PM_RUNTIME */
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 50d16e3cb0a9..a53ebd265701 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -708,6 +708,7 @@ void rpm_sysfs_remove(struct device *dev)
708 708
709void dpm_sysfs_remove(struct device *dev) 709void dpm_sysfs_remove(struct device *dev)
710{ 710{
711 dev_pm_qos_constraints_destroy(dev);
711 rpm_sysfs_remove(dev); 712 rpm_sysfs_remove(dev);
712 sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); 713 sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
713 sysfs_remove_group(&dev->kobj, &pm_attr_group); 714 sysfs_remove_group(&dev->kobj, &pm_attr_group);