From f3e3ccd232458b637d7b65394bcecd81e021cd3e Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 5 Oct 2015 16:45:31 +0200 Subject: [PATCH] FROMLIST: PM / sleep: Go direct_complete if driver has no callbacks If a suitable prepare callback cannot be found for a given device and its driver has no PM callbacks at all, assume that it can go direct to complete when the system goes to sleep. The reason for this is that there's lots of devices in a system that do no PM at all and there's no reason for them to prevent their ancestors to do direct_complete if they can support it. BUG=chrome-os-partner:46491 TEST=suspend_stress_test on glados Signed-off-by: Tomeu Vizoso (am from https://patchwork.kernel.org/patch/7328171/) Signed-off-by: Derek Basehore Change-Id: Ic3d6cd732f6c64ef34f28e065454f690eed4e6f8 Reviewed-on: https://chromium-review.googlesource.com/305815 Commit-Ready: Derek Basehore Tested-by: Derek Basehore Reviewed-by: Eric Caruso --- drivers/base/dd.c | 3 +++ drivers/base/power/common.c | 3 +++ drivers/base/power/domain.c | 2 ++ drivers/base/power/main.c | 35 +++++++++++++++++++++++++++++++++++ drivers/base/power/power.h | 3 +++ include/linux/pm.h | 1 + 6 files changed, 47 insertions(+) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d25ddcbbfbef5..c1797b1b33705 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -219,6 +219,8 @@ static void driver_bound(struct device *dev) klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + device_pm_check_callbacks(dev); + /* * Make sure the device is no longer in one of the deferred lists and * kick off retrying all pending devices @@ -657,6 +659,7 @@ static void __device_release_driver(struct device *dev) dev->driver = NULL; dev_set_drvdata(dev, NULL); klist_remove(&dev->p->knode_driver); + device_pm_check_callbacks(dev); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_UNBOUND_DRIVER, diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index e939e99e1ed95..1e1c890800da7 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -14,6 +14,8 @@ #include #include +#include "power.h" + /** * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device. * @dev: Device to handle. @@ -153,5 +155,6 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) WARN(device_is_bound(dev), "PM domains can only be changed for unbound devices\n"); dev->pm_domain = pd; + device_pm_check_callbacks(dev); } EXPORT_SYMBOL_GPL(dev_pm_domain_set); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5f7306731e73c..5aeeb9de743a4 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -18,6 +18,8 @@ #include #include +#include "power.h" + #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ ({ \ type (*__routine)(struct device *__d); \ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index bed94f1c9ba23..2dea912476991 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -126,6 +126,7 @@ void device_pm_add(struct device *dev) { pr_debug("PM: Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); + device_pm_check_callbacks(dev); mutex_lock(&dpm_list_mtx); if (dev->parent && dev->parent->power.is_prepared) dev_warn(dev, "parent %s should not be sleeping\n", @@ -148,6 +149,7 @@ void device_pm_remove(struct device *dev) mutex_unlock(&dpm_list_mtx); device_wakeup_disable(dev); pm_runtime_remove(dev); + device_pm_check_callbacks(dev); } /** @@ -1559,6 +1561,11 @@ static int device_prepare(struct device *dev, pm_message_t state) dev->power.wakeup_path = device_may_wakeup(dev); + if (dev->power.no_pm_callbacks) { + ret = 1; /* Let device go direct_complete */ + goto unlock; + } + if (dev->pm_domain) { info = "preparing power domain "; callback = dev->pm_domain->ops.prepare; @@ -1584,6 +1591,7 @@ static int device_prepare(struct device *dev, pm_message_t state) trace_device_pm_callback_end(dev, ret); } +unlock: device_unlock(dev); if (ret < 0) { @@ -1710,3 +1718,30 @@ void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) device_pm_unlock(); } EXPORT_SYMBOL_GPL(dpm_for_each_dev); + +static bool pm_ops_is_empty(const struct dev_pm_ops *ops) +{ + if (!ops) + return true; + + return !ops->prepare && + !ops->suspend && + !ops->suspend_late && + !ops->suspend_noirq && + !ops->resume_noirq && + !ops->resume_early && + !ops->resume && + !ops->complete; +} + +void device_pm_check_callbacks(struct device *dev) +{ + spin_lock_irq(&dev->power.lock); + dev->power.no_pm_callbacks = + (!dev->bus || pm_ops_is_empty(dev->bus->pm)) && + (!dev->class || pm_ops_is_empty(dev->class->pm)) && + (!dev->type || pm_ops_is_empty(dev->type->pm)) && + (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) && + (!dev->driver || pm_ops_is_empty(dev->driver->pm)); + spin_unlock_irq(&dev->power.lock); +} diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index d13744bff5644..84a29ac139e20 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -51,6 +51,7 @@ extern void device_pm_remove(struct device *); extern void device_pm_move_before(struct device *, struct device *); extern void device_pm_move_after(struct device *, struct device *); extern void device_pm_move_last(struct device *); +extern void device_pm_check_callbacks(struct device *dev); #else /* !CONFIG_PM_SLEEP */ @@ -69,6 +70,8 @@ static inline void device_pm_move_after(struct device *deva, struct device *devb) {} static inline void device_pm_move_last(struct device *dev) {} +static inline void device_pm_check_callbacks(struct device *dev) {} + #endif /* !CONFIG_PM_SLEEP */ static inline void device_pm_init(struct device *dev) diff --git a/include/linux/pm.h b/include/linux/pm.h index 42f948df73a9a..09d1a800a3148 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -628,6 +628,7 @@ struct dev_pm_info { enum wakeup_type wakeup_source_type; bool wakeup_path:1; bool syscore:1; + bool no_pm_callbacks:1; /* Owned by the PM core */ bool use_dark_resume:1; #else unsigned int should_wakeup:1;