Merge branch 'pm-runtime' into pm-for-linus

* pm-runtime:
  PM / Tracing: build rpm-traces.c only if CONFIG_PM_RUNTIME is set
  PM / Runtime: Replace dev_dbg() with trace_rpm_*()
  PM / Runtime: Introduce trace points for tracing rpm_* functions
  PM / Runtime: Don't run callbacks under lock for power.irq_safe set
  USB: Add wakeup info to debugging messages
  PM / Runtime: pm_runtime_idle() can be called in atomic context
  PM / Runtime: Add macro to test for runtime PM events
  PM / Runtime: Add might_sleep() to runtime PM functions
This commit is contained in:
Rafael J. Wysocki 2011-10-07 23:16:55 +02:00
commit d727b60659
20 changed files with 226 additions and 66 deletions

View File

@ -477,12 +477,14 @@ pm_runtime_autosuspend_expiration()
If pm_runtime_irq_safe() has been called for a device then the following helper
functions may also be used in interrupt context:
pm_runtime_idle()
pm_runtime_suspend()
pm_runtime_autosuspend()
pm_runtime_resume()
pm_runtime_get_sync()
pm_runtime_put_sync()
pm_runtime_put_sync_suspend()
pm_runtime_put_sync_autosuspend()
5. Runtime PM Initialization, Device Probing and Removal

View File

@ -439,10 +439,10 @@ cause autosuspends to fail with -EBUSY if the driver needs to use the
device.
External suspend calls should never be allowed to fail in this way,
only autosuspend calls. The driver can tell them apart by checking
the PM_EVENT_AUTO bit in the message.event argument to the suspend
method; this bit will be set for internal PM events (autosuspend) and
clear for external PM events.
only autosuspend calls. The driver can tell them apart by applying
the PMSG_IS_AUTO() macro to the message argument to the suspend
method; it will return True for internal PM events (autosuspend) and
False for external PM events.
Mutual exclusion

View File

