* [RFC][PATCH 0/2] PM: Generic PM domains and device PM QoS (v2) @ 2011-10-21 23:09 ` Rafael J. Wysocki 0 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:09 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm Hi, This patchset is a refresh of patches introducing simple PM QoS governor functions for PM domains. The patches still haven't been tested properly, although they should build. The purpose of them is mainly to illustrate how PM QoS may be used with PM domains to control device runtime PM. Thanks, Rafael ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC][PATCH 0/2] PM: Generic PM domains and device PM QoS (v2) @ 2011-10-21 23:09 ` Rafael J. Wysocki 0 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:09 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm Hi, This patchset is a refresh of patches introducing simple PM QoS governor functions for PM domains. The patches still haven't been tested properly, although they should build. The purpose of them is mainly to illustrate how PM QoS may be used with PM domains to control device runtime PM. Thanks, Rafael ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC][PATCH 1/2] PM / Domains: Add device stop governor function (v2) 2011-10-21 23:09 ` Rafael J. Wysocki @ 2011-10-21 23:10 ` Rafael J. Wysocki -1 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:10 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm From: Rafael J. Wysocki <rjw@sisk.pl> Add a function deciding whether or not devices should be stopped in pm_genpd_runtime_suspend() depending on their PM QoS values. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> --- arch/arm/mach-shmobile/pm-sh7372.c | 4 ++- drivers/base/power/Makefile | 2 - drivers/base/power/domain.c | 29 ++++++++++++++++++---- drivers/base/power/domain_governor.c | 40 +++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 45 +++++++++++++++++++++++++++++++---- 5 files changed, 109 insertions(+), 11 deletions(-) Index: linux/include/linux/pm_domain.h =================================--- linux.orig/include/linux/pm_domain.h +++ linux/include/linux/pm_domain.h @@ -21,6 +21,7 @@ enum gpd_status { struct dev_power_governor { bool (*power_down_ok)(struct dev_pm_domain *domain); + bool (*stop_ok)(struct device *dev); }; struct generic_pm_domain { @@ -62,8 +63,13 @@ struct gpd_link { struct list_head slave_node; }; +struct gpd_gov_dev_data { + s64 break_even_ns; +}; + struct generic_pm_domain_data { struct pm_domain_data base; + struct gpd_gov_dev_data *gov_data; bool need_restore; }; @@ -73,8 +79,19 @@ static inline struct generic_pm_domain_d } #ifdef CONFIG_PM_GENERIC_DOMAINS -extern int pm_genpd_add_device(struct generic_pm_domain *genpd, - struct device *dev); +extern struct dev_power_governor simple_qos_governor; + +extern struct generic_pm_domain *dev_to_genpd(struct device *dev); +extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data); + +static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev) +{ + return __pm_genpd_add_device(genpd, dev, NULL); +} + extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, @@ -83,8 +100,23 @@ extern int pm_genpd_remove_subdomain(str struct generic_pm_domain *target); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); + extern int pm_genpd_poweron(struct generic_pm_domain *genpd); + +extern bool default_stop_ok(struct device *dev); + #else + +static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) +{ + return ERR_PTR(-ENOSYS); +} +static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data) +{ + return -ENOSYS; +} static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { @@ -105,12 +137,17 @@ static inline int pm_genpd_remove_subdom { return -ENOSYS; } -static inline void pm_genpd_init(struct generic_pm_domain *genpd, - struct dev_power_governor *gov, bool is_off) {} +static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) +{ +} static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) { return -ENOSYS; } +static inline bool default_stop_ok(struct device *dev) +{ + return false; +} #endif #ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME Index: linux/drivers/base/power/domain.c =================================--- linux.orig/drivers/base/power/domain.c +++ linux/drivers/base/power/domain.c @@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock); #ifdef CONFIG_PM -static struct generic_pm_domain *dev_to_genpd(struct device *dev) +struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); @@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru } /** + * genpd_stop_dev - Stop a given device if that's beneficial. + * @genpd: PM domain the device belongs to. + * @dev: Device to stop. + */ +static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + bool (*stop_ok)(struct device *dev); + + stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; + if (stop_ok && !stop_ok(dev)) + return -EBUSY; + + return genpd->stop_device(dev); +} + +/** * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. * @dev: Device to suspend. * @@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru might_sleep_if(!genpd->dev_irq_safe); if (genpd->stop_device) { - int ret = genpd->stop_device(dev); + int ret = genpd_stop_dev(genpd, dev); if (ret) return ret; } @@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc mutex_lock(&genpd->lock); } finish_wait(&genpd->status_wait_queue, &wait); - __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); + __pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd); genpd->resume_count--; genpd_set_active(genpd); wake_up_all(&genpd->status_wait_queue); @@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev #endif /* CONFIG_PM_SLEEP */ /** - * pm_genpd_add_device - Add a device to an I/O PM domain. + * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. * @dev: Device to be added. + * @gov_data: Set of PM QoS parameters to attach to the device. */ -int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) +int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, + struct gpd_gov_dev_data *gov_data) { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; @@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p gpd_data->base.dev = dev; gpd_data->need_restore = false; list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->gov_data = gov_data; out: genpd_release_lock(genpd); Index: linux/drivers/base/power/Makefile =================================--- linux.orig/drivers/base/power/Makefile +++ linux/drivers/base/power/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup. obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o -obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG Index: linux/drivers/base/power/domain_governor.c =================================--- /dev/null +++ linux/drivers/base/power/domain_governor.c @@ -0,0 +1,40 @@ +/* + * drivers/base/power/domain_governor.c - Governors for device PM domains. + * + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. + * + * This file is released under the GPLv2. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/pm_qos.h> + +bool default_stop_ok(struct device *dev) +{ + struct gpd_gov_dev_data *gov_data; + s64 constraint_ns; + s32 constraint; + + dev_dbg(dev, "%s()\n", __func__); + + gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data; + if (!gov_data) + return true; + + constraint = dev_pm_qos_read_value(dev); + if (constraint < 0) + return false; + else if (constraint = 0) /* 0 means "don't care" */ + return true; + + constraint_ns = constraint; + constraint_ns *= NSEC_PER_USEC; + + return constraint_ns > gov_data->break_even_ns; +} + +struct dev_power_governor simple_qos_governor = { + .stop_ok = default_stop_ok, +}; Index: linux/arch/arm/mach-shmobile/pm-sh7372.c =================================--- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c +++ linux/arch/arm/mach-shmobile/pm-sh7372.c @@ -161,13 +161,15 @@ static bool sh7372_power_down_forbidden( struct dev_power_governor sh7372_always_on_gov = { .power_down_ok = sh7372_power_down_forbidden, + .stop_ok = default_stop_ok, }; void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) { struct generic_pm_domain *genpd = &sh7372_pd->genpd; + struct dev_power_governor *gov = sh7372_pd->gov; - pm_genpd_init(genpd, sh7372_pd->gov, false); + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); genpd->stop_device = pm_clk_suspend; genpd->start_device = pm_clk_resume; genpd->dev_irq_safe = true; ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC][PATCH 1/2] PM / Domains: Add device stop governor function (v2) @ 2011-10-21 23:10 ` Rafael J. Wysocki 0 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:10 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm From: Rafael J. Wysocki <rjw@sisk.pl> Add a function deciding whether or not devices should be stopped in pm_genpd_runtime_suspend() depending on their PM QoS values. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> --- arch/arm/mach-shmobile/pm-sh7372.c | 4 ++- drivers/base/power/Makefile | 2 - drivers/base/power/domain.c | 29 ++++++++++++++++++---- drivers/base/power/domain_governor.c | 40 +++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 45 +++++++++++++++++++++++++++++++---- 5 files changed, 109 insertions(+), 11 deletions(-) Index: linux/include/linux/pm_domain.h =================================================================== --- linux.orig/include/linux/pm_domain.h +++ linux/include/linux/pm_domain.h @@ -21,6 +21,7 @@ enum gpd_status { struct dev_power_governor { bool (*power_down_ok)(struct dev_pm_domain *domain); + bool (*stop_ok)(struct device *dev); }; struct generic_pm_domain { @@ -62,8 +63,13 @@ struct gpd_link { struct list_head slave_node; }; +struct gpd_gov_dev_data { + s64 break_even_ns; +}; + struct generic_pm_domain_data { struct pm_domain_data base; + struct gpd_gov_dev_data *gov_data; bool need_restore; }; @@ -73,8 +79,19 @@ static inline struct generic_pm_domain_d } #ifdef CONFIG_PM_GENERIC_DOMAINS -extern int pm_genpd_add_device(struct generic_pm_domain *genpd, - struct device *dev); +extern struct dev_power_governor simple_qos_governor; + +extern struct generic_pm_domain *dev_to_genpd(struct device *dev); +extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data); + +static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev) +{ + return __pm_genpd_add_device(genpd, dev, NULL); +} + extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, @@ -83,8 +100,23 @@ extern int pm_genpd_remove_subdomain(str struct generic_pm_domain *target); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); + extern int pm_genpd_poweron(struct generic_pm_domain *genpd); + +extern bool default_stop_ok(struct device *dev); + #else + +static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) +{ + return ERR_PTR(-ENOSYS); +} +static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data) +{ + return -ENOSYS; +} static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { @@ -105,12 +137,17 @@ static inline int pm_genpd_remove_subdom { return -ENOSYS; } -static inline void pm_genpd_init(struct generic_pm_domain *genpd, - struct dev_power_governor *gov, bool is_off) {} +static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) +{ +} static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) { return -ENOSYS; } +static inline bool default_stop_ok(struct device *dev) +{ + return false; +} #endif #ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME Index: linux/drivers/base/power/domain.c =================================================================== --- linux.orig/drivers/base/power/domain.c +++ linux/drivers/base/power/domain.c @@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock); #ifdef CONFIG_PM -static struct generic_pm_domain *dev_to_genpd(struct device *dev) +struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); @@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru } /** + * genpd_stop_dev - Stop a given device if that's beneficial. + * @genpd: PM domain the device belongs to. + * @dev: Device to stop. + */ +static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + bool (*stop_ok)(struct device *dev); + + stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; + if (stop_ok && !stop_ok(dev)) + return -EBUSY; + + return genpd->stop_device(dev); +} + +/** * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. * @dev: Device to suspend. * @@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru might_sleep_if(!genpd->dev_irq_safe); if (genpd->stop_device) { - int ret = genpd->stop_device(dev); + int ret = genpd_stop_dev(genpd, dev); if (ret) return ret; } @@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc mutex_lock(&genpd->lock); } finish_wait(&genpd->status_wait_queue, &wait); - __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); + __pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd); genpd->resume_count--; genpd_set_active(genpd); wake_up_all(&genpd->status_wait_queue); @@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev #endif /* CONFIG_PM_SLEEP */ /** - * pm_genpd_add_device - Add a device to an I/O PM domain. + * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. * @dev: Device to be added. + * @gov_data: Set of PM QoS parameters to attach to the device. */ -int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) +int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, + struct gpd_gov_dev_data *gov_data) { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; @@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p gpd_data->base.dev = dev; gpd_data->need_restore = false; list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->gov_data = gov_data; out: genpd_release_lock(genpd); Index: linux/drivers/base/power/Makefile =================================================================== --- linux.orig/drivers/base/power/Makefile +++ linux/drivers/base/power/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup. obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o -obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG Index: linux/drivers/base/power/domain_governor.c =================================================================== --- /dev/null +++ linux/drivers/base/power/domain_governor.c @@ -0,0 +1,40 @@ +/* + * drivers/base/power/domain_governor.c - Governors for device PM domains. + * + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. + * + * This file is released under the GPLv2. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/pm_qos.h> + +bool default_stop_ok(struct device *dev) +{ + struct gpd_gov_dev_data *gov_data; + s64 constraint_ns; + s32 constraint; + + dev_dbg(dev, "%s()\n", __func__); + + gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data; + if (!gov_data) + return true; + + constraint = dev_pm_qos_read_value(dev); + if (constraint < 0) + return false; + else if (constraint == 0) /* 0 means "don't care" */ + return true; + + constraint_ns = constraint; + constraint_ns *= NSEC_PER_USEC; + + return constraint_ns > gov_data->break_even_ns; +} + +struct dev_power_governor simple_qos_governor = { + .stop_ok = default_stop_ok, +}; Index: linux/arch/arm/mach-shmobile/pm-sh7372.c =================================================================== --- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c +++ linux/arch/arm/mach-shmobile/pm-sh7372.c @@ -161,13 +161,15 @@ static bool sh7372_power_down_forbidden( struct dev_power_governor sh7372_always_on_gov = { .power_down_ok = sh7372_power_down_forbidden, + .stop_ok = default_stop_ok, }; void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) { struct generic_pm_domain *genpd = &sh7372_pd->genpd; + struct dev_power_governor *gov = sh7372_pd->gov; - pm_genpd_init(genpd, sh7372_pd->gov, false); + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); genpd->stop_device = pm_clk_suspend; genpd->start_device = pm_clk_resume; genpd->dev_irq_safe = true; ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC][PATCH 2/2] PM / Domains: Add default power off governor function (v2) 2011-10-21 23:09 ` Rafael J. Wysocki @ 2011-10-21 23:11 ` Rafael J. Wysocki -1 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:11 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm From: Rafael J. Wysocki <rjw@sisk.pl> Add a function deciding whether or not a given PM domain should be powered off on the basis of that domain's devices' PM QoS constraints. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> --- drivers/base/power/domain_governor.c | 96 +++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 7 ++ 2 files changed, 103 insertions(+) Index: linux/include/linux/pm_domain.h =================================--- linux.orig/include/linux/pm_domain.h +++ linux/include/linux/pm_domain.h @@ -49,6 +49,10 @@ struct generic_pm_domain { int (*start_device)(struct device *dev); int (*stop_device)(struct device *dev); bool (*active_wakeup)(struct device *dev); + ktime_t power_off_latency; + ktime_t power_on_latency; + s64 break_even_ns; + s64 min_delta_ns; }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) @@ -64,6 +68,9 @@ struct gpd_link { }; struct gpd_gov_dev_data { + ktime_t start_latency; + ktime_t suspend_latency; + ktime_t resume_latency; s64 break_even_ns; }; Index: linux/drivers/base/power/domain_governor.c =================================--- linux.orig/drivers/base/power/domain_governor.c +++ linux/drivers/base/power/domain_governor.c @@ -35,6 +35,102 @@ bool default_stop_ok(struct device *dev) return constraint_ns > gov_data->break_even_ns; } +/* This routine must be executed under the PM domain's lock. */ +static bool default_power_down_ok(struct dev_pm_domain *pd) +{ + struct generic_pm_domain *genpd = pd_to_genpd(pd); + struct gpd_link *link; + struct pm_domain_data *pdd; + ktime_t off_time, on_time; + s64 delta_ns, min_delta_ns; + + on_time = genpd->power_on_latency; + /* Check if slave domains can be off for enough time. */ + delta_ns = ktime_to_ns(ktime_add(genpd->power_off_latency, on_time)); + min_delta_ns = 0; + /* All slave domains have been powered off at this point. */ + list_for_each_entry(link, &genpd->master_links, master_node) { + if (delta_ns > link->slave->min_delta_ns) + return false; + + delta_ns = link->slave->min_delta_ns - delta_ns; + if (delta_ns < min_delta_ns) + min_delta_ns = delta_ns; + } + + genpd->min_delta_ns = min_delta_ns; + + /* Compute the total time needed to power off the domain. */ + off_time = ktime_set(0, 0); + /* All devices have been stopped at this point. */ + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + struct gpd_gov_dev_data *gov_data; + + if (!pdd->dev->driver) + continue; + + gov_data = to_gpd_data(pdd)->gov_data; + if (!gov_data) + continue; + + off_time = ktime_add(off_time, gov_data->suspend_latency); + } + off_time = ktime_add(off_time, genpd->power_off_latency); + + /* + * For each device in the domain compute the difference between the + * QoS value and the total time required to bring the device back + * assuming that the domain will be powered off and compute the minimum + * of those. + */ + min_delta_ns = 0; + on_time = ktime_add(on_time, off_time); + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + struct gpd_gov_dev_data *gov_data; + struct device *dev = pdd->dev; + ktime_t dev_up_time; + s32 constraint; + s64 constraint_ns; + + if (!dev->driver) + continue; + + gov_data = to_gpd_data(pdd)->gov_data; + if (gov_data) { + dev_up_time = ktime_add(on_time, + gov_data->resume_latency); + dev_up_time = ktime_add(dev_up_time, + gov_data->start_latency); + } else { + dev_up_time = on_time; + } + + constraint = dev_pm_qos_read_value(dev); + if (constraint < 0) + return false; + else if (constraint = 0) /* 0 means "don't care" */ + continue; + + constraint_ns = constraint; + constraint_ns *= NSEC_PER_USEC; + delta_ns = constraint_ns - ktime_to_ns(dev_up_time); + if (min_delta_ns > delta_ns) + min_delta_ns = delta_ns; + } + + /* Compare the computed delta with the break even value. */ + if (min_delta_ns < genpd->break_even_ns) + return false; + + /* Store the computed value for the masters to use. */ + if (genpd->min_delta_ns > min_delta_ns) + genpd->min_delta_ns = min_delta_ns; + + /* The domain can be powered off. */ + return true; +} + struct dev_power_governor simple_qos_governor = { .stop_ok = default_stop_ok, + .power_down_ok = default_power_down_ok, }; ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC][PATCH 2/2] PM / Domains: Add default power off governor function (v2) @ 2011-10-21 23:11 ` Rafael J. Wysocki 0 siblings, 0 replies; 6+ messages in thread From: Rafael J. Wysocki @ 2011-10-21 23:11 UTC (permalink / raw) To: Linux PM list; +Cc: LKML, Linux-sh list, Jean Pihet, Magnus Damm From: Rafael J. Wysocki <rjw@sisk.pl> Add a function deciding whether or not a given PM domain should be powered off on the basis of that domain's devices' PM QoS constraints. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> --- drivers/base/power/domain_governor.c | 96 +++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 7 ++ 2 files changed, 103 insertions(+) Index: linux/include/linux/pm_domain.h =================================================================== --- linux.orig/include/linux/pm_domain.h +++ linux/include/linux/pm_domain.h @@ -49,6 +49,10 @@ struct generic_pm_domain { int (*start_device)(struct device *dev); int (*stop_device)(struct device *dev); bool (*active_wakeup)(struct device *dev); + ktime_t power_off_latency; + ktime_t power_on_latency; + s64 break_even_ns; + s64 min_delta_ns; }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) @@ -64,6 +68,9 @@ struct gpd_link { }; struct gpd_gov_dev_data { + ktime_t start_latency; + ktime_t suspend_latency; + ktime_t resume_latency; s64 break_even_ns; }; Index: linux/drivers/base/power/domain_governor.c =================================================================== --- linux.orig/drivers/base/power/domain_governor.c +++ linux/drivers/base/power/domain_governor.c @@ -35,6 +35,102 @@ bool default_stop_ok(struct device *dev) return constraint_ns > gov_data->break_even_ns; } +/* This routine must be executed under the PM domain's lock. */ +static bool default_power_down_ok(struct dev_pm_domain *pd) +{ + struct generic_pm_domain *genpd = pd_to_genpd(pd); + struct gpd_link *link; + struct pm_domain_data *pdd; + ktime_t off_time, on_time; + s64 delta_ns, min_delta_ns; + + on_time = genpd->power_on_latency; + /* Check if slave domains can be off for enough time. */ + delta_ns = ktime_to_ns(ktime_add(genpd->power_off_latency, on_time)); + min_delta_ns = 0; + /* All slave domains have been powered off at this point. */ + list_for_each_entry(link, &genpd->master_links, master_node) { + if (delta_ns > link->slave->min_delta_ns) + return false; + + delta_ns = link->slave->min_delta_ns - delta_ns; + if (delta_ns < min_delta_ns) + min_delta_ns = delta_ns; + } + + genpd->min_delta_ns = min_delta_ns; + + /* Compute the total time needed to power off the domain. */ + off_time = ktime_set(0, 0); + /* All devices have been stopped at this point. */ + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + struct gpd_gov_dev_data *gov_data; + + if (!pdd->dev->driver) + continue; + + gov_data = to_gpd_data(pdd)->gov_data; + if (!gov_data) + continue; + + off_time = ktime_add(off_time, gov_data->suspend_latency); + } + off_time = ktime_add(off_time, genpd->power_off_latency); + + /* + * For each device in the domain compute the difference between the + * QoS value and the total time required to bring the device back + * assuming that the domain will be powered off and compute the minimum + * of those. + */ + min_delta_ns = 0; + on_time = ktime_add(on_time, off_time); + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + struct gpd_gov_dev_data *gov_data; + struct device *dev = pdd->dev; + ktime_t dev_up_time; + s32 constraint; + s64 constraint_ns; + + if (!dev->driver) + continue; + + gov_data = to_gpd_data(pdd)->gov_data; + if (gov_data) { + dev_up_time = ktime_add(on_time, + gov_data->resume_latency); + dev_up_time = ktime_add(dev_up_time, + gov_data->start_latency); + } else { + dev_up_time = on_time; + } + + constraint = dev_pm_qos_read_value(dev); + if (constraint < 0) + return false; + else if (constraint == 0) /* 0 means "don't care" */ + continue; + + constraint_ns = constraint; + constraint_ns *= NSEC_PER_USEC; + delta_ns = constraint_ns - ktime_to_ns(dev_up_time); + if (min_delta_ns > delta_ns) + min_delta_ns = delta_ns; + } + + /* Compare the computed delta with the break even value. */ + if (min_delta_ns < genpd->break_even_ns) + return false; + + /* Store the computed value for the masters to use. */ + if (genpd->min_delta_ns > min_delta_ns) + genpd->min_delta_ns = min_delta_ns; + + /* The domain can be powered off. */ + return true; +} + struct dev_power_governor simple_qos_governor = { .stop_ok = default_stop_ok, + .power_down_ok = default_power_down_ok, }; ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2011-10-21 23:11 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-10-21 23:09 [RFC][PATCH 0/2] PM: Generic PM domains and device PM QoS (v2) Rafael J. Wysocki 2011-10-21 23:09 ` Rafael J. Wysocki 2011-10-21 23:10 ` [RFC][PATCH 1/2] PM / Domains: Add device stop governor function (v2) Rafael J. Wysocki 2011-10-21 23:10 ` Rafael J. Wysocki 2011-10-21 23:11 ` [RFC][PATCH 2/2] PM / Domains: Add default power off " Rafael J. Wysocki 2011-10-21 23:11 ` Rafael J. Wysocki
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.