Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 27 Feb 2010 01:22:53 +0000 (17:22 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 27 Feb 2010 01:22:53 +0000 (17:22 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
  PM / Hibernate: Fix preallocating of memory
  PM / Hibernate: Remove swsusp.c finally
  PM / Hibernate: Remove trailing space in message
  PM: Allow SCSI devices to suspend/resume asynchronously
  PM: Allow USB devices to suspend/resume asynchronously
  USB: implement non-tree resume ordering constraints for PCI host controllers
  PM: Allow PCI devices to suspend/resume asynchronously
  PM / Hibernate: Swap, remove useless check from swsusp_read()
  PM / Hibernate: Really deprecate deprecated user ioctls
  PM: Allow device drivers to use dpm_wait()
  PM: Start asynchronous resume threads upfront
  PM: Add facility for advanced testing of async suspend/resume
  PM: Add a switch for disabling/enabling asynchronous suspend/resume
  PM: Asynchronous suspend and resume of devices
  PM: Add parent information to timing messages
  PM: Document device power attributes in sysfs
  PM / Runtime: Add sysfs switch for disabling device run-time PM

28 files changed:
Documentation/ABI/testing/sysfs-devices-power [new file with mode: 0644]
Documentation/ABI/testing/sysfs-power
Documentation/feature-removal-schedule.txt
drivers/base/power/main.c
drivers/base/power/power.h
drivers/base/power/runtime.c
drivers/base/power/sysfs.c
drivers/pci/pci.c
drivers/pci/pcie/portdrv_core.c
drivers/pci/probe.c
drivers/scsi/hosts.c
drivers/scsi/scsi_sysfs.c
drivers/usb/core/driver.c
drivers/usb/core/endpoint.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
include/linux/device.h
include/linux/pm.h
include/linux/pm_runtime.h
include/linux/resume-trace.h
include/linux/usb.h
kernel/power/Kconfig
kernel/power/main.c
kernel/power/snapshot.c
kernel/power/swap.c
kernel/power/swsusp.c [deleted file]
kernel/power/user.c

diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
new file mode 100644 (file)
index 0000000..6123c52
--- /dev/null
@@ -0,0 +1,79 @@
+What:          /sys/devices/.../power/
+Date:          January 2009
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/devices/.../power directory contains attributes
+               allowing the user space to check and modify some power
+               management related properties of given device.
+
+What:          /sys/devices/.../power/wakeup
+Date:          January 2009
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/devices/.../power/wakeup attribute allows the user
+               space to check if the device is enabled to wake up the system
+               from sleep states, such as the memory sleep state (suspend to
+               RAM) and hibernation (suspend to disk), and to enable or disable
+               it to do that as desired.
+
+               Some devices support "wakeup" events, which are hardware signals
+               used to activate the system from a sleep state.  Such devices
+               have one of the following two values for the sysfs power/wakeup
+               file:
+
+               + "enabled\n" to issue the events;
+               + "disabled\n" not to do so;
+
+               In that cases the user space can change the setting represented
+               by the contents of this file by writing either "enabled", or
+               "disabled" to it.
+
+               For the devices that are not capable of generating system wakeup
+               events this file contains "\n".  In that cases the user space
+               cannot modify the contents of this file and the device cannot be
+               enabled to wake up the system.
+
+What:          /sys/devices/.../power/control
+Date:          January 2009
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/devices/.../power/control attribute allows the user
+               space to control the run-time power management of the device.
+
+               All devices have one of the following two values for the
+               power/control file:
+
+               + "auto\n" to allow the device to be power managed at run time;
+               + "on\n" to prevent the device from being power managed;
+
+               The default for all devices is "auto", which means that they may
+               be subject to automatic power management, depending on their
+               drivers.  Changing this attribute to "on" prevents the driver
+               from power managing the device at run time.  Doing that while
+               the device is suspended causes it to be woken up.
+
+What:          /sys/devices/.../power/async
+Date:          January 2009
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/devices/.../async attribute allows the user space to
+               enable or diasble the device's suspend and resume callbacks to
+               be executed asynchronously (ie. in separate threads, in parallel
+               with the main suspend/resume thread) during system-wide power
+               transitions (eg. suspend to RAM, hibernation).
+
+               All devices have one of the following two values for the
+               power/async file:
+
+               + "enabled\n" to permit the asynchronous suspend/resume;
+               + "disabled\n" to forbid it;
+
+               The value of this attribute may be changed by writing either
+               "enabled", or "disabled" to it.
+
+               It generally is unsafe to permit the asynchronous suspend/resume
+               of a device unless it is certain that all of the PM dependencies
+               of the device are known to the PM core.  However, for some
+               devices this attribute is set to "enabled" by bus type code or
+               device drivers and in that cases it should be safe to leave the
+               default value.
index dcff4d0623add0e7708c642d0cfe210b0c1f48ab..d6a801f45b484e4df433ff09e85a6f3bc07707d5 100644 (file)
@@ -101,3 +101,16 @@ Description:
 
                CAUTION: Using it will cause your machine's real-time (CMOS)
                clock to be set to a random invalid time after a resume.
+
+What:          /sys/power/pm_async
+Date:          January 2009
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/power/pm_async file controls the switch allowing the
+               user space to enable or disable asynchronous suspend and resume
+               of devices.  If enabled, this feature will cause some device
+               drivers' suspend and resume callbacks to be executed in parallel
+               with each other and with the main suspend thread.  It is enabled
+               if this file contains "1", which is the default.  It may be
+               disabled by writing "0" to this file, in which case all devices
+               will be suspended and resumed synchronously.
index 0a46833c1b764dfc4e983edfb55030e938e40948..b9eba900e0f0ae055043d5acf77ca4da65085aea 100644 (file)
@@ -64,6 +64,17 @@ Who: Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com>
 
 ---------------------------
 
+What:  Deprecated snapshot ioctls
+When:  2.6.36
+
+Why:   The ioctls in kernel/power/user.c were marked as deprecated long time
+       ago. Now they notify users about that so that they need to replace
+       their userspace. After some more time, remove them completely.
+
+Who:   Jiri Slaby <jirislaby@gmail.com>
+
+---------------------------
+
 What:  The ieee80211_regdom module parameter
 When:  March 2010 / desktop catchup
 
index a5142bddef412e079c3083dec37ffeb82c163cd5..0e26a6f6fd48193e5c5fd3cec6a20269a4a5ddc9 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/resume-trace.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
+#include <linux/async.h>
 
 #include "../base.h"
 #include "power.h"
@@ -42,6 +43,7 @@
 LIST_HEAD(dpm_list);
 
 static DEFINE_MUTEX(dpm_list_mtx);
+static pm_message_t pm_transition;
 
 /*
  * Set once the preparation of devices for a PM transition has started, reset
@@ -56,6 +58,7 @@ static bool transition_started;
 void device_pm_init(struct device *dev)
 {
        dev->power.status = DPM_ON;
+       init_completion(&dev->power.completion);
        pm_runtime_init(dev);
 }
 
@@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
        pr_debug("PM: Removing info for %s:%s\n",
                 dev->bus ? dev->bus->name : "No Bus",
                 kobject_name(&dev->kobj));
+       complete_all(&dev->power.completion);
        mutex_lock(&dpm_list_mtx);
        list_del_init(&dev->power.entry);
        mutex_unlock(&dpm_list_mtx);
@@ -187,6 +191,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
        }
 }
 
+/**
+ * dpm_wait - Wait for a PM operation to complete.
+ * @dev: Device to wait for.
+ * @async: If unset, wait only if the device's power.async_suspend flag is set.
+ */
+static void dpm_wait(struct device *dev, bool async)
+{
+       if (!dev)
+               return;
+
+       if (async || (pm_async_enabled && dev->power.async_suspend))
+               wait_for_completion(&dev->power.completion);
+}
+
+static int dpm_wait_fn(struct device *dev, void *async_ptr)
+{
+       dpm_wait(dev, *((bool *)async_ptr));
+       return 0;
+}
+
+static void dpm_wait_for_children(struct device *dev, bool async)
+{
+       device_for_each_child(dev, &async, dpm_wait_fn);
+}
+
 /**
  * pm_op - Execute the PM operation appropriate for given PM event.
  * @dev: Device to handle.
@@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev,
        ktime_t calltime, delta, rettime;
 
        if (initcall_debug) {
-               pr_info("calling  %s_i+ @ %i\n",
-                               dev_name(dev), task_pid_nr(current));
+               pr_info("calling  %s+ @ %i, parent: %s\n",
+                               dev_name(dev), task_pid_nr(current),
+                               dev->parent ? dev_name(dev->parent) : "none");
                calltime = ktime_get();
        }
 
@@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
  * device_resume - Execute "resume" callbacks for given device.
  * @dev: Device to handle.
  * @state: PM transition of the system being carried out.
+ * @async: If true, the device is being resumed asynchronously.
  */
-static int device_resume(struct device *dev, pm_message_t state)
+static int device_resume(struct device *dev, pm_message_t state, bool async)
 {
        int error = 0;
 
        TRACE_DEVICE(dev);
        TRACE_RESUME(0);
 
+       dpm_wait(dev->parent, async);
        down(&dev->sem);
 
+       dev->power.status = DPM_RESUMING;
+
        if (dev->bus) {
                if (dev->bus->pm) {
                        pm_dev_dbg(dev, state, "");
@@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state)
        }
  End:
        up(&dev->sem);
+       complete_all(&dev->power.completion);
 
        TRACE_RESUME(error);
        return error;
 }
 
+static void async_resume(void *data, async_cookie_t cookie)
+{
+       struct device *dev = (struct device *)data;
+       int error;
+
+       error = device_resume(dev, pm_transition, true);
+       if (error)
+               pm_dev_err(dev, pm_transition, " async", error);
+       put_device(dev);
+}
+
+static bool is_async(struct device *dev)
+{
+       return dev->power.async_suspend && pm_async_enabled
+               && !pm_trace_is_enabled();
+}
+
 /**
  * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
  * @state: PM transition of the system being carried out.
@@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state)
 static void dpm_resume(pm_message_t state)
 {
        struct list_head list;
+       struct device *dev;
        ktime_t starttime = ktime_get();
 
        INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       while (!list_empty(&dpm_list)) {
-               struct device *dev = to_device(dpm_list.next);
+       pm_transition = state;
+
+       list_for_each_entry(dev, &dpm_list, power.entry) {
+               if (dev->power.status < DPM_OFF)
+                       continue;
+
+               INIT_COMPLETION(dev->power.completion);
+               if (is_async(dev)) {
+                       get_device(dev);
+                       async_schedule(async_resume, dev);
+               }
+       }
 
+       while (!list_empty(&dpm_list)) {
+               dev = to_device(dpm_list.next);
                get_device(dev);
-               if (dev->power.status >= DPM_OFF) {
+               if (dev->power.status >= DPM_OFF && !is_async(dev)) {
                        int error;
 
-                       dev->power.status = DPM_RESUMING;
                        mutex_unlock(&dpm_list_mtx);
 
-                       error = device_resume(dev, state);
+                       error = device_resume(dev, state, false);
 
                        mutex_lock(&dpm_list_mtx);
                        if (error)
@@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state)
        }
        list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
+       async_synchronize_full();
        dpm_show_time(starttime, state, NULL);
 }
 
@@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
        return error;
 }
 
+static int async_error;
+
 /**
  * device_suspend - Execute "suspend" callbacks for given device.
  * @dev: Device to handle.
  * @state: PM transition of the system being carried out.
+ * @async: If true, the device is being suspended asynchronously.
  */
-static int device_suspend(struct device *dev, pm_message_t state)
+static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 {
        int error = 0;
 
+       dpm_wait_for_children(dev, async);
        down(&dev->sem);
 
+       if (async_error)
+               goto End;
+
        if (dev->class) {
                if (dev->class->pm) {
                        pm_dev_dbg(dev, state, "class ");
@@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
                        error = legacy_suspend(dev, state, dev->bus->suspend);
                }
        }
+
+       if (!error)
+               dev->power.status = DPM_OFF;
+
  End:
        up(&dev->sem);
+       complete_all(&dev->power.completion);
 
        return error;
 }
 
+static void async_suspend(void *data, async_cookie_t cookie)
+{
+       struct device *dev = (struct device *)data;
+       int error;
+
+       error = __device_suspend(dev, pm_transition, true);
+       if (error) {
+               pm_dev_err(dev, pm_transition, " async", error);
+               async_error = error;
+       }
+
+       put_device(dev);
+}
+
+static int device_suspend(struct device *dev)
+{
+       INIT_COMPLETION(dev->power.completion);
+
+       if (pm_async_enabled && dev->power.async_suspend) {
+               get_device(dev);
+               async_schedule(async_suspend, dev);
+               return 0;
+       }
+
+       return __device_suspend(dev, pm_transition, false);
+}
+
 /**
  * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
  * @state: PM transition of the system being carried out.
@@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state)
 
        INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
+       pm_transition = state;
+       async_error = 0;
        while (!list_empty(&dpm_list)) {
                struct device *dev = to_device(dpm_list.prev);
 
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
 
-               error = device_suspend(dev, state);
+               error = device_suspend(dev);
 
                mutex_lock(&dpm_list_mtx);
                if (error) {
@@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state)
                        put_device(dev);
                        break;
                }
-               dev->power.status = DPM_OFF;
                if (!list_empty(&dev->power.entry))
                        list_move(&dev->power.entry, &list);
                put_device(dev);
+               if (async_error)
+                       break;
        }
        list_splice(&list, dpm_list.prev);
        mutex_unlock(&dpm_list_mtx);
+       async_synchronize_full();
+       if (!error)
+               error = async_error;
        if (!error)
                dpm_show_time(starttime, state, NULL);
        return error;
@@ -936,3 +1046,14 @@ void __suspend_report_result(const char *function, void *fn, int ret)
                printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
 }
 EXPORT_SYMBOL_GPL(__suspend_report_result);
+
+/**
+ * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete.
+ * @dev: Device to wait for.
+ * @subordinate: Device that needs to wait for @dev.
+ */
+void device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
+{
+       dpm_wait(dev, subordinate->power.async_suspend);
+}
+EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
index b8fa1aa5225a4a7baff8f1b8f83d46d644f82012..c0bd03c83b9cad1dbbb9341aa70f13e85b520760 100644 (file)
@@ -12,10 +12,10 @@ static inline void pm_runtime_remove(struct device *dev) {}
 
 #ifdef CONFIG_PM_SLEEP
 
-/*
- * main.c
- */
+/* kernel/power/main.c */
+extern int pm_async_enabled;
 
+/* drivers/base/power/main.c */
 extern struct list_head dpm_list;      /* The active device list */
 
 static inline struct device *to_device(struct list_head *entry)
index f8b044e8aef7f5e8683c9af1d6ec461d86d197f5..626dd147b75ffce582944d303430197213d61fcf 100644 (file)
@@ -1010,6 +1010,50 @@ void pm_runtime_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(pm_runtime_enable);
 
+/**
+ * pm_runtime_forbid - Block run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Increase the device's usage count and clear its power.runtime_auto flag,
+ * so that it cannot be suspended at run time until pm_runtime_allow() is called
+ * for it.
+ */
+void pm_runtime_forbid(struct device *dev)
+{
+       spin_lock_irq(&dev->power.lock);
+       if (!dev->power.runtime_auto)
+               goto out;
+
+       dev->power.runtime_auto = false;
+       atomic_inc(&dev->power.usage_count);
+       __pm_runtime_resume(dev, false);
+
+ out:
+       spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_forbid);
+
+/**
+ * pm_runtime_allow - Unblock run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Decrease the device's usage count and set its power.runtime_auto flag.
+ */
+void pm_runtime_allow(struct device *dev)
+{
+       spin_lock_irq(&dev->power.lock);
+       if (dev->power.runtime_auto)
+               goto out;
+
+       dev->power.runtime_auto = true;
+       if (atomic_dec_and_test(&dev->power.usage_count))
+               __pm_runtime_idle(dev);
+
+ out:
+       spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_allow);
+
 /**
  * pm_runtime_init - Initialize run-time PM fields in given device object.
  * @dev: Device object to initialize.
@@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev)
 
        atomic_set(&dev->power.child_count, 0);
        pm_suspend_ignore_children(dev, false);
+       dev->power.runtime_auto = true;
 
        dev->power.request_pending = false;
        dev->power.request = RPM_REQ_NONE;
index 596aeecfdffe4be62c7e6773046e9c23d4dd37bb..86fd9373447e4761fe6b0a8f8c81110b99febc54 100644 (file)
@@ -4,9 +4,25 @@
 
 #include <linux/device.h>
 #include <linux/string.h>
+#include <linux/pm_runtime.h>
 #include "power.h"
 
 /*
+ *     control - Report/change current runtime PM setting of the device
+ *
+ *     Runtime power management of a device can be blocked with the help of
+ *     this attribute.  All devices have one of the following two values for
+ *     the power/control file:
+ *
+ *      + "auto\n" to allow the device to be power managed at run time;
+ *      + "on\n" to prevent the device from being power managed at run time;
+ *
+ *     The default for all devices is "auto", which means that devices may be
+ *     subject to automatic power management, depending on their drivers.
+ *     Changing this attribute to "on" prevents the driver from power managing
+ *     the device at run time.  Doing that while the device is suspended causes
+ *     it to be woken up.
+ *
  *     wakeup - Report/change current wakeup option for device
  *
  *     Some devices support "wakeup" events, which are hardware signals
  *     wakeup events internally (unless they are disabled), keeping
  *     their hardware in low power modes whenever they're unused.  This
  *     saves runtime power, without requiring system-wide sleep states.
+ *
+ *     async - Report/change current async suspend setting for the device
+ *
+ *     Asynchronous suspend and resume of the device during system-wide power
+ *     state transitions can be enabled by writing "enabled" to this file.
+ *     Analogously, if "disabled" is written to this file, the device will be
+ *     suspended and resumed synchronously.
+ *
+ *     All devices have one of the following two values for power/async:
+ *
+ *      + "enabled\n" to permit the asynchronous suspend/resume of the device;
+ *      + "disabled\n" to forbid it;
+ *
+ *     NOTE: It generally is unsafe to permit the asynchronous suspend/resume
+ *     of a device unless it is certain that all of the PM dependencies of the
+ *     device are known to the PM core.  However, for some devices this
+ *     attribute is set to "enabled" by bus type code or device drivers and in
+ *     that cases it should be safe to leave the default value.
  */
 
 static const char enabled[] = "enabled";
 static const char disabled[] = "disabled";
 
+#ifdef CONFIG_PM_RUNTIME
+static const char ctrl_auto[] = "auto";
+static const char ctrl_on[] = "on";
+
+static ssize_t control_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       return sprintf(buf, "%s\n",
+                               dev->power.runtime_auto ? ctrl_auto : ctrl_on);
+}
+
+static ssize_t control_store(struct device * dev, struct device_attribute *attr,
+                            const char * buf, size_t n)
+{
+       char *cp;
+       int len = n;
+
+       cp = memchr(buf, '\n', n);
+       if (cp)
+               len = cp - buf;
+       if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
+               pm_runtime_allow(dev);
+       else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
+               pm_runtime_forbid(dev);
+       else
+               return -EINVAL;
+       return n;
+}
+
+static DEVICE_ATTR(control, 0644, control_show, control_store);
+#endif
+
 static ssize_t
 wake_show(struct device * dev, struct device_attribute *attr, char * buf)
 {
@@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr,
 
 static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
 
+#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
+static ssize_t async_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       return sprintf(buf, "%s\n",
+                       device_async_suspend_enabled(dev) ? enabled : disabled);
+}
+
+static ssize_t async_store(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t n)
+{
+       char *cp;
+       int len = n;
+
+       cp = memchr(buf, '\n', n);
+       if (cp)
+               len = cp - buf;
+       if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
+               device_enable_async_suspend(dev);
+       else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
+               device_disable_async_suspend(dev);
+       else
+               return -EINVAL;
+       return n;
+}
+
+static DEVICE_ATTR(async, 0644, async_show, async_store);
+#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */
 
 static struct attribute * power_attrs[] = {
+#ifdef CONFIG_PM_RUNTIME
+       &dev_attr_control.attr,
+#endif
        &dev_attr_wakeup.attr,
+#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
+       &dev_attr_async.attr,
+#endif
        NULL,
 };
 static struct attribute_group pm_attr_group = {
index f4a2738bf0bf740ed96dd54b9efce8eea2c19a74..2b9ac9e594afcfcb3a2aaa797e9bbc07e54f921e 100644 (file)
@@ -1540,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev)
        int pm;
        u16 pmc;
 
+       device_enable_async_suspend(&dev->dev);
        dev->wakeup_prepared = false;
        dev->pm_cap = 0;
 
index 0d34ff4153998ca823ebf7b928b428e165691f3a..e73effbe402c55e82be4d8a821b3d39e64f96bac 100644 (file)
@@ -285,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
                     pci_name(pdev),
                     get_descriptor_id(pdev->pcie_type, service));
        device->parent = &pdev->dev;
