Skip to content

Commit

Permalink
PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Rafael J. Wysocki committed May 1, 2012
1 parent 7483b4a commit 5585094
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 5 deletions.
11 changes: 11 additions & 0 deletions Documentation/ABI/testing/sysfs-devices-power
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
24 changes: 24 additions & 0 deletions drivers/base/power/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
};
Expand Down
61 changes: 57 additions & 4 deletions drivers/base/power/wakeup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

/**
Expand All @@ -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);

Expand All @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions include/linux/pm_wakeup.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
6 changes: 5 additions & 1 deletion kernel/power/autosleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 5585094

Please sign in to comment.