aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-03-12 20:01:39 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-03-13 17:37:14 -0400
commit85dc0b8a4019e38ad4fd0c008f89a5c241805ac2 (patch)
treea73c5864d8ce4a6845e616ade7b8107e181279b6
parenta9b542ee607a8afafa9447292394959fc84ea650 (diff)
PM / QoS: Make it possible to expose PM QoS latency constraints
A runtime suspend of a device (e.g. an MMC controller) belonging to a power domain or, in a more complicated scenario, a runtime suspend of another device in the same power domain, may cause power to be removed from the entire domain. In that case, the amount of time necessary to runtime-resume the given device (e.g. the MMC controller) is often substantially greater than the time needed to run its driver's runtime resume callback. That may hurt performance in some situations, because user data may need to wait for the device to become operational, so we should make it possible to prevent that from happening. For this reason, introduce a new sysfs attribute for devices, power/pm_qos_resume_latency_us, allowing user space to specify the upper bound of the time necessary to bring the (runtime-suspended) device up after the resume of it has been requested. However, make that attribute appear only for the devices whose drivers declare support for it by calling the (new) dev_pm_qos_expose_latency_limit() helper function with the appropriate initial value of the attribute. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Reviewed-by: Kevin Hilman <khilman@ti.com> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--Documentation/ABI/testing/sysfs-devices-power18
-rw-r--r--drivers/base/power/power.h4
-rw-r--r--drivers/base/power/qos.c61
-rw-r--r--drivers/base/power/sysfs.c47
-rw-r--r--include/linux/pm.h1
-rw-r--r--include/linux/pm_qos.h9
6 files changed, 140 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
index 8ffbc25376a0..840f7d64d483 100644
--- a/Documentation/ABI/testing/sysfs-devices-power
+++ b/Documentation/ABI/testing/sysfs-devices-power
@@ -165,3 +165,21 @@ Description:
165 165
166 Not all drivers support this attribute. If it isn't supported, 166 Not all drivers support this attribute. If it isn't supported,
167 attempts to read or write it will yield I/O errors. 167 attempts to read or write it will yield I/O errors.
168
169What: /sys/devices/.../power/pm_qos_latency_us
170Date: March 2012
171Contact: Rafael J. Wysocki <rjw@sisk.pl>
172Description:
173 The /sys/devices/.../power/pm_qos_resume_latency_us attribute
174 contains the PM QoS resume latency limit for the given device,
175 which is the maximum allowed time it can take to resume the
176 device, after it has been suspended at run time, from a resume
177 request to the moment the device will be ready to process I/O,
178 in microseconds. If it is equal to 0, however, this means that
179 the PM QoS resume latency may be arbitrary.
180
181 Not all drivers support this attribute. If it isn't supported,
182 it is not present.
183
184 This attribute has no effect on system-wide suspend/resume and
185 hibernation.
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 9bf62323aaf3..eeb4bff9505c 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -71,6 +71,8 @@ extern void dpm_sysfs_remove(struct device *dev);
71extern void rpm_sysfs_remove(struct device *dev); 71extern void rpm_sysfs_remove(struct device *dev);
72extern int wakeup_sysfs_add(struct device *dev); 72extern int wakeup_sysfs_add(struct device *dev);
73extern void wakeup_sysfs_remove(struct device *dev); 73extern void wakeup_sysfs_remove(struct device *dev);
74extern int pm_qos_sysfs_add(struct device *dev);
75extern void pm_qos_sysfs_remove(struct device *dev);
74 76
75#else /* CONFIG_PM */ 77#else /* CONFIG_PM */
76 78
@@ -79,5 +81,7 @@ static inline void dpm_sysfs_remove(struct device *dev) {}
79static inline void rpm_sysfs_remove(struct device *dev) {} 81static inline void rpm_sysfs_remove(struct device *dev) {}
80static inline int wakeup_sysfs_add(struct device *dev) { return 0; } 82static inline int wakeup_sysfs_add(struct device *dev) { return 0; }
81static inline void wakeup_sysfs_remove(struct device *dev) {} 83static inline void wakeup_sysfs_remove(struct device *dev) {}
84static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
85static inline void pm_qos_sysfs_remove(struct device *dev) {}
82 86
83#endif 87#endif
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index c5d358837461..71855570922d 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 43
44#include "power.h"
44 45
45static DEFINE_MUTEX(dev_pm_qos_mtx); 46static DEFINE_MUTEX(dev_pm_qos_mtx);
46 47
@@ -166,6 +167,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
166 struct dev_pm_qos_request *req, *tmp; 167 struct dev_pm_qos_request *req, *tmp;
167 struct pm_qos_constraints *c; 168 struct pm_qos_constraints *c;
168 169
170 /*
171 * If the device's PM QoS resume latency limit has been exposed to user
172 * space, it has to be hidden at this point.
173 */
174 dev_pm_qos_hide_latency_limit(dev);
175
169 mutex_lock(&dev_pm_qos_mtx); 176 mutex_lock(&dev_pm_qos_mtx);
170 177
171 dev->power.power_state = PMSG_INVALID; 178 dev->power.power_state = PMSG_INVALID;
@@ -445,3 +452,57 @@ int dev_pm_qos_add_ancestor_request(struct device *dev,
445 return error; 452 return error;
446} 453}
447EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); 454EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request);
455
456#ifdef CONFIG_PM_RUNTIME
457static void __dev_pm_qos_drop_user_request(struct device *dev)
458{
459 dev_pm_qos_remove_request(dev->power.pq_req);
460 dev->power.pq_req = 0;
461}
462
463/**
464 * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space.
465 * @dev: Device whose PM QoS latency limit is to be exposed to user space.
466 * @value: Initial value of the latency limit.
467 */
468int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
469{
470 struct dev_pm_qos_request *req;
471 int ret;
472
473 if (!device_is_registered(dev) || value < 0)
474 return -EINVAL;
475
476 if (dev->power.pq_req)
477 return -EEXIST;
478
479 req = kzalloc(sizeof(*req), GFP_KERNEL);
480 if (!req)
481 return -ENOMEM;
482
483 ret = dev_pm_qos_add_request(dev, req, value);
484 if (ret < 0)
485 return ret;
486
487 dev->power.pq_req = req;
488 ret = pm_qos_sysfs_add(dev);
489 if (ret)
490 __dev_pm_qos_drop_user_request(dev);
491
492 return ret;
493}
494EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);
495
496/**
497 * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space.
498 * @dev: Device whose PM QoS latency limit is to be hidden from user space.
499 */
500void dev_pm_qos_hide_latency_limit(struct device *dev)
501{
502 if (dev->power.pq_req) {
503 pm_qos_sysfs_remove(dev);
504 __dev_pm_qos_drop_user_request(dev);
505 }
506}
507EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit);
508#endif /* CONFIG_PM_RUNTIME */
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index adf41be0ea66..95c12f6cb5b9 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -5,6 +5,7 @@
5#include <linux/device.h> 5#include <linux/device.h>
6#include <linux/string.h> 6#include <linux/string.h>
7#include <linux/export.h> 7#include <linux/export.h>
8#include <linux/pm_qos.h>
8#include <linux/pm_runtime.h> 9#include <linux/pm_runtime.h>
9#include <linux/atomic.h> 10#include <linux/atomic.h>
10#include <linux/jiffies.h> 11#include <linux/jiffies.h>
@@ -217,6 +218,31 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
217static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, 218static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
218 autosuspend_delay_ms_store); 219 autosuspend_delay_ms_store);
219 220
221static ssize_t pm_qos_latency_show(struct device *dev,
222 struct device_attribute *attr, char *buf)
223{
224 return sprintf(buf, "%d\n", dev->power.pq_req->node.prio);
225}
226
227static ssize_t pm_qos_latency_store(struct device *dev,
228 struct device_attribute *attr,
229 const char *buf, size_t n)
230{
231 s32 value;
232 int ret;
233
234 if (kstrtos32(buf, 0, &value))
235 return -EINVAL;
236
237 if (value < 0)
238 return -EINVAL;
239
240 ret = dev_pm_qos_update_request(dev->power.pq_req, value);
241 return ret < 0 ? ret : n;
242}
243
244static DEVICE_ATTR(pm_qos_resume_latency_us, 0644,
245 pm_qos_latency_show, pm_qos_latency_store);
220#endif /* CONFIG_PM_RUNTIME */ 246#endif /* CONFIG_PM_RUNTIME */
221 247
222#ifdef CONFIG_PM_SLEEP 248#ifdef CONFIG_PM_SLEEP
@@ -490,6 +516,17 @@ static struct attribute_group pm_runtime_attr_group = {
490 .attrs = runtime_attrs, 516 .attrs = runtime_attrs,
491}; 517};
492 518
519static struct attribute *pm_qos_attrs[] = {
520#ifdef CONFIG_PM_RUNTIME
521 &dev_attr_pm_qos_resume_latency_us.attr,
522#endif /* CONFIG_PM_RUNTIME */
523 NULL,
524};
525static struct attribute_group pm_qos_attr_group = {
526 .name = power_group_name,
527 .attrs = pm_qos_attrs,
528};
529
493int dpm_sysfs_add(struct device *dev) 530int dpm_sysfs_add(struct device *dev)
494{ 531{
495 int rc; 532 int rc;
@@ -530,6 +567,16 @@ void wakeup_sysfs_remove(struct device *dev)
530 sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); 567 sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
531} 568}
532 569
570int pm_qos_sysfs_add(struct device *dev)
571{
572 return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group);
573}
574
575void pm_qos_sysfs_remove(struct device *dev)
576{
577 sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group);
578}
579
533void rpm_sysfs_remove(struct device *dev) 580void rpm_sysfs_remove(struct device *dev)
534{ 581{
535 sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); 582 sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 73c610573a74..4db39ed1a6ef 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -537,6 +537,7 @@ struct dev_pm_info {
537 unsigned long accounting_timestamp; 537 unsigned long accounting_timestamp;
538 ktime_t suspend_time; 538 ktime_t suspend_time;
539 s64 max_time_suspended_ns; 539 s64 max_time_suspended_ns;
540 struct dev_pm_qos_request *pq_req;
540#endif 541#endif
541 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ 542 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
542 struct pm_qos_constraints *constraints; 543 struct pm_qos_constraints *constraints;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index c8a541e13380..2e9191a712f3 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -137,4 +137,13 @@ static inline int dev_pm_qos_add_ancestor_request(struct device *dev,
137 { return 0; } 137 { return 0; }
138#endif 138#endif
139 139
140#ifdef CONFIG_PM_RUNTIME
141int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value);
142void dev_pm_qos_hide_latency_limit(struct device *dev);
143#else
144static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
145 { return 0; }
146static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {}
147#endif
148
140#endif 149#endif