PM: Do not create wakeup sysfs files for devices that cannot wake up
authorRafael J. Wysocki <rjw@sisk.pl>
Tue, 8 Feb 2011 22:26:02 +0000 (23:26 +0100)
committerRafael J. Wysocki <rjw@sisk.pl>
Mon, 14 Mar 2011 23:43:14 +0000 (00:43 +0100)
Currently, wakeup sysfs attributes are created for all devices,
regardless of whether or not they are wakeup-capable.  This is
excessive and complicates wakeup device identification from user
space (i.e. to identify wakeup-capable devices user space has to read
/sys/devices/.../power/wakeup for all devices and see if they are not
empty).

Fix this issue by avoiding to create wakeup sysfs files for devices
that cannot wake up the system from sleep states (i.e. whose
power.can_wakeup flags are unset during registration) and modify
device_set_wakeup_capable() so that it adds (or removes) the relevant
sysfs attributes if a device's wakeup capability status is changed.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Documentation/ABI/testing/sysfs-devices-power
Documentation/power/devices.txt
drivers/base/power/power.h
drivers/base/power/sysfs.c
drivers/base/power/wakeup.c
include/linux/pm_runtime.h
include/linux/pm_wakeup.h

index 7628cd1bc36a5e8080a646da492736c3e43b917d..8ffbc25376a0f59cfcde7b10157a0d58b235a748 100644 (file)
@@ -29,9 +29,8 @@ Description:
                "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.
+               events this file is not present.  In that case the device cannot
+               be enabled to wake up the system from sleep states.
 
 What:          /sys/devices/.../power/control
 Date:          January 2009
@@ -85,7 +84,7 @@ Description:
                The /sys/devices/.../wakeup_count attribute contains the number
                of signaled wakeup events associated with the device.  This
                attribute is read-only.  If the device is not enabled to wake up
-               the system from sleep states, this attribute is empty.
+               the system from sleep states, this attribute is not present.
 
 What:          /sys/devices/.../power/wakeup_active_count
 Date:          September 2010
@@ -95,7 +94,7 @@ Description:
                number of times the processing of wakeup events associated with
                the device was completed (at the kernel level).  This attribute
                is read-only.  If the device is not enabled to wake up the
-               system from sleep states, this attribute is empty.
+               system from sleep states, this attribute is not present.
 
 What:          /sys/devices/.../power/wakeup_hit_count
 Date:          September 2010
@@ -105,7 +104,8 @@ Description:
                number of times the processing of a wakeup event associated with
                the device might prevent the system from entering a sleep state.
                This attribute is read-only.  If the device is not enabled to
-               wake up the system from sleep states, this attribute is empty.
+               wake up the system from sleep states, this attribute is not
+               present.
 
 What:          /sys/devices/.../power/wakeup_active
 Date:          September 2010
@@ -115,7 +115,7 @@ Description:
                or 0, depending on whether or not a wakeup event associated with
                the device is being processed (1).  This attribute is read-only.
                If the device is not enabled to wake up the system from sleep
-               states, this attribute is empty.
+               states, this attribute is not present.
 
 What:          /sys/devices/.../power/wakeup_total_time_ms
 Date:          September 2010
@@ -125,7 +125,7 @@ Description:
                the total time of processing wakeup events associated with the
                device, in milliseconds.  This attribute is read-only.  If the
                device is not enabled to wake up the system from sleep states,
-               this attribute is empty.
+               this attribute is not present.
 
 What:          /sys/devices/.../power/wakeup_max_time_ms
 Date:          September 2010
@@ -135,7 +135,7 @@ Description:
                the maximum time of processing a single wakeup event associated
                with the device, in milliseconds.  This attribute is read-only.
                If the device is not enabled to wake up the system from sleep
-               states, this attribute is empty.
+               states, this attribute is not present.
 
 What:          /sys/devices/.../power/wakeup_last_time_ms
 Date:          September 2010