@ -9,6 +9,7 @@
#include <linux/sched.h>
#include <linux/pm_runtime.h>
#include <trace/events/rpm.h>
#include "power.h"
static int rpm_resume(struct device *dev, int rpmflags);
@ -154,6 +155,31 @@ static int rpm_check_suspend_allowed(struct device *dev)
return retval;
}
/**
* __rpm_callback - Run a given runtime PM callback for a given device.
* @cb: Runtime PM callback to run.
* @dev: Device to run the callback for.
*/
static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
int retval;
if (dev->power.irq_safe)
spin_unlock(&dev->power.lock);
else
spin_unlock_irq(&dev->power.lock);
retval = cb(dev);
if (dev->power.irq_safe)
spin_lock(&dev->power.lock);
else
spin_lock_irq(&dev->power.lock);
return retval;
}
/**
* rpm_idle - Notify device bus type if the device can be suspended.
* @dev: Device to notify the bus type about.
@ -171,6 +197,7 @@ static int rpm_idle(struct device *dev, int rpmflags)
int (*callback)(struct device *);
int retval;
trace_rpm_idle(dev, rpmflags);
retval = rpm_check_suspend_allowed(dev);
if (retval < 0)
; /* Conditions are wrong. */
@ -225,24 +252,14 @@ static int rpm_idle(struct device *dev, int rpmflags)
else
callback = NULL;
if (callback) {
if (dev->power.irq_safe)
spin_unlock(&dev->power.lock);
else
spin_unlock_irq(&dev->power.lock);
callback(dev);
if (dev->power.irq_safe)
spin_lock(&dev->power.lock);
else
spin_lock_irq(&dev->power.lock);
}
if (callback)
__rpm_callback(callback, dev);
dev->power.idle_notification = false;
wake_up_all(&dev->power.wait_queue);
out:
trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
@ -252,22 +269,14 @@ static int rpm_idle(struct device *dev, int rpmflags)
* @dev: Device to run the callback for.
*/
static int rpm_callback(int (*cb)(struct device *), struct device *dev)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
int retval;
if (!cb)
return -ENOSYS;
if (dev->power.irq_safe) {
retval = cb(dev);
} else {
spin_unlock_irq(&dev->power.lock);
retval = __rpm_callback(cb, dev);
retval = cb(dev);
spin_lock_irq(&dev->power.lock);
}
dev->power.runtime_error = retval;
return retval != -EACCES ? retval : -EIO;
}
@ -295,7 +304,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval;
dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
trace_rpm_suspend(dev, rpmflags);
repeat:
retval = rpm_check_suspend_allowed(dev);
@ -347,6 +356,15 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto out;
}
if (dev->power.irq_safe) {
spin_unlock(&dev->power.lock);
cpu_relax();
spin_lock(&dev->power.lock);
goto repeat;
}
/* Wait for the other suspend running in parallel with us. */
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait,
@ -430,7 +448,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
}
out:
dev_dbg(dev, "%s returns %d\n", __func__, retval);
trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
@ -459,7 +477,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval = 0;
dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
trace_rpm_resume(dev, rpmflags);
repeat:
if (dev->power.runtime_error)
@ -496,6 +514,15 @@ static int rpm_resume(struct device *dev, int rpmflags)
goto out;
}
if (dev->power.irq_safe) {
spin_unlock(&dev->power.lock);
cpu_relax();
spin_lock(&dev->power.lock);
goto repeat;
}
/* Wait for the operation carried out in parallel with us. */
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait,
@ -615,7 +642,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
spin_lock_irq(&dev->power.lock);
}
dev_dbg(dev, "%s returns %d\n", __func__, retval);
trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
@ -732,13 +759,16 @@ EXPORT_SYMBOL_GPL(pm_schedule_suspend);
* return immediately if it is larger than zero. Then carry out an idle
* notification, either synchronous or asynchronous.
*
* This routine may be called in atomic context if the RPM_ASYNC flag is set.
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_idle(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
@ -761,13 +791,16 @@ EXPORT_SYMBOL_GPL(__pm_runtime_idle);
* return immediately if it is larger than zero. Then carry out a suspend,
* either synchronous or asynchronous.
*
* This routine may be called in atomic context if the RPM_ASYNC flag is set.
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_suspend(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count))
return 0;
@ -789,13 +822,16 @@ EXPORT_SYMBOL_GPL(__pm_runtime_suspend);
* If the RPM_GET_PUT flag is set, increment the device's usage count. Then
* carry out a resume, either synchronous or asynchronous.
*
* This routine may be called in atomic context if the RPM_ASYNC flag is set.
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
* or if pm_runtime_irq_safe() has been called.
*/
int __pm_runtime_resume(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
if (rpmflags & RPM_GET_PUT)
atomic_inc(&dev->power.usage_count);

View File

@ -1116,7 +1116,7 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
spin_lock_irq(&data->txlock);
if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
set_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
} else {

View File

@ -2409,7 +2409,7 @@ static int picolcd_raw_event(struct hid_device *hdev,
#ifdef CONFIG_PM
static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
{
if (message.event & PM_EVENT_AUTO)
if (PMSG_IS_AUTO(message))
return 0;
picolcd_suspend_backlight(hid_get_drvdata(hdev));

View File

@ -1332,7 +1332,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
struct usbhid_device *usbhid = hid->driver_data;
int status;
if (message.event & PM_EVENT_AUTO) {
if (PMSG_IS_AUTO(message)) {
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
@ -1367,7 +1367,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
return -EIO;
}
if (!ignoreled && (message.event & PM_EVENT_AUTO)) {
if (!ignoreled && PMSG_IS_AUTO(message)) {
spin_lock_irq(&usbhid->lock);
if (test_bit(HID_LED_ON, &usbhid->iofl)) {
spin_unlock_irq(&usbhid->lock);
@ -1380,8 +1380,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
hid_cancel_delayed_stuff(usbhid);
hid_cease_io(usbhid);
if ((message.event & PM_EVENT_AUTO) &&
test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
/* lost race against keypresses */
status = hid_start_in(hid);
if (status < 0)

View File

@ -1470,7 +1470,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
if (!dev->suspend_count++) {
spin_lock_irq(&dev->txq.lock);
/* don't autosuspend while transmitting */
if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
if (dev->txq.qlen && PMSG_IS_AUTO(message)) {
spin_unlock_irq(&dev->txq.lock);
return -EBUSY;
} else {

View File

@ -599,7 +599,7 @@ void i2400mu_disconnect(struct usb_interface *iface)
*
* As well, the device might refuse going to sleep for whichever
* reason. In this case we just fail. For system suspend/hibernate,
* we *can't* fail. We check PM_EVENT_AUTO to see if the
* we *can't* fail. We check PMSG_IS_AUTO to see if the
* suspend call comes from the USB stack or from the system and act
* in consequence.
*
@ -615,7 +615,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
struct i2400m *i2400m = &i2400mu->i2400m;
#ifdef CONFIG_PM
if (pm_msg.event & PM_EVENT_AUTO)
if (PMSG_IS_AUTO(pm_msg))
is_autosuspend = 1;
#endif

View File

@ -1305,7 +1305,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
struct acm *acm = usb_get_intfdata(intf);
int cnt;
if (message.event & PM_EVENT_AUTO) {
if (PMSG_IS_AUTO(message)) {
int b;
spin_lock_irq(&acm->write_lock);

View File

@ -798,11 +798,11 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
/* if this is an autosuspend the caller does the locking */
if (!(message.event & PM_EVENT_AUTO))
if (!PMSG_IS_AUTO(message))
mutex_lock(&desc->lock);
spin_lock_irq(&desc->iuspin);
if ((message.event & PM_EVENT_AUTO) &&
if (PMSG_IS_AUTO(message) &&
(test_bit(WDM_IN_USE, &desc->flags)
|| test_bit(WDM_RESPONDING, &desc->flags))) {
spin_unlock_irq(&desc->iuspin);
@ -815,7 +815,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
}
if (!(message.event & PM_EVENT_AUTO))
if (!PMSG_IS_AUTO(message))
mutex_unlock(&desc->lock);
return rv;

View File

@ -1046,8 +1046,7 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
/* Non-root devices on a full/low-speed bus must wait for their
* companion high-speed root hub, in case a handoff is needed.
*/
if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
udev->bus->hs_companion)
if (!PMSG_IS_AUTO(msg) && udev->parent && udev->bus->hs_companion)
device_pm_wait_for_dev(&udev->dev,
&udev->bus->hs_companion->root_hub->dev);
@ -1075,7 +1074,7 @@ static int usb_suspend_interface(struct usb_device *udev,
if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status && !(msg.event & PM_EVENT_AUTO))
if (status && !PMSG_IS_AUTO(msg))
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
@ -1189,7 +1188,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
status = usb_suspend_interface(udev, intf, msg);
/* Ignore errors during system sleep transitions */
if (!(msg.event & PM_EVENT_AUTO))
if (!PMSG_IS_AUTO(msg))
status = 0;
if (status != 0)
break;
@ -1199,7 +1198,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
status = usb_suspend_device(udev, msg);
/* Again, ignore errors during system sleep transitions */
if (!(msg.event & PM_EVENT_AUTO))
if (!PMSG_IS_AUTO(msg))
status = 0;
}

View File

@ -1961,8 +1961,9 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
int status;
int old_state = hcd->state;
dev_dbg(&rhdev->dev, "bus %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
dev_dbg(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
rhdev->do_remote_wakeup);
if (HCD_DEAD(hcd)) {
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
return 0;
@ -1997,8 +1998,8 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
int status;
int old_state = hcd->state;
dev_dbg(&rhdev->dev, "usb %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
dev_dbg(&rhdev->dev, "usb %sresume\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""));
if (HCD_DEAD(hcd)) {
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
return 0;

View File

@ -2324,8 +2324,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
int port1 = udev->portnum;
int status;
// dev_dbg(hub->intfdev, "suspend port %d\n", port1);
/* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
*
@ -2342,7 +2340,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
/* bail if autosuspend is requested */
if (msg.event & PM_EVENT_AUTO)
if (PMSG_IS_AUTO(msg))
return status;
}
}
@ -2367,12 +2365,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
USB_CTRL_SET_TIMEOUT);
/* System sleep transitions should never fail */
if (!(msg.event & PM_EVENT_AUTO))
if (!PMSG_IS_AUTO(msg))
status = 0;
} else {
/* device has up to 10 msec to fully suspend */
dev_dbg(&udev->dev, "usb %ssuspend\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
udev->do_remote_wakeup);
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
@ -2523,7 +2522,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
} else {
/* drive resume for at least 20 msec */
dev_dbg(&udev->dev, "usb %sresume\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
(PMSG_IS_AUTO(msg) ? "auto-" : ""));
msleep(25);
/* Virtual root hubs can trigger on GET_PORT_STATUS to
@ -2625,7 +2624,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
udev = hdev->children [port1-1];
if (udev && udev->can_submit) {
dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
if (msg.event & PM_EVENT_AUTO)
if (PMSG_IS_AUTO(msg))
return -EBUSY;
}
}

View File

@ -1009,7 +1009,7 @@ static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
struct sierra_intf_private *intfdata;
int b;
if (message.event & PM_EVENT_AUTO) {
if (PMSG_IS_AUTO(message)) {
intfdata = serial->private;
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;

View File

@ -651,7 +651,7 @@ int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
dbg("%s entered", __func__);
if (message.event & PM_EVENT_AUTO) {
if (PMSG_IS_AUTO(message)) {
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;
spin_unlock_irq(&intfdata->susp_lock);

View File

@ -366,6 +366,8 @@ extern struct dev_pm_ops generic_subsys_pm_ops;
#define PMSG_AUTO_RESUME ((struct pm_message) \
{ .event = PM_EVENT_AUTO_RESUME, })
#define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0)
/**
* Device run-time power management status.
*

View File

@ -0,0 +1,99 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM rpm
#if !defined(_TRACE_RUNTIME_POWER_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_RUNTIME_POWER_H
#include <linux/ktime.h>
#include <linux/tracepoint.h>
#include <linux/device.h>
/*
* The rpm_internal events are used for tracing some important
* runtime pm internal functions.
*/
DECLARE_EVENT_CLASS(rpm_internal,
TP_PROTO(struct device *dev, int flags),
TP_ARGS(dev, flags),
TP_STRUCT__entry(
__string( name, dev_name(dev) )
__field( int, flags )
__field( int , usage_count )
__field( int , disable_depth )
__field( int , runtime_auto )
__field( int , request_pending )
__field( int , irq_safe )
__field( int , child_count )
),
TP_fast_assign(
__assign_str(name, dev_name(dev));
__entry->flags = flags;
__entry->usage_count = atomic_read(
&dev->power.usage_count);
__entry->disable_depth = dev->power.disable_depth;
__entry->runtime_auto = dev->power.runtime_auto;
__entry->request_pending = dev->power.request_pending;
__entry->irq_safe = dev->power.irq_safe;
__entry->child_count = atomic_read(
&dev->power.child_count);
),
TP_printk("%s flags-%x cnt-%-2d dep-%-2d auto-%-1d p-%-1d"
" irq-%-1d child-%d",
__get_str(name), __entry->flags,
__entry->usage_count,
__entry->disable_depth,
__entry->runtime_auto,
__entry->request_pending,
__entry->irq_safe,
__entry->child_count
)
);
DEFINE_EVENT(rpm_internal, rpm_suspend,
TP_PROTO(struct device *dev, int flags),
TP_ARGS(dev, flags)
);
DEFINE_EVENT(rpm_internal, rpm_resume,
TP_PROTO(struct device *dev, int flags),
TP_ARGS(dev, flags)
);
DEFINE_EVENT(rpm_internal, rpm_idle,
TP_PROTO(struct device *dev, int flags),
TP_ARGS(dev, flags)
);
TRACE_EVENT(rpm_return_int,
TP_PROTO(struct device *dev, unsigned long ip, int ret),
TP_ARGS(dev, ip, ret),
TP_STRUCT__entry(
__string( name, dev_name(dev))
__field( unsigned long, ip )
__field( int, ret )
),
TP_fast_assign(
__assign_str(name, dev_name(dev));
__entry->ip = ip;
__entry->ret = ret;
),
TP_printk("%pS:%s ret=%d", (void *)__entry->ip, __get_str(name),
__entry->ret)
);
#endif /* _TRACE_RUNTIME_POWER_H */
/* This part must be outside protection */
#include <trace/define_trace.h>

View File

@ -53,6 +53,9 @@ endif
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
obj-$(CONFIG_TRACEPOINTS) += power-traces.o
ifeq ($(CONFIG_PM_RUNTIME),y)
obj-$(CONFIG_TRACEPOINTS) += rpm-traces.o
endif
ifeq ($(CONFIG_TRACING),y)
obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
endif

20
kernel/trace/rpm-traces.c Normal file
View File

@ -0,0 +1,20 @@
/*
* Power trace points
*
* Copyright (C) 2009 Ming Lei <ming.lei@canonical.com>
*/
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/usb.h>
#define CREATE_TRACE_POINTS
#include <trace/events/rpm.h>
EXPORT_TRACEPOINT_SYMBOL_GPL(rpm_return_int);
EXPORT_TRACEPOINT_SYMBOL_GPL(rpm_idle);
EXPORT_TRACEPOINT_SYMBOL_GPL(rpm_suspend);
EXPORT_TRACEPOINT_SYMBOL_GPL(rpm_resume);

View File

@ -631,7 +631,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
if (chip == (void *)-1L)
return 0;
if (!(message.event & PM_EVENT_AUTO)) {
if (!PMSG_IS_AUTO(message)) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
if (!chip->num_suspended_intf++) {
list_for_each(p, &chip->pcm_list) {