PM / runtime: Optimize the use of device links
If the device has no links to suppliers that should be used for runtime PM (links with DEVICE_LINK_PM_RUNTIME set), there is no reason to walk the list of suppliers for that device during runtime suspend and resume. Add a simple mechanism to detect that case and possibly avoid the extra unnecessary overhead. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
21d5c57b37
commit
baa8809f60
|
@ -205,14 +205,17 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
if (!link)
|
||||
goto out;
|
||||
|
||||
if ((flags & DL_FLAG_PM_RUNTIME) && (flags & DL_FLAG_RPM_ACTIVE)) {
|
||||
if (pm_runtime_get_sync(supplier) < 0) {
|
||||
pm_runtime_put_noidle(supplier);
|
||||
kfree(link);
|
||||
link = NULL;
|
||||
goto out;
|
||||
if (flags & DL_FLAG_PM_RUNTIME) {
|
||||
if (flags & DL_FLAG_RPM_ACTIVE) {
|
||||
if (pm_runtime_get_sync(supplier) < 0) {
|
||||
pm_runtime_put_noidle(supplier);
|
||||
kfree(link);
|
||||
link = NULL;
|
||||
goto out;
|
||||
}
|
||||
link->rpm_active = true;
|
||||
}
|
||||
link->rpm_active = true;
|
||||
pm_runtime_new_link(consumer);
|
||||
}
|
||||
get_device(supplier);
|
||||
link->supplier = supplier;
|
||||
|
@ -296,6 +299,9 @@ static void __device_link_del(struct device_link *link)
|
|||
dev_info(link->consumer, "Dropping the link to %s\n",
|
||||
dev_name(link->supplier));
|
||||
|
||||
if (link->flags & DL_FLAG_PM_RUNTIME)
|
||||
pm_runtime_drop_link(link->consumer);
|
||||
|
||||
list_del_rcu(&link->s_node);
|
||||
list_del_rcu(&link->c_node);
|
||||
call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu);
|
||||
|
|
|
@ -305,6 +305,7 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
|
|||
__releases(&dev->power.lock) __acquires(&dev->power.lock)
|
||||
{
|
||||
int retval, idx;
|
||||
bool use_links = dev->power.links_count > 0;
|
||||
|
||||
if (dev->power.irq_safe) {
|
||||
spin_unlock(&dev->power.lock);
|
||||
|
@ -318,7 +319,7 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
|
|||
* routine returns, so it is safe to read the status outside of
|
||||
* the lock.
|
||||
*/
|
||||
if (dev->power.runtime_status == RPM_RESUMING) {
|
||||
if (use_links && dev->power.runtime_status == RPM_RESUMING) {
|
||||
idx = device_links_read_lock();
|
||||
|
||||
retval = rpm_get_suppliers(dev);
|
||||
|
@ -341,8 +342,9 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
|
|||
*
|
||||
* Do that if resume fails too.
|
||||
*/
|
||||
if ((dev->power.runtime_status == RPM_SUSPENDING && !retval)
|
||||
|| (dev->power.runtime_status == RPM_RESUMING && retval)) {
|
||||
if (use_links
|
||||
&& ((dev->power.runtime_status == RPM_SUSPENDING && !retval)
|
||||
|| (dev->power.runtime_status == RPM_RESUMING && retval))) {
|
||||
idx = device_links_read_lock();
|
||||
|
||||
fail:
|
||||
|
@ -1593,6 +1595,21 @@ void pm_runtime_put_suppliers(struct device *dev)
|
|||
device_links_read_unlock(idx);
|
||||
}
|
||||
|
||||
void pm_runtime_new_link(struct device *dev)
|
||||
{
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.links_count++;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
}
|
||||
|
||||
void pm_runtime_drop_link(struct device *dev)
|
||||
{
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
WARN_ON(dev->power.links_count == 0);
|
||||
dev->power.links_count--;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_force_suspend - Force a device into suspend state if needed.
|
||||
* @dev: Device to suspend.
|
||||
|
|
|
@ -597,6 +597,7 @@ struct dev_pm_info {
|
|||
unsigned int use_autosuspend:1;
|
||||
unsigned int timer_autosuspends:1;
|
||||
unsigned int memalloc_noio:1;
|
||||
unsigned int links_count;
|
||||
enum rpm_request request;
|
||||
enum rpm_status runtime_status;
|
||||
int runtime_error;
|
||||
|
|
|
@ -58,6 +58,8 @@ extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable);
|
|||
extern void pm_runtime_clean_up_links(struct device *dev);
|
||||
extern void pm_runtime_get_suppliers(struct device *dev);
|
||||
extern void pm_runtime_put_suppliers(struct device *dev);
|
||||
extern void pm_runtime_new_link(struct device *dev);
|
||||
extern void pm_runtime_drop_link(struct device *dev);
|
||||
|
||||
static inline void pm_suspend_ignore_children(struct device *dev, bool enable)
|
||||
{
|
||||
|
@ -192,6 +194,8 @@ static inline void pm_runtime_set_memalloc_noio(struct device *dev,
|
|||
static inline void pm_runtime_clean_up_links(struct device *dev) {}
|
||||
static inline void pm_runtime_get_suppliers(struct device *dev) {}
|
||||
static inline void pm_runtime_put_suppliers(struct device *dev) {}
|
||||
static inline void pm_runtime_new_link(struct device *dev) {}
|
||||
static inline void pm_runtime_drop_link(struct device *dev) {}
|
||||
|
||||
#endif /* !CONFIG_PM */
|
||||
|
||||
|
|
Loading…
Reference in a new issue