PM / Runtime: Add synchronous runtime interface for interrupt handlers (v3)
This patch (as1431c) makes the synchronous runtime-PM interface suitable for use in interrupt handlers. Subsystems can call the new pm_runtime_irq_safe() function to tell the PM core that a device's runtime_suspend and runtime_resume callbacks should be invoked with interrupts disabled and the spinlock held. This permits the pm_runtime_get_sync() and the new pm_runtime_put_sync_suspend() routines to be called from within interrupt handlers. When a device is declared irq-safe in this way, the PM core increments the parent's usage count, so the parent will never be runtime suspended. This prevents difficult situations in which an irq-safe device can't resume because it is forced to wait for its non-irq-safe parent. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
This commit is contained in:

committed by
Rafael J. Wysocki

parent
5262a47502
commit
c7b61de5b7
@@ -250,13 +250,16 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
|
||||
if (!cb)
|
||||
return -ENOSYS;
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
if (dev->power.irq_safe) {
|
||||
retval = cb(dev);
|
||||
} else {
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
retval = cb(dev);
|
||||
retval = cb(dev);
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
}
|
||||
dev->power.runtime_error = retval;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -404,7 +407,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (parent && !parent->power.ignore_children) {
|
||||
if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
pm_request_idle(parent);
|
||||
@@ -527,10 +530,13 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||
|
||||
if (!parent && dev->parent) {
|
||||
/*
|
||||
* Increment the parent's resume counter and resume it if
|
||||
* necessary.
|
||||
* Increment the parent's usage counter and resume it if
|
||||
* necessary. Not needed if dev is irq-safe; then the
|
||||
* parent is permanently resumed.
|
||||
*/
|
||||
parent = dev->parent;
|
||||
if (dev->power.irq_safe)
|
||||
goto skip_parent;
|
||||
spin_unlock(&dev->power.lock);
|
||||
|
||||
pm_runtime_get_noresume(parent);
|
||||
@@ -553,6 +559,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||
goto out;
|
||||
goto repeat;
|
||||
}
|
||||
skip_parent:
|
||||
|
||||
if (dev->power.no_callbacks)
|
||||
goto no_callback; /* Assume success. */
|
||||
@@ -584,7 +591,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||
rpm_idle(dev, RPM_ASYNC);
|
||||
|
||||
out:
|
||||
if (parent) {
|
||||
if (parent && !dev->power.irq_safe) {
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
pm_runtime_put(parent);
|
||||
@@ -1065,7 +1072,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow);
|
||||
* Set the power.no_callbacks flag, which tells the PM core that this
|
||||
* device is power-managed through its parent and has no run-time PM
|
||||
* callbacks of its own. The run-time sysfs attributes will be removed.
|
||||
*
|
||||
*/
|
||||
void pm_runtime_no_callbacks(struct device *dev)
|
||||
{
|
||||
@@ -1077,6 +1083,27 @@ void pm_runtime_no_callbacks(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
|
||||
|
||||
/**
|
||||
* pm_runtime_irq_safe - Leave interrupts disabled during callbacks.
|
||||
* @dev: Device to handle
|
||||
*
|
||||
* Set the power.irq_safe flag, which tells the PM core that the
|
||||
* ->runtime_suspend() and ->runtime_resume() callbacks for this device should
|
||||
* always be invoked with the spinlock held and interrupts disabled. It also
|
||||
* causes the parent's usage counter to be permanently incremented, preventing
|
||||
* the parent from runtime suspending -- otherwise an irq-safe child might have
|
||||
* to wait for a non-irq-safe parent.
|
||||
*/
|
||||
void pm_runtime_irq_safe(struct device *dev)
|
||||
{
|
||||
if (dev->parent)
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.irq_safe = 1;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_runtime_irq_safe);
|
||||
|
||||
/**
|
||||
* update_autosuspend - Handle a change to a device's autosuspend settings.
|
||||
* @dev: Device to handle.
|
||||
@@ -1199,4 +1226,6 @@ void pm_runtime_remove(struct device *dev)
|
||||
/* Change the status back to 'suspended' to match the initial status. */
|
||||
if (dev->power.runtime_status == RPM_ACTIVE)
|
||||
pm_runtime_set_suspended(dev);
|
||||
if (dev->power.irq_safe && dev->parent)
|
||||
pm_runtime_put_sync(dev->parent);
|
||||
}
|
||||
|
Reference in New Issue
Block a user