+       device_enable_async_suspend(device);
 
        retval = device_register(device);
        if (retval)
index 270d069819f7070a9a27f9bdf36e8961e2f67e7e..2a943090a3b7838b3a8fcb86a9caa89dfe611133 100644 (file)
@@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent,
        if (error)
                goto dev_reg_err;
        b->bridge = get_device(dev);
+       device_enable_async_suspend(b->bridge);
 
        if (!parent)
                set_dev_node(b->bridge, pcibus_to_node(b));
index 554626e1806256fb439d84ed681ee57d575180cc..09dbcb847b73c307ecb835e6463735f00ce9139c 100644 (file)
@@ -215,6 +215,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
                shost->shost_gendev.parent = dev ? dev : &platform_bus;
        shost->dma_dev = dma_dev;
 
+       device_enable_async_suspend(&shost->shost_gendev);
+
        error = device_add(&shost->shost_gendev);
        if (error)
                goto out;
@@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
        scsi_host_set_state(shost, SHOST_RUNNING);
        get_device(shost->shost_gendev.parent);
 
+       device_enable_async_suspend(&shost->shost_dev);
+
        error = device_add(&shost->shost_dev);
        if (error)
                goto out_del_gendev;
index a4936c4e2f46f89f0edbede325b0be7642129445..19ec9e2d3f391c1189e57b178173bd446ed14b20 100644 (file)
@@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget)
        if (starget->state != STARGET_CREATED)
                return 0;
 
+       device_enable_async_suspend(&starget->dev);
+
        error = device_add(&starget->dev);
        if (error) {
                dev_err(&starget->dev, "target device_add failed, error %d\n", error);
@@ -887,11 +889,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
                return error;
 
        transport_configure_device(&starget->dev);
+       device_enable_async_suspend(&sdev->sdev_gendev);
        error = device_add(&sdev->sdev_gendev);
        if (error) {
                printk(KERN_INFO "error 1\n");
                return error;
        }
+       device_enable_async_suspend(&sdev->sdev_dev);
        error = device_add(&sdev->sdev_dev);
        if (error) {
                printk(KERN_INFO "error 2\n");
index 60a45f1e3a67e13b41fd41770d51cbe83b39f730..f2f055eb6831d73dc006b7e3f54e1e954437c6f4 100644 (file)
@@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
                goto done;
        }
 
+       /* 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)
+               device_pm_wait_for_dev(&udev->dev,
+                               &udev->bus->hs_companion->root_hub->dev);
+
        if (udev->quirks & USB_QUIRK_RESET_RESUME)
                udev->reset_resume = 1;
 
index fdfaa7885515d075330921d17442bbc80f1dcf06..d26b9ea981f92f322c5c402f38e53d7c075e7e52 100644 (file)
@@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent,
        ep_dev->dev.parent = parent;
        ep_dev->dev.release = ep_device_release;
        dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
+       device_enable_async_suspend(&ep_dev->dev);
 
        retval = device_register(&ep_dev->dev);
        if (retval)
index 2dcf906df569440d18a0cecb9b4801dec581e77f..15286533c15ac5c315712d30ed3a23c2e7bf3a73 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/pm_runtime.h>
 #include <linux/usb.h>
 
 #include <asm/io.h>
 
 /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
 
+#ifdef CONFIG_PM_SLEEP
+
+/* Coordinate handoffs between EHCI and companion controllers
+ * during system resume
+ */
+
+static DEFINE_MUTEX(companions_mutex);
+
+#define CL_UHCI                PCI_CLASS_SERIAL_USB_UHCI
+#define CL_OHCI                PCI_CLASS_SERIAL_USB_OHCI
+#define CL_EHCI                PCI_CLASS_SERIAL_USB_EHCI
+
+enum companion_action {
+       SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
+};
+
+static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
+               enum companion_action action)
+{
+       struct pci_dev          *companion;
+       struct usb_hcd          *companion_hcd;
+       unsigned int            slot = PCI_SLOT(pdev->devfn);
+
+       /* Iterate through other PCI functions in the same slot.
+        * If pdev is OHCI or UHCI then we are looking for EHCI, and
+        * vice versa.
+        */
+       companion = NULL;
+       for (;;) {
+               companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
+               if (!companion)
+                       break;
+               if (companion->bus != pdev->bus ||
+                               PCI_SLOT(companion->devfn) != slot)
+                       continue;
+
+               companion_hcd = pci_get_drvdata(companion);
+               if (!companion_hcd)
+                       continue;
+
+               /* For SET_HS_COMPANION, store a pointer to the EHCI bus in
+                * the OHCI/UHCI companion bus structure.
+                * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
+                * in the OHCI/UHCI companion bus structure.
+                * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
+                * companion controllers have fully resumed.
+                */
+
+               if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
+                               companion->class == CL_EHCI) {
+                       /* action must be SET_HS_COMPANION */
+                       dev_dbg(&companion->dev, "HS companion for %s\n",
+                                       dev_name(&pdev->dev));
+                       hcd->self.hs_companion = &companion_hcd->self;
+
+               } else if (pdev->class == CL_EHCI &&
+                               (companion->class == CL_OHCI ||
+                               companion->class == CL_UHCI)) {
+                       switch (action) {
+                       case SET_HS_COMPANION:
+                               dev_dbg(&pdev->dev, "HS companion for %s\n",
+                                               dev_name(&companion->dev));
+                               companion_hcd->self.hs_companion = &hcd->self;
+                               break;
+                       case CLEAR_HS_COMPANION:
+                               companion_hcd->self.hs_companion = NULL;
+                               break;
+                       case WAIT_FOR_COMPANIONS:
+                               device_pm_wait_for_dev(&pdev->dev,
+                                               &companion->dev);
+                               break;
+                       }
+               }
+       }
+}
+
+static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+       mutex_lock(&companions_mutex);
+       dev_set_drvdata(&pdev->dev, hcd);
+       companion_common(pdev, hcd, SET_HS_COMPANION);
+       mutex_unlock(&companions_mutex);
+}
+
+static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+       mutex_lock(&companions_mutex);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
+       if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
+               hcd->self.hs_companion = NULL;
+
+       /* Otherwise search for companion buses and clear their pointers */
+       else
+               companion_common(pdev, hcd, CLEAR_HS_COMPANION);
+       mutex_unlock(&companions_mutex);
+}
+
+static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
+{
+       /* Only EHCI controllers need to wait.
+        * No locking is needed because a controller cannot be resumed
+        * while one of its companions is getting unbound.
+        */
+       if (pdev->class == CL_EHCI)
+               companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
+}
+
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
+static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
+static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
+
+#endif /* !CONFIG_PM_SLEEP */
 
 /*-------------------------------------------------------------------------*/
 
@@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                if (region == PCI_ROM_RESOURCE) {
                        dev_dbg(&dev->dev, "no i/o regions available\n");
                        retval = -EBUSY;
-                       goto err1;
+                       goto err2;
                }
        }
 
@@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
        if (retval != 0)
                goto err4;
+       set_hs_companion(dev, hcd);
        return retval;
 
  err4:
@@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        } else
                release_region(hcd->rsrc_start, hcd->rsrc_len);
  err2:
+       clear_hs_companion(dev, hcd);
        usb_put_hcd(hcd);
  err1:
        pci_disable_device(dev);
@@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
        } else {
                release_region(hcd->rsrc_start, hcd->rsrc_len);
        }
+       clear_hs_companion(dev, hcd);
        usb_put_hcd(hcd);
        pci_disable_device(dev);
 }
@@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated)
        clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
 
        if (hcd->driver->pci_resume) {
+               /* This call should be made only during system resume,
+                * not during runtime resume.
+                */
+               wait_for_companions(pci_dev, hcd);
+
                retval = hcd->driver->pci_resume(hcd, hibernated);
                if (retval) {
                        dev_err(dev, "PCI post-resume error %d!\n", retval);
index 35cc8b9ba1f5bfab21331d6241a529ca2b3de304..20ecb4cec8de56540f507d459c205b51151da148 100644 (file)
@@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       device_enable_async_suspend(&udev->dev);
        /* Register the device.  The device driver is responsible
         * for configuring the device and invoking the add-device
         * notifier chain (used by usbfs and possibly others).
index 9bc95fec793fa674727d19c53820853c128afabb..df73574a9cc9e567fc621a00d935f423923ac762 100644 (file)
@@ -1867,6 +1867,7 @@ free_interfaces:
                        "adding %s (config #%d, interface %d)\n",
                        dev_name(&intf->dev), configuration,
                        intf->cur_altsetting->desc.bInterfaceNumber);
+               device_enable_async_suspend(&intf->dev);
                ret = device_add(&intf->dev);
                if (ret != 0) {
                        dev_err(&dev->dev, "device_add(%s) --> %d\n",
index a62799f2ab0019863d30e4f55f7677c5bd97d124..b30527db3ac0f69ed95089f79c1e1dd7965479fc 100644 (file)
@@ -472,6 +472,23 @@ static inline int device_is_registered(struct device *dev)
        return dev->kobj.state_in_sysfs;
 }
 
+static inline void device_enable_async_suspend(struct device *dev)
+{
+       if (dev->power.status == DPM_ON)
+               dev->power.async_suspend = true;
+}
+
+static inline void device_disable_async_suspend(struct device *dev)
+{
+       if (dev->power.status == DPM_ON)
+               dev->power.async_suspend = false;
+}
+
+static inline bool device_async_suspend_enabled(struct device *dev)
+{
+       return !!dev->power.async_suspend;
+}
+
 void driver_init(void);
 
 /*
index 198b8f9fe05ec2ab97d351fbfba053f980869100..e80df06ad22ab1aaa7d86a76434a4a5b9a2aadf8 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <linux/timer.h>
+#include <linux/completion.h>
 
 /*
  * Callbacks for platform drivers to implement.
@@ -412,9 +413,11 @@ struct dev_pm_info {
        pm_message_t            power_state;
        unsigned int            can_wakeup:1;
        unsigned int            should_wakeup:1;
+       unsigned                async_suspend:1;
        enum dpm_state          status;         /* Owned by the PM core */
 #ifdef CONFIG_PM_SLEEP
        struct list_head        entry;
+       struct completion       completion;
 #endif
 #ifdef CONFIG_PM_RUNTIME
        struct timer_list       suspend_timer;
@@ -430,6 +433,7 @@ struct dev_pm_info {
        unsigned int            request_pending:1;
        unsigned int            deferred_resume:1;
        unsigned int            run_wake:1;
+       unsigned int            runtime_auto:1;
        enum rpm_request        request;
        enum rpm_status         runtime_status;
        int                     runtime_error;
@@ -508,6 +512,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
                __suspend_report_result(__func__, fn, ret);             \
        } while (0)
 
+extern void device_pm_wait_for_dev(struct device *sub, struct device *dev);
 #else /* !CONFIG_PM_SLEEP */
 
 #define device_pm_lock() do {} while (0)
@@ -520,6 +525,7 @@ static inline int dpm_suspend_start(pm_message_t state)
 
 #define suspend_report_result(fn, ret)         do {} while (0)
 
+static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {}
 #endif /* !CONFIG_PM_SLEEP */
 
 /* How to reorder dpm_list after device_move() */
index 370ce0a6fe4a8702f9c4f25de37f49c217dfd158..7d773aac5314765f131fd5f108a55f800ee3e589 100644 (file)
@@ -28,6 +28,8 @@ extern int __pm_runtime_set_status(struct device *dev, unsigned int status);
 extern int pm_runtime_barrier(struct device *dev);
 extern void pm_runtime_enable(struct device *dev);
 extern void __pm_runtime_disable(struct device *dev, bool check_resume);
+extern void pm_runtime_allow(struct device *dev);
+extern void pm_runtime_forbid(struct device *dev);
 
 static inline bool pm_children_suspended(struct device *dev)
 {
@@ -78,6 +80,8 @@ static inline int __pm_runtime_set_status(struct device *dev,
 static inline int pm_runtime_barrier(struct device *dev) { return 0; }
 static inline void pm_runtime_enable(struct device *dev) {}
 static inline void __pm_runtime_disable(struct device *dev, bool c) {}
+static inline void pm_runtime_allow(struct device *dev) {}
+static inline void pm_runtime_forbid(struct device *dev) {}
 
 static inline bool pm_children_suspended(struct device *dev) { return false; }
 static inline void pm_suspend_ignore_children(struct device *dev, bool en) {}
index c9ba2fdf807d5cbca878d5873ef38ca50332a201..bc8c3881c729fb29128b373b8fb6e0ad68feac45 100644 (file)
@@ -6,6 +6,11 @@
 
 extern int pm_trace_enabled;
 
+static inline int pm_trace_is_enabled(void)
+{
+       return pm_trace_enabled;
+}
+
 struct device;
 extern void set_trace_device(struct device *);
 extern void generate_resume_trace(const void *tracedata, unsigned int user);
@@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user);
 
 #else
 
+static inline int pm_trace_is_enabled(void) { return 0; }
+
 #define TRACE_DEVICE(dev) do { } while (0)
 #define TRACE_RESUME(dev) do { } while (0)
 
index d7ace1b80f09d0936c1f2f23e8a62b82f1c5baa7..332eaea610217aab63378e27daf8afe3720b6a6c 100644 (file)
@@ -339,6 +339,7 @@ struct usb_bus {
 
        struct usb_devmap devmap;       /* device address allocation map */
        struct usb_device *root_hub;    /* Root hub */
+       struct usb_bus *hs_companion;   /* Companion EHCI bus, if any */
        struct list_head bus_list;      /* list of busses */
 
        int bandwidth_allocated;        /* on this bus: how much of the time
index 4c9cffcf69c7e90fcb0c8cd37a151b43e8945115..5c36ea9d55d22ccf5a27d542b262968a13489d57 100644 (file)
@@ -27,6 +27,15 @@ config PM_DEBUG
        code. This is helpful when debugging and reporting PM bugs, like
        suspend support.
 
+config PM_ADVANCED_DEBUG
+       bool "Extra PM attributes in sysfs for low-level debugging/testing"
+       depends on PM_DEBUG
+       default n
+       ---help---
+       Add extra sysfs attributes allowing one to access some Power Management
+       fields of device objects from user space.  If you are not a kernel
+       developer interested in debugging/testing Power Management, say "no".
+
 config PM_VERBOSE
        bool "Verbose Power Management debugging"
        depends on PM_DEBUG
@@ -85,6 +94,11 @@ config PM_SLEEP
        depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE
        default y
 
+config PM_SLEEP_ADVANCED_DEBUG
+       bool
+       depends on PM_ADVANCED_DEBUG
+       default n
+
 config SUSPEND
        bool "Suspend to RAM and standby"
        depends on PM && ARCH_SUSPEND_POSSIBLE
index 0998c7139053c8c5ec46165d32327bba42f2ecce..b58800b21fc01b98fc8af60c2b9ab16385256cc7 100644 (file)
@@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long val)
                        == NOTIFY_BAD) ? -EINVAL : 0;
 }
 
+/* If set, devices may be suspended and resumed asynchronously. */
+int pm_async_enabled = 1;
+
+static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
+                            char *buf)
+{
+       return sprintf(buf, "%d\n", pm_async_enabled);
+}
+
+static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
+                             const char *buf, size_t n)
+{
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+
+       if (val > 1)
+               return -EINVAL;
+
+       pm_async_enabled = val;
+       return n;
+}
+
+power_attr(pm_async);
+
 #ifdef CONFIG_PM_DEBUG
 int pm_test_level = TEST_NONE;
 
@@ -208,8 +234,11 @@ static struct attribute * g[] = {
 #ifdef CONFIG_PM_TRACE
        &pm_trace_attr.attr,
 #endif
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
+#ifdef CONFIG_PM_SLEEP
+       &pm_async_attr.attr,
+#ifdef CONFIG_PM_DEBUG
        &pm_test_attr.attr,
+#endif
 #endif
        NULL,
 };
index 36cb168e4330936a9120691fd19e80ddb8d2db9d..830cadecbdfcf2c9f7adedeca5cb6e1f169f8e87 100644 (file)
@@ -1181,7 +1181,7 @@ static void free_unnecessary_pages(void)
 
        memory_bm_position_reset(&copy_bm);
 
-       while (to_free_normal > 0 && to_free_highmem > 0) {
+       while (to_free_normal > 0 || to_free_highmem > 0) {
                unsigned long pfn = memory_bm_next_pfn(&copy_bm);
                struct page *page = pfn_to_page(pfn);
 
@@ -1500,7 +1500,7 @@ asmlinkage int swsusp_save(void)
 {
        unsigned int nr_pages, nr_highmem;
 
-       printk(KERN_INFO "PM: Creating hibernation image: \n");
+       printk(KERN_INFO "PM: Creating hibernation image:\n");
 
        drain_local_pages(NULL);
        nr_pages = count_data_pages();
index 09b2b0ae9e9d474fdc2f459f9741ab5545c4cf6c..1d575733d4e142f68ee3a43ea033ccb1882111c7 100644 (file)
@@ -657,10 +657,6 @@ int swsusp_read(unsigned int *flags_p)
        struct swsusp_info *header;
 
        *flags_p = swsusp_header->flags;
-       if (IS_ERR(resume_bdev)) {
-               pr_debug("PM: Image device not initialised\n");
-               return PTR_ERR(resume_bdev);
-       }
 
        memset(&snapshot, 0, sizeof(struct snapshot_handle));
        error = snapshot_write_next(&snapshot, PAGE_SIZE);
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
deleted file mode 100644 (file)
index 5b3601b..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * linux/kernel/power/swsusp.c
- *
- * This file provides code to write suspend image to swap and read it back.
- *
- * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
- * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz>
- *
- * This file is released under the GPLv2.
- *
- * I'd like to thank the following people for their work:
- *
- * Pavel Machek <pavel@ucw.cz>:
- * Modifications, defectiveness pointing, being with me at the very beginning,
- * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
- *
- * Steve Doddi <dirk@loth.demon.co.uk>:
- * Support the possibility of hardware state restoring.
- *
- * Raph <grey.havens@earthling.net>:
- * Support for preserving states of network devices and virtual console
- * (including X and svgatextmode)
- *
- * Kurt Garloff <garloff@suse.de>:
- * Straightened the critical function in order to prevent compilers from
- * playing tricks with local variables.
- *
- * Andreas Mohr <a.mohr@mailto.de>
- *
- * Alex Badea <vampire@go.ro>:
- * Fixed runaway init
- *
- * Rafael J. Wysocki <rjw@sisk.pl>
- * Reworked the freeing of memory and the handling of swap
- *
- * More state savers are welcome. Especially for the scsi layer...
- *
- * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
- */
-
-#include <linux/mm.h>
-#include <linux/suspend.h>
-#include <linux/spinlock.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/swap.h>
-#include <linux/pm.h>
-#include <linux/swapops.h>
-#include <linux/bootmem.h>
-#include <linux/syscalls.h>
-#include <linux/highmem.h>
-#include <linux/time.h>
-#include <linux/rbtree.h>
-#include <linux/io.h>
-
-#include "power.h"
-
-int in_suspend __nosavedata = 0;
index bf0014d6a5f0972a1b2352d35a09eb82296164d1..4d2289626a84e6d03dbc618db13fbb8af4bd6092 100644 (file)
@@ -195,6 +195,15 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,
        return res;
 }
 
+static void snapshot_deprecated_ioctl(unsigned int cmd)
+{
+       if (printk_ratelimit())
+               printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will "
+                               "be removed soon, update your suspend-to-disk "
+                               "utilities\n",
+                               __builtin_return_address(0), cmd);
+}
+
 static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                                                        unsigned long arg)
 {
@@ -246,8 +255,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                data->frozen = 0;
                break;
 
-       case SNAPSHOT_CREATE_IMAGE:
        case SNAPSHOT_ATOMIC_SNAPSHOT:
+               snapshot_deprecated_ioctl(cmd);
+       case SNAPSHOT_CREATE_IMAGE:
                if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
                        error = -EPERM;
                        break;
@@ -275,8 +285,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                data->ready = 0;
                break;
 
-       case SNAPSHOT_PREF_IMAGE_SIZE:
        case SNAPSHOT_SET_IMAGE_SIZE:
+               snapshot_deprecated_ioctl(cmd);
+       case SNAPSHOT_PREF_IMAGE_SIZE:
                image_size = arg;
                break;
 
@@ -290,15 +301,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                error = put_user(size, (loff_t __user *)arg);
                break;
 
-       case SNAPSHOT_AVAIL_SWAP_SIZE:
        case SNAPSHOT_AVAIL_SWAP:
+               snapshot_deprecated_ioctl(cmd);
+       case SNAPSHOT_AVAIL_SWAP_SIZE:
                size = count_swap_pages(data->swap, 1);
                size <<= PAGE_SHIFT;
                error = put_user(size, (loff_t __user *)arg);
                break;
 
-       case SNAPSHOT_ALLOC_SWAP_PAGE:
        case SNAPSHOT_GET_SWAP_PAGE:
+               snapshot_deprecated_ioctl(cmd);
+       case SNAPSHOT_ALLOC_SWAP_PAGE:
                if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
                        error = -ENODEV;
                        break;
@@ -321,6 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                break;
 
        case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */
+               snapshot_deprecated_ioctl(cmd);
                if (!swsusp_swap_in_use()) {
                        /*
                         * User space encodes device types as two-byte values,
@@ -362,6 +376,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                break;
 
        case SNAPSHOT_PMOPS: /* This ioctl is deprecated */
+               snapshot_deprecated_ioctl(cmd);
                error = -EINVAL;
 
                switch (arg) {