From d031e1de2c5ba91e67ed83f6adf624543ab2b03d Mon Sep 17 00:00:00 2001 From: Alex Frid Date: Sun, 29 Jan 2012 20:39:25 +0100 Subject: PM / QoS: Simplify PM QoS expansion/merge - Replace class ID #define with enumeration - Loop through PM QoS objects during initialization (rather than initializing them one-by-one) Signed-off-by: Alex Frid Reviewed-by: Antti Miettinen Reviewed-by: Diwakar Tundlam Reviewed-by: Scott Williams Reviewed-by: Yu-Huan Hsu Acked-by: markgross Signed-off-by: Rafael J. Wysocki --- include/linux/pm_qos.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux/pm_qos.h') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index e5bbcbaa6f5..5ac91d8e69d 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -9,12 +9,16 @@ #include #include -#define PM_QOS_RESERVED 0 -#define PM_QOS_CPU_DMA_LATENCY 1 -#define PM_QOS_NETWORK_LATENCY 2 -#define PM_QOS_NETWORK_THROUGHPUT 3 +enum { + PM_QOS_RESERVED = 0, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_NETWORK_LATENCY, + PM_QOS_NETWORK_THROUGHPUT, + + /* insert new class ID */ + PM_QOS_NUM_CLASSES, +}; -#define PM_QOS_NUM_CLASSES 4 #define PM_QOS_DEFAULT_VALUE -1 #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) -- cgit v1.2.3-70-g09d2 From a9b542ee607a8afafa9447292394959fc84ea650 Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Mon, 13 Feb 2012 16:23:42 +0100 Subject: PM / QoS: unconditionally build the feature The PM QoS feature originally didn't depend on CONFIG_PM, which was mistakenly changed by commit e8db0be1245de16a6cc6365506abc392c3c212d4 PM QoS: Move and rename the implementation files Later, commit d020283dc694c9ec31b410f522252f7a8397e67d PM / QoS: CPU C-state breakage with PM Qos change partially fixed that by introducing a static inline definition of pm_qos_request(), but that still didn't allow user space to use the PM QoS interface if CONFIG_PM was unset (which had been possible before). For this reason, remove the dependency of PM QoS on CONFIG_PM to make it work (as intended) with CONFIG_PM unset. [rjw: Replaced the original changelog with a new one.] Signed-off-by: Jean Pihet Reported-by: Venkatesh Pallipadi Signed-off-by: Rafael J. Wysocki --- include/linux/pm_qos.h | 41 +---------------------------------------- kernel/power/Makefile | 3 ++- 2 files changed, 3 insertions(+), 41 deletions(-) (limited to 'include/linux/pm_qos.h') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 67c521731f4..c8a541e1338 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -67,7 +67,6 @@ static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) return req->dev != 0; } -#ifdef CONFIG_PM int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, enum pm_qos_req_action action, int value); void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, @@ -82,6 +81,7 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); +#ifdef CONFIG_PM s32 __dev_pm_qos_read_value(struct device *dev); s32 dev_pm_qos_read_value(struct device *dev); int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, @@ -99,45 +99,6 @@ void dev_pm_qos_constraints_destroy(struct device *dev); int dev_pm_qos_add_ancestor_request(struct device *dev, struct dev_pm_qos_request *req, s32 value); #else -static inline int pm_qos_update_target(struct pm_qos_constraints *c, - struct plist_node *node, - enum pm_qos_req_action action, - int value) - { return 0; } -static inline void pm_qos_add_request(struct pm_qos_request *req, - int pm_qos_class, s32 value) - { return; } -static inline void pm_qos_update_request(struct pm_qos_request *req, - s32 new_value) - { return; } -static inline void pm_qos_remove_request(struct pm_qos_request *req) - { return; } - -static inline int pm_qos_request(int pm_qos_class) -{ - switch (pm_qos_class) { - case PM_QOS_CPU_DMA_LATENCY: - return PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - case PM_QOS_NETWORK_LATENCY: - return PM_QOS_NETWORK_LAT_DEFAULT_VALUE; - case PM_QOS_NETWORK_THROUGHPUT: - return PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE; - default: - return PM_QOS_DEFAULT_VALUE; - } -} - -static inline int pm_qos_add_notifier(int pm_qos_class, - struct notifier_block *notifier) - { return 0; } -static inline int pm_qos_remove_notifier(int pm_qos_class, - struct notifier_block *notifier) - { return 0; } -static inline int pm_qos_request_active(struct pm_qos_request *req) - { return 0; } -static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) - { return 0; } - static inline s32 __dev_pm_qos_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_read_value(struct device *dev) diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 07e0e28ffba..66d808ec525 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,7 +1,8 @@ ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG -obj-$(CONFIG_PM) += main.o qos.o +obj-y += qos.o +obj-$(CONFIG_PM) += main.o obj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o -- cgit v1.2.3-70-g09d2 From 85dc0b8a4019e38ad4fd0c008f89a5c241805ac2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 13 Mar 2012 01:01:39 +0100 Subject: 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 Reviewed-by: Kevin Hilman Reviewed-by: Mark Brown Acked-by: Linus Walleij --- Documentation/ABI/testing/sysfs-devices-power | 18 ++++++++ drivers/base/power/power.h | 4 ++ drivers/base/power/qos.c | 61 +++++++++++++++++++++++++++ drivers/base/power/sysfs.c | 47 +++++++++++++++++++++ include/linux/pm.h | 1 + include/linux/pm_qos.h | 9 ++++ 6 files changed, 140 insertions(+) (limited to 'include/linux/pm_qos.h') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 8ffbc25376a..840f7d64d48 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -165,3 +165,21 @@ Description: Not all drivers support this attribute. If it isn't supported, attempts to read or write it will yield I/O errors. + +What: /sys/devices/.../power/pm_qos_latency_us +Date: March 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power/pm_qos_resume_latency_us attribute + contains the PM QoS resume latency limit for the given device, + which is the maximum allowed time it can take to resume the + device, after it has been suspended at run time, from a resume + request to the moment the device will be ready to process I/O, + in microseconds. If it is equal to 0, however, this means that + the PM QoS resume latency may be arbitrary. + + Not all drivers support this attribute. If it isn't supported, + it is not present. + + This attribute has no effect on system-wide suspend/resume and + hibernation. diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 9bf62323aaf..eeb4bff9505 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); extern void rpm_sysfs_remove(struct device *dev); extern int wakeup_sysfs_add(struct device *dev); extern void wakeup_sysfs_remove(struct device *dev); +extern int pm_qos_sysfs_add(struct device *dev); +extern void pm_qos_sysfs_remove(struct device *dev); #else /* CONFIG_PM */ @@ -79,5 +81,7 @@ static inline void dpm_sysfs_remove(struct device *dev) {} static inline void rpm_sysfs_remove(struct device *dev) {} static inline int wakeup_sysfs_add(struct device *dev) { return 0; } static inline void wakeup_sysfs_remove(struct device *dev) {} +static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } +static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index c5d35883746..71855570922 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -41,6 +41,7 @@ #include #include +#include "power.h" static DEFINE_MUTEX(dev_pm_qos_mtx); @@ -166,6 +167,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev) struct dev_pm_qos_request *req, *tmp; struct pm_qos_constraints *c; + /* + * If the device's PM QoS resume latency limit has been exposed to user + * space, it has to be hidden at this point. + */ + dev_pm_qos_hide_latency_limit(dev); + mutex_lock(&dev_pm_qos_mtx); dev->power.power_state = PMSG_INVALID; @@ -445,3 +452,57 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, return error; } EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); + +#ifdef CONFIG_PM_RUNTIME +static void __dev_pm_qos_drop_user_request(struct device *dev) +{ + dev_pm_qos_remove_request(dev->power.pq_req); + dev->power.pq_req = 0; +} + +/** + * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space. + * @dev: Device whose PM QoS latency limit is to be exposed to user space. + * @value: Initial value of the latency limit. + */ +int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) +{ + struct dev_pm_qos_request *req; + int ret; + + if (!device_is_registered(dev) || value < 0) + return -EINVAL; + + if (dev->power.pq_req) + return -EEXIST; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + ret = dev_pm_qos_add_request(dev, req, value); + if (ret < 0) + return ret; + + dev->power.pq_req = req; + ret = pm_qos_sysfs_add(dev); + if (ret) + __dev_pm_qos_drop_user_request(dev); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); + +/** + * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. + * @dev: Device whose PM QoS latency limit is to be hidden from user space. + */ +void dev_pm_qos_hide_latency_limit(struct device *dev) +{ + if (dev->power.pq_req) { + pm_qos_sysfs_remove(dev); + __dev_pm_qos_drop_user_request(dev); + } +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); +#endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index adf41be0ea6..95c12f6cb5b 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,31 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev, static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, autosuspend_delay_ms_store); +static ssize_t pm_qos_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); +} + +static ssize_t pm_qos_latency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + s32 value; + int ret; + + if (kstrtos32(buf, 0, &value)) + return -EINVAL; + + if (value < 0) + return -EINVAL; + + ret = dev_pm_qos_update_request(dev->power.pq_req, value); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, + pm_qos_latency_show, pm_qos_latency_store); #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -490,6 +516,17 @@ static struct attribute_group pm_runtime_attr_group = { .attrs = runtime_attrs, }; +static struct attribute *pm_qos_attrs[] = { +#ifdef CONFIG_PM_RUNTIME + &dev_attr_pm_qos_resume_latency_us.attr, +#endif /* CONFIG_PM_RUNTIME */ + NULL, +}; +static struct attribute_group pm_qos_attr_group = { + .name = power_group_name, + .attrs = pm_qos_attrs, +}; + int dpm_sysfs_add(struct device *dev) { int rc; @@ -530,6 +567,16 @@ void wakeup_sysfs_remove(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); } +int pm_qos_sysfs_add(struct device *dev) +{ + return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); +} + +void pm_qos_sysfs_remove(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); +} + void rpm_sysfs_remove(struct device *dev) { sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); diff --git a/include/linux/pm.h b/include/linux/pm.h index 73c610573a7..4db39ed1a6e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -537,6 +537,7 @@ struct dev_pm_info { unsigned long accounting_timestamp; ktime_t suspend_time; s64 max_time_suspended_ns; + struct dev_pm_qos_request *pq_req; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct pm_qos_constraints *constraints; diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index c8a541e1338..2e9191a712f 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, { return 0; } #endif +#ifdef CONFIG_PM_RUNTIME +int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value); +void dev_pm_qos_hide_latency_limit(struct device *dev); +#else +static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) + { return 0; } +static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {} +#endif + #endif -- cgit v1.2.3-70-g09d2 From c4772d192c70b61d52262b0db76f7abd8aeb51c6 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 28 Mar 2012 23:31:24 +0200 Subject: PM / QoS: add pm_qos_update_request_timeout() API The new API, pm_qos_update_request_timeout() is to provide a timeout with pm_qos_update_request. For example, pm_qos_update_request_timeout(req, 100, 1000), means that QoS request on req with value 100 will be active for 1000 microseconds. After 1000 microseconds, the QoS request thru req is reset. If there were another pm_qos_update_request(req, x) during the 1000 us, this new request with value x will override as this is another request on the same req handle. A new request on the same req handle will always override the previous request whether it is the conventional request or it is the new timeout request. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Acked-by: Mark Gross Signed-off-by: Rafael J. Wysocki --- include/linux/pm_qos.h | 4 ++++ kernel/power/qos.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'include/linux/pm_qos.h') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 2e9191a712f..233149cb19f 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -8,6 +8,7 @@ #include #include #include +#include enum { PM_QOS_RESERVED = 0, @@ -29,6 +30,7 @@ enum { struct pm_qos_request { struct plist_node node; int pm_qos_class; + struct delayed_work work; /* for pm_qos_update_request_timeout */ }; struct dev_pm_qos_request { @@ -73,6 +75,8 @@ void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); +void pm_qos_update_request_timeout(struct pm_qos_request *req, + s32 new_value, unsigned long timeout_us); void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index d6d6dbd1ecc..6a031e68402 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -229,6 +229,21 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); +/** + * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout + * @work: work struct for the delayed work (timeout) + * + * This cancels the timeout request by falling back to the default at timeout. + */ +static void pm_qos_work_fn(struct work_struct *work) +{ + struct pm_qos_request *req = container_of(to_delayed_work(work), + struct pm_qos_request, + work); + + pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); +} + /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle @@ -253,6 +268,7 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } req->pm_qos_class = pm_qos_class; + INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, &req->node, PM_QOS_ADD_REQ, value); } @@ -279,6 +295,9 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + if (new_value != req->node.prio) pm_qos_update_target( pm_qos_array[req->pm_qos_class]->constraints, @@ -286,6 +305,34 @@ void pm_qos_update_request(struct pm_qos_request *req, } EXPORT_SYMBOL_GPL(pm_qos_update_request); +/** + * pm_qos_update_request_timeout - modifies an existing qos request temporarily. + * @req : handle to list element holding a pm_qos request to use + * @new_value: defines the temporal qos request + * @timeout_us: the effective duration of this qos request in usecs. + * + * After timeout_us, this qos request is cancelled automatically. + */ +void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, + unsigned long timeout_us) +{ + if (!req) + return; + if (WARN(!pm_qos_request_active(req), + "%s called for unknown object.", __func__)) + return; + + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + + if (new_value != req->node.prio) + pm_qos_update_target( + pm_qos_array[req->pm_qos_class]->constraints, + &req->node, PM_QOS_UPDATE_REQ, new_value); + + schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us)); +} + /** * pm_qos_remove_request - modifies an existing qos request * @req: handle to request list element @@ -305,6 +352,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } + if (delayed_work_pending(&req->work)) + cancel_delayed_work_sync(&req->work); + pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, &req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); -- cgit v1.2.3-70-g09d2