@@ -146,7 +146,7 @@ Description:
                signaling the last wakeup event associated with the device, in
                milliseconds.  This attribute is read-only.  If the device is
                not enabled to wake up the system from sleep states, this
-               attribute is empty.
+               attribute is not present.
 
 What:          /sys/devices/.../power/autosuspend_delay_ms
 Date:          September 2010
index 57080cd74575511ccaa3eebfb119a8560f343871..dd9b49251db324b26038d07bc310006236fd745e 100644 (file)
@@ -159,18 +159,18 @@ matter, and the kernel is responsible for keeping track of it.  By contrast,
 whether or not a wakeup-capable device should issue wakeup events is a policy
 decision, and it is managed by user space through a sysfs attribute: the
 power/wakeup file.  User space can write the strings "enabled" or "disabled" to
-set or clear the should_wakeup flag, respectively.  Reads from the file will
-return the corresponding string if can_wakeup is true, but if can_wakeup is
-false then reads will return an empty string, to indicate that the device
-doesn't support wakeup events.  (But even though the file appears empty, writes
-will still affect the should_wakeup flag.)
+set or clear the "should_wakeup" flag, respectively.  This file is only present
+for wakeup-capable devices (i.e. devices whose "can_wakeup" flags are set)
+and is created (or removed) by device_set_wakeup_capable().  Reads from the
+file will return the corresponding string.
 
 The device_may_wakeup() routine returns true only if both flags are set.
-Drivers should check this routine when putting devices in a low-power state
-during a system sleep transition, to see whether or not to enable the devices'
-wakeup mechanisms.  However for runtime power management, wakeup events should
-be enabled whenever the device and driver both support them, regardless of the
-should_wakeup flag.
+This information is used by subsystems, like the PCI bus type code, to see
+whether or not to enable the devices' wakeup mechanisms.  If device wakeup
+mechanisms are enabled or disabled directly by drivers, they also should use
+device_may_wakeup() to decide what to do during a system sleep transition.
+However for runtime power management, wakeup events should be enabled whenever
+the device and driver both support them, regardless of the should_wakeup flag.
 
 
 /sys/devices/.../power/control files
index 698dde74258792544098eb8866e77ee72303c865..f2a25f18fde7caaa3a1949c893afae3c872b76c3 100644 (file)
@@ -58,19 +58,18 @@ static inline void device_pm_move_last(struct device *dev) {}
  * sysfs.c
  */
 
-extern int dpm_sysfs_add(struct device *);
-extern void dpm_sysfs_remove(struct device *);
-extern void rpm_sysfs_remove(struct device *);
+extern int dpm_sysfs_add(struct device *dev);
+extern void dpm_sysfs_remove(struct device *dev);
+extern void rpm_sysfs_remove(struct device *dev);
+extern int wakeup_sysfs_add(struct device *dev);
+extern void wakeup_sysfs_remove(struct device *dev);
 
 #else /* CONFIG_PM */
 
-static inline int dpm_sysfs_add(struct device *dev)
-{
-       return 0;
-}
-
-static inline void dpm_sysfs_remove(struct device *dev)
-{
-}
+static inline int dpm_sysfs_add(struct device *dev) { return 0; }
+static inline void dpm_sysfs_remove(struct device *dev) {}
+static inline void rpm_sysfs_remove(struct device *dev) {}
+static inline int wakeup_sysfs_add(struct device *dev) { return 0; }
+static inline void wakeup_sysfs_remove(struct device *dev) {}
 
 #endif
index 0b1e46bf3e56950e45366ab46a664992ac2c1f0f..fff49bee781d44f4fd2f35dda22f86a388fd9259 100644 (file)
@@ -431,26 +431,18 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr,
 static DEVICE_ATTR(async, 0644, async_show, async_store);
 #endif /* CONFIG_PM_ADVANCED_DEBUG */
 
