PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
authorRafael J. Wysocki <rjw@sisk.pl>
Sun, 29 Apr 2012 20:53:32 +0000 (22:53 +0200)
committerRafael J. Wysocki <rjw@sisk.pl>
Tue, 1 May 2012 19:25:49 +0000 (21:25 +0200)
Android uses one wakelock statistics that is only necessary for
opportunistic sleep.  Namely, the prevent_suspend_time field
accumulates the total time the given wakelock has been locked
while "automatic suspend" was enabled.  Add an analogous field,
prevent_sleep_time, to wakeup sources and make it behave in a similar
way.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-devices-power
drivers/base/power/sysfs.c
drivers/base/power/wakeup.c
include/linux/pm_wakeup.h
include/linux/suspend.h
kernel/power/autosleep.c

index b0a5d9a6135e4c9bfd1c460eb1dcdc3daeb09f91..45000f0db4d4b4f7779ee9dbd44e727613a930cf 100644 (file)
@@ -158,6 +158,17 @@ Description:
                not enabled to wake up the system from sleep states, this
                attribute is not present.
 
+What:          /sys/devices/.../power/wakeup_prevent_sleep_time_ms
+Date:          February 2012
+Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+               The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute
+               contains the total time the device has been preventing
+               opportunistic transitions to sleep states from occuring.
+               This attribute is read-only.  If the device is not enabled to
+               wake up the system from sleep states, this attribute is not
+               present.
+
 What:          /sys/devices/.../power/autosuspend_delay_ms
 Date:          September 2010
 Contact:       Alan Stern <stern@rowland.harvard.edu>
index 13e40b9021b977177e9b93ac4932eff167dc9ba0..48be2ad4dd2cb4f54c592bfdcaf99f2aa509ca8c 100644 (file)
@@ -417,6 +417,27 @@ static ssize_t wakeup_last_time_show(struct device *dev,
 }
 
 static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
+
+#ifdef CONFIG_PM_AUTOSLEEP
+static ssize_t wakeup_prevent_sleep_time_show(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       s64 msec = 0;
+       bool enabled = false;
+
+       spin_lock_irq(&dev->power.lock);
+       if (dev->power.wakeup) {
+               msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time);
+               enabled = true;
+       }
+       spin_unlock_irq(&dev->power.lock);
+       return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+}
+
+static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444,
+                  wakeup_prevent_sleep_time_show, NULL);
+#endif /* CONFIG_PM_AUTOSLEEP */
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM_ADVANCED_DEBUG
@@ -511,6 +532,9 @@ static struct attribute *wakeup_attrs[] = {
        &dev_attr_wakeup_total_time_ms.attr,
        &dev_attr_wakeup_max_time_ms.attr,
        &dev_attr_wakeup_last_time_ms.attr,
+#ifdef CONFIG_PM_AUTOSLEEP
+       &dev_attr_wakeup_prevent_sleep_time_ms.attr,
+#endif
 #endif
        NULL,
 };
index cf1706df7610e4c5f29fff947e0a5a78e7b5cf01..2595b8d8fe1f92a99637d5d2a6fea2a016f7a0b6 100644 (file)
@@ -380,6 +380,8 @@ static void wakeup_source_activate(struct wakeup_source *ws)
        ws->active = true;
        ws->active_count++;
        ws->last_time = ktime_get();
+       if (ws->autosleep_enabled)
+               ws->start_prevent_time = ws->last_time;
 
        /* Increment the counter of events in progress. */
        cec = atomic_inc_return(&combined_event_count);
@@ -449,6 +451,17 @@ void pm_stay_awake(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(pm_stay_awake);
 
+#ifdef CONFIG_PM_AUTOSLEEP
+static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now)
+{
+       ktime_t delta = ktime_sub(now, ws->start_prevent_time);
+       ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta);
+}
+#else
+static inline void update_prevent_sleep_time(struct wakeup_source *ws,
+                                            ktime_t now) {}
+#endif
+
 /**
  * wakup_source_deactivate - Mark given wakeup source as inactive.
  * @ws: Wakeup source to handle.
@@ -490,6 +503,9 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
        del_timer(&ws->timer);
        ws->timer_expires = 0;
 
+       if (ws->autosleep_enabled)
+               update_prevent_sleep_time(ws, now);
+
        /*
         * Increment the counter of registered wakeup events and decrement the
         * couter of wakeup events in progress simultaneously.
@@ -718,6 +734,34 @@ bool pm_save_wakeup_count(unsigned int count)
        return events_check_enabled;
 }
 
+#ifdef CONFIG_PM_AUTOSLEEP
+/**
+ * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources.
+ * @enabled: Whether to set or to clear the autosleep_enabled flags.
+ */
+void pm_wakep_autosleep_enabled(bool set)
+{
+       struct wakeup_source *ws;
+       ktime_t now = ktime_get();
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+               spin_lock_irq(&ws->lock);
+               if (ws->autosleep_enabled != set) {
+                       ws->autosleep_enabled = set;
+                       if (ws->active) {
+                               if (set)
+                                       ws->start_prevent_time = now;
+                               else
+                                       update_prevent_sleep_time(ws, now);
+                       }
+               }
+               spin_unlock_irq(&ws->lock);
+       }
+       rcu_read_unlock();
+}
+#endif /* CONFIG_PM_AUTOSLEEP */
+
 static struct dentry *wakeup_sources_stats_dentry;
 
 /**
@@ -733,28 +777,37 @@ static int print_wakeup_source_stats(struct seq_file *m,
        ktime_t max_time;
        unsigned long active_count;
        ktime_t active_time;
+       ktime_t prevent_sleep_time;
        int ret;
 
        spin_lock_irqsave(&ws->lock, flags);
 
        total_time = ws->total_time;
        max_time = ws->max_time;
+       prevent_sleep_time = ws->prevent_sleep_time;
        active_count = ws->active_count;
        if (ws->active) {
-               active_time = ktime_sub(ktime_get(), ws->last_time);
+               ktime_t now = ktime_get();
+
+               active_time = ktime_sub(now, ws->last_time);
                total_time = ktime_add(total_time, active_time);
                if (active_time.tv64 > max_time.tv64)
                        max_time = active_time;
+
+               if (ws->autosleep_enabled)
+                       prevent_sleep_time = ktime_add(prevent_sleep_time,
+                               ktime_sub(now, ws->start_prevent_time));
        } else {
                active_time = ktime_set(0, 0);
        }
 
        ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t"
-                       "%lld\t\t%lld\t\t%lld\t\t%lld\n",
+                       "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
                        ws->name, active_count, ws->event_count,
                        ws->wakeup_count, ws->expire_count,
                        ktime_to_ms(active_time), ktime_to_ms(total_time),
-                       ktime_to_ms(max_time), ktime_to_ms(ws->last_time));
+                       ktime_to_ms(max_time), ktime_to_ms(ws->last_time),
+                       ktime_to_ms(prevent_sleep_time));
 
        spin_unlock_irqrestore(&ws->lock, flags);
 
@@ -771,7 +824,7 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
 
        seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
                "expire_count\tactive_since\ttotal_time\tmax_time\t"
-               "last_change\n");
+               "last_change\tprevent_suspend_time\n");
 
        rcu_read_lock();
        list_for_each_entry_rcu(ws, &wakeup_sources, entry)
index 5285317a612aa144d3510936d8d160120f0c2c40..569781faa5042a7930e082a74985bceac6f89786 100644 (file)
@@ -34,6 +34,7 @@
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
+ * @prevent_sleep_time: Total time this source has been preventing autosleep.
  * @event_count: Number of signaled wakeup events.
  * @active_count: Number of times the wakeup sorce was activated.
  * @relax_count: Number of times the wakeup sorce was deactivated.
@@ -51,12 +52,15 @@ struct wakeup_source {
        ktime_t total_time;
        ktime_t max_time;
        ktime_t last_time;
+       ktime_t start_prevent_time;
+       ktime_t prevent_sleep_time;
        unsigned long           event_count;
        unsigned long           active_count;
        unsigned long           relax_count;
        unsigned long           expire_count;
        unsigned long           wakeup_count;
        bool                    active:1;
+       bool                    autosleep_enabled:1;
 };
 
 #ifdef CONFIG_PM_SLEEP
index 76b7ec7d3a817ca451bc6566883f5ba1d2084e50..cd83059fb592c7464fc2e812d92e315fe7c77381 100644 (file)
@@ -358,6 +358,7 @@ extern bool events_check_enabled;
 extern bool pm_wakeup_pending(void);
 extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 extern bool pm_save_wakeup_count(unsigned int count);
+extern void pm_wakep_autosleep_enabled(bool set);
 
 static inline void lock_system_sleep(void)
 {
index 42348e3589d3a55577182477d4711955223f156b..ca304046d9e2b60c35c75eac43f5c3efcf2d0d01 100644 (file)
@@ -101,8 +101,12 @@ int pm_autosleep_set_state(suspend_state_t state)
 
        __pm_relax(autosleep_ws);
 
-       if (state > PM_SUSPEND_ON)
+       if (state > PM_SUSPEND_ON) {
+               pm_wakep_autosleep_enabled(true);
                queue_up_suspend_work();
+       } else {
+               pm_wakep_autosleep_enabled(false);
+       }
 
        mutex_unlock(&autosleep_lock);
        return 0;