-static struct attribute * power_attrs[] = {
-       &dev_attr_wakeup.attr,
-#ifdef CONFIG_PM_SLEEP
-       &dev_attr_wakeup_count.attr,
-       &dev_attr_wakeup_active_count.attr,
-       &dev_attr_wakeup_hit_count.attr,
-       &dev_attr_wakeup_active.attr,
-       &dev_attr_wakeup_total_time_ms.attr,
-       &dev_attr_wakeup_max_time_ms.attr,
-       &dev_attr_wakeup_last_time_ms.attr,
-#endif
+static struct attribute *power_attrs[] = {
 #ifdef CONFIG_PM_ADVANCED_DEBUG
+#ifdef CONFIG_PM_SLEEP
        &dev_attr_async.attr,
+#endif
 #ifdef CONFIG_PM_RUNTIME
        &dev_attr_runtime_status.attr,
        &dev_attr_runtime_usage.attr,
        &dev_attr_runtime_active_kids.attr,
        &dev_attr_runtime_enabled.attr,
 #endif
-#endif
+#endif /* CONFIG_PM_ADVANCED_DEBUG */
        NULL,
 };
 static struct attribute_group pm_attr_group = {
@@ -458,9 +450,26 @@ static struct attribute_group pm_attr_group = {
        .attrs  = power_attrs,
 };
 
-#ifdef CONFIG_PM_RUNTIME
+static struct attribute *wakeup_attrs[] = {
+#ifdef CONFIG_PM_SLEEP
+       &dev_attr_wakeup.attr,
+       &dev_attr_wakeup_count.attr,
+       &dev_attr_wakeup_active_count.attr,
+       &dev_attr_wakeup_hit_count.attr,
+       &dev_attr_wakeup_active.attr,
+       &dev_attr_wakeup_total_time_ms.attr,
+       &dev_attr_wakeup_max_time_ms.attr,
+       &dev_attr_wakeup_last_time_ms.attr,
+#endif
+       NULL,
+};
+static struct attribute_group pm_wakeup_attr_group = {
+       .name   = power_group_name,
+       .attrs  = wakeup_attrs,
+};
 
 static struct attribute *runtime_attrs[] = {
+#ifdef CONFIG_PM_RUNTIME
 #ifndef CONFIG_PM_ADVANCED_DEBUG
        &dev_attr_runtime_status.attr,
 #endif
@@ -468,6 +477,7 @@ static struct attribute *runtime_attrs[] = {
        &dev_attr_runtime_suspended_time.attr,
        &dev_attr_runtime_active_time.attr,
        &dev_attr_autosuspend_delay_ms.attr,
+#endif /* CONFIG_PM_RUNTIME */
        NULL,
 };
 static struct attribute_group pm_runtime_attr_group = {
@@ -480,35 +490,49 @@ int dpm_sysfs_add(struct device *dev)
        int rc;
 
        rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
-       if (rc == 0 && !dev->power.no_callbacks) {
+       if (rc)
+               return rc;
+
+       if (pm_runtime_callbacks_present(dev)) {
                rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
                if (rc)
-                       sysfs_remove_group(&dev->kobj, &pm_attr_group);
+                       goto err_out;
+       }
+
+       if (device_can_wakeup(dev)) {
+               rc = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
+               if (rc) {
+                       if (pm_runtime_callbacks_present(dev))
+                               sysfs_unmerge_group(&dev->kobj,
+                                                   &pm_runtime_attr_group);
+                       goto err_out;
+               }
        }
+       return 0;
+
+ err_out:
+       sysfs_remove_group(&dev->kobj, &pm_attr_group);
        return rc;
 }
 
-void rpm_sysfs_remove(struct device *dev)
+int wakeup_sysfs_add(struct device *dev)
 {
-       sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
+       return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
 }
 
-void dpm_sysfs_remove(struct device *dev)
+void wakeup_sysfs_remove(struct device *dev)
 {
-       rpm_sysfs_remove(dev);
-       sysfs_remove_group(&dev->kobj, &pm_attr_group);
+       sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
 }
 
-#else /* CONFIG_PM_RUNTIME */
-
-int dpm_sysfs_add(struct device * dev)
+void rpm_sysfs_remove(struct device *dev)
 {
-       return sysfs_create_group(&dev->kobj, &pm_attr_group);
+       sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
 }
 
-void dpm_sysfs_remove(struct device * dev)
+void dpm_sysfs_remove(struct device *dev)
 {
+       rpm_sysfs_remove(dev);
+       sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
        sysfs_remove_group(&dev->kobj, &pm_attr_group);
 }
-
-#endif
index 82836383997f9193923634bab9e04ab5603ba18c..4573c83df6ddc4d996e342bc47bb54f717063474 100644 (file)
@@ -241,6 +241,35 @@ int device_wakeup_disable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_disable);
 
+/**
+ * device_set_wakeup_capable - Set/reset device wakeup capability flag.
+ * @dev: Device to handle.
+ * @capable: Whether or not @dev is capable of waking up the system from sleep.
+ *
+ * If @capable is set, set the @dev's power.can_wakeup flag and add its
+ * wakeup-related attributes to sysfs.  Otherwise, unset the @dev's
+ * power.can_wakeup flag and remove its wakeup-related attributes from sysfs.
+ *
+ * This function may sleep and it can't be called from any context where
+ * sleeping is not allowed.
+ */
+void device_set_wakeup_capable(struct device *dev, bool capable)
+{
+       if (!!dev->power.can_wakeup == !!capable)
+               return;
+
+       if (device_is_registered(dev)) {
+               if (capable) {
+                       if (wakeup_sysfs_add(dev))
+                               return;
+               } else {
+                       wakeup_sysfs_remove(dev);
+               }
+       }
+       dev->power.can_wakeup = capable;
+}
+EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
+
 /**
  * device_init_wakeup - Device wakeup initialization.
  * @dev: Device to handle.
index d34f067e2a7f31e5b391b22382b811913b407515..8de9aa6e7def9a4e2380b3f2d4e9a17727518890 100644 (file)
@@ -87,6 +87,11 @@ static inline bool pm_runtime_enabled(struct device *dev)
        return !dev->power.disable_depth;
 }
 
+static inline bool pm_runtime_callbacks_present(struct device *dev)
+{
+       return !dev->power.no_callbacks;
+}
+
 static inline void pm_runtime_mark_last_busy(struct device *dev)
 {
        ACCESS_ONCE(dev->power.last_busy) = jiffies;
@@ -133,6 +138,7 @@ static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
 static inline void pm_runtime_no_callbacks(struct device *dev) {}
 static inline void pm_runtime_irq_safe(struct device *dev) {}
 
+static inline bool pm_runtime_callbacks_present(struct device *dev) { return false; }
 static inline void pm_runtime_mark_last_busy(struct device *dev) {}
 static inline void __pm_runtime_use_autosuspend(struct device *dev,
                                                bool use) {}
index 03a67db03d01e4133e027d22871929e5c150956e..a32da962d6932963196cf0ea3783d27db33c9fbe 100644 (file)
@@ -62,18 +62,11 @@ struct wakeup_source {
  * Changes to device_may_wakeup take effect on the next pm state change.
  */
 
-static inline void device_set_wakeup_capable(struct device *dev, bool capable)
-{
-       dev->power.can_wakeup = capable;
-}
-
 static inline bool device_can_wakeup(struct device *dev)
 {
        return dev->power.can_wakeup;
 }
 
-
-
 static inline bool device_may_wakeup(struct device *dev)
 {
        return dev->power.can_wakeup && !!dev->power.wakeup;
@@ -88,6 +81,7 @@ extern struct wakeup_source *wakeup_source_register(const char *name);
 extern void wakeup_source_unregister(struct wakeup_source *ws);
 extern int device_wakeup_enable(struct device *dev);
 extern int device_wakeup_disable(struct device *dev);
+extern void device_set_wakeup_capable(struct device *dev, bool capable);
 extern int device_init_wakeup(struct device *dev, bool val);
 extern int device_set_wakeup_enable(struct device *dev, bool enable);
 extern void __pm_stay_awake(struct wakeup_source *ws);