PM: Implement autosleep and "wake locks"
Subsystems (8)
cover PM: Implement autosleep and "wake locks"
Hi all,
This series tests the theory that the easiest way to sell a once rejected feature is to advertise it under a different name.
Well, there actually are two different features, although they are closely related to each other. First, patch [6/8] introduces a feature that allows the kernel to trigger system suspend (or more generally a transition into a sleep state) whenever there are no active wakeup sources (no, they aren't called wakelocks). It is called "autosleep" here, but it was called a few different names in the past ("opportunistic suspend" was probably the most popular one). Second, patch [8/8] introduces "wake locks" that are, essentially, wakeup sources which may be created and manipulated by user space. Using them user space may control the autosleep feature introduced earlier.
This also is a kind of a proof of concept for the people who wanted me to show a kernel-based implementation of automatic suspend, so there you go. Please note, however, that it is done so that the user space "wake locks" interface is compatible with Android in support of its user space. I don't really like this interface, but since the Android's user space seems to rely on it, I'm fine with using it as is. YMMV.
Let me say a few words about every patch in the series individually.
[1/8] - This really is a bug fix, so it's v3.4 material. Nobody has stepped
on this bug so far, but it should be fixed anyway.
[2/8] - This is a freezer cleanup, worth doing anyway IMO, so v3.4 material too.
[3/8] - This is something we can do no problem, although completely optional
without the autosleep feature. Rather necessary with it, though.
[4/8] - This kind of reintroduces my original idea of using a wait queue for
waiting until there are no wakeup events in progress. Alan convinced me that it would be better to poll the counter to prevent wakeup_source_deactivate() from having to call wake_up_all() occasionally (that may be costly in fast paths), but then quite some people told me that the wait queue migh be better. I think that the polling will make much less sense with autosleep and user space "wake locks". Anyway, [4/8] is something we can do without those things too.
The patches above were given Sign-off-by tags, because I think they make some sense regardless of the features introcuded by the remaining patches that in turn are total RFC.
[5/8] - This changes wakeup source statistics so that they are more similar to
the statistics collected for wakelocks on Android. The file those statistics may be read from is still located in debugfs, though (I don't think it belongs to proc and its name is different from the analogous Android's file name anyway). It could be done without autosleep, but then it would be a bit pointless. BTW, this changes interfaces that _in_ _theory_ may be used by someone, but I'm not aware of anyone using them. If you are one, I'll be pleased to learn about that, so please tell me who you are. :-)
[6/8] - Autosleep implementation. I think the changelog explains the idea
quite well and the code is really nothing special. It doesn't really add anything new to the kernel in terms of infrastructure etc., it just uses the existing stuff to implement an alternative method of triggering system sleep transitions. Note, though, that the interface here is different from the Android's one, because Android actually modifies /sys/power/state to trigger something called "early suspend" (that is never going to be implemented in the "stock" kernel as long as I have any influence on it) and we simply can't do that in the mainline.
[7/8] - This adds a wakeup source statistics that only makes sense with
autosleep and (I believe) is analogous to the Android's prevent_suspend_time statistics. Nothing really special, but I didn't want wakeup_source_activate/deactivate() to take a common lock to avoid congestion.
[8/8] - This adds a user space interface to create, activate and deactivate
wakeup sources. Since the files it consists of are called wake_lock and wake_unlock, to follow Android, the objects the wakeup sources are wrapped into are called "wakelocks" (for added confusion). Since the interface doesn't provide any means to destroy those "wakelocks", I added a garbage collection mechanism to get rid of the unused ones, if any. I also tought it might be a good idea to put a limit on the number of those things that user space can operate simultaneously, so I did that too.
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
Thanks,
Rafael
PATCH 1/8 PM / Sleep: Initialize wakeup source locks in wakeup_source_add()
From: Rafael J. Wysocki <[email protected]>
Initialize wakeup source locks in wakeup_source_add() instead of wakeup_source_create(), because otherwise the locks of the wakeup sources that haven't been allocated with wakeup_source_create() aren't initialized and handled properly.
--- drivers/base/power/wakeup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/base/power/wakeup.c +1 -1
@@ -64,7 +64,6 @@ struct wakeup_source *wakeup_source_creaif (!ws)return NULL ; -spin_lock_init (&ws->lock);if (name) ws->name =kstrdup (name,GFP_KERNEL );
@@ -105,6 +104,7 @@ void wakeup_source_add(struct wakeup_souif (WARN_ON (!ws))return ; +spin_lock_init (&ws->lock);setup_timer (&ws->timer, pm_wakeup_timer_fn, (unsigned long )ws); ws->active = false;
Re: [PATCH 1/8] PM / Sleep: Initialize wakeup source locks in wakeup_source_add()
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Initialize wakeup source locks in
wakeup_source_add()instead ofwakeup_source_create(), because otherwise the locks of the wakeup sources that haven't been allocated withwakeup_source_create()aren't initialized and handled properly.Signed-off-by Rafael J. Wysocki <[email protected]>
Ah, I've shot myself in the foot before, forgetting to init the wakeup source, so this should be good. Although, would a WARN_ON be better then just initializing the lock in add? That way bad behavior is more likely to be corrected, rather then just ignored.
thanks
-john
Re: [PATCH 1/8] PM / Sleep: Initialize wakeup source locks in wakeup_source_add()
Quoted 8 lines · John Stultz
Quoted 7 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Initialize wakeup source locks in
wakeup_source_add()instead ofwakeup_source_create(), because otherwise the locks of the wakeup sources that haven't been allocated withwakeup_source_create()aren't initialized and handled properly.
Quoted 6 lines
Quoted 1 line
Signed-off-by Rafael J. Wysocki <[email protected]>Ah, I've shot myself in the foot before, forgetting to init the wakeup source, so this should be good. Although, would a
WARN_ONbe better then just initializing the lock in add? That way bad behavior is more likely to be corrected, rather then just ignored.
Well, that's not bad behavior, since users are not supposed to open code wakeup source initialization. _add() is supposed to do the job (that's why I regard this one as a fix).
Thanks,
Rafael
PATCH 2/8 PM / Sleep: Do not check wakeup too often in try_to_freeze_tasks()
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Use the observation that it is more efficient to check the wakeup variable once before the loop reporting tasks that were not frozen in try_to_freeze_tasks() than to do that in every step of that loop.
--- kernel/power/process.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
kernel/power/process.c +9 -7
@@ -98,13 +98,15 @@ static int try_to_freeze_tasks(bool user elapsed_csecs /100 , elapsed_csecs %100 , todo - wq_busy, wq_busy); -read_lock (&tasklist_lock); - do_each_thread(g, p) { -if (!wakeup && !freezer_should_skip (p) && - p != current &&freezing (p) && !frozen (p)) -sched_show_task (p); - }while_each_thread (g, p); -read_unlock (&tasklist_lock); +if (!wakeup) { +read_lock (&tasklist_lock); + do_each_thread(g, p) { +if (p != current && !freezer_should_skip (p) + &&freezing (p) && !frozen (p)) +sched_show_task (p); + }while_each_thread (g, p); +read_unlock (&tasklist_lock); + } }else {printk ("(elapsed %d.%02d seconds) " , elapsed_csecs /100 , elapsed_csecs %100 );
PATCH 3/8 PM / Sleep: Look for wakeup events in later stages of device suspend
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Currently, the device suspend code only checks if there have been any wakeup events, and therefore the ongoing system transition to a sleep state should be aborted, during the first (i.e. "suspend") device suspend phase. However, wakeup events may be reported later as well, so it's reasonable to look for them in the in the subsequent (i.e. "late suspend" and "suspend noirq") phases.
--- drivers/base/power/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/base/power/main.c +10 -0
@@ -889,6 +889,11 @@ static int dpm_suspend_noirq(pm_message_if (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_noirq_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
@@ -962,6 +967,11 @@ static int dpm_suspend_late(pm_message_tif (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_late_early_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
PATCH 4/8 PM / Sleep: Use wait queue to signal "no wakeup events in progress"
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires pm_get_wakeup_count() to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.
--- drivers/base/power/wakeup.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/base/power/wakeup.c +14 -4
@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition.
@@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source.
@@ -84,7 +84,7 @@ void wakeup_source_destroy(struct wakeupwhile (ws->active) {spin_unlock_irq (&ws->lock); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); +schedule_timeout_interruptible (msecs_to_jiffies (100 ));spin_lock_irq (&ws->lock); }
@@ -411,6 +411,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;
@@ -444,6 +445,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr) +wake_up_all (&wakeup_count_wait_queue); }/**
@@ -624,14 +629,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Re: [PATCH 4/8] PM / Sleep: Use wait queue to signal "no wakeup events in progress"
Quoted 56 lines · On Tue, 7 Feb 2012 02:04:19 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
From: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires
pm_get_wakeup_count()to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/wakeup.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +4
-3@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. @@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. @@ -84,7 +84,7 @@ void wakeup_source_destroy(struct wakeupwhile (ws->active) {spin_unlock_irq (&ws->lock); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); +schedule_timeout_interruptible (msecs_to_jiffies (100 ));spin_lock_irq (&ws->lock); }@@ -411,6 +411,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 452
Quoted 8 lines
drivers/base/power/wakeup.c +4
-0@@ -444,6 +445,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr) +wake_up_all (&wakeup_count_wait_queue); }
Would it be worth making this:
if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) wake_up_all(&wakeup_count_wait_queue);
??
It would often save a spinlock.
Also was there a reason you used wake_up_all(). That is only really needed were EXCLUSIVE waits are happening, and there aren't any of those.
Thanks,
NeilBrown
Quoted 23 lines
drivers/base/power/wakeup.c +6
-1/** @@ -624,14 +629,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Re: [PATCH 4/8] PM / Sleep: Use wait queue to signal "no wakeup events in progress"
Quoted 69 lines · NeilBrown
Quoted 56 lines · On Tue, 7 Feb 2012 02:04:19 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
From: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires
pm_get_wakeup_count()to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/wakeup.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +4
-3@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. @@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. @@ -84,7 +84,7 @@ void wakeup_source_destroy(struct wakeupwhile (ws->active) {spin_unlock_irq (&ws->lock); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); +schedule_timeout_interruptible (msecs_to_jiffies (100 ));spin_lock_irq (&ws->lock); }@@ -411,6 +411,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 452
Quoted 8 lines
drivers/base/power/wakeup.c +4
-0@@ -444,6 +445,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr) +wake_up_all (&wakeup_count_wait_queue); }Would it be worth making this:
Quoted 5 lines
if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) wake_up_all(&wakeup_count_wait_queue);??
It would often save a spinlock.
Yes, good point. :-)
Quoted 2 lines
Also was there a reason you used
wake_up_all(). That is only really needed were EXCLUSIVE waits are happening, and there aren't any of those.
Right, I think wake_up() should be fine too.
Thanks,
Rafael
Re: [PATCH 4/8] PM / Sleep: Use wait queue to signal "no wakeup events in progress"
In reply to Rafael J. WysockiQuoted 39 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires
pm_get_wakeup_count()to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/wakeup.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +2
-2@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. @@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source.
drivers/base/power/wakeup.c void wakeup_source_destroy(struct wakeup · line 87
Quoted 5 lines
drivers/base/power/wakeup.c +1
-1@@ -84,7 +84,7 @@ void wakeup_source_destroy(struct wakeupwhile (ws->active) {spin_unlock_irq (&ws->lock); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); +schedule_timeout_interruptible (msecs_to_jiffies (100 ));
Nit/ style comment: how is replacing a TIMEOUT macro with a magic number an improvement. (maybe timeout is a un-helpful name but 100 isn't any better. )
Quoted 44 lines
drivers/base/power/wakeup.c +11
-1spin_lock_irq (&ws->lock); }@@ -411,6 +411,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;@@ -444,6 +445,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr) +wake_up_all (&wakeup_count_wait_queue); }/** @@ -624,14 +629,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
RFC PATCH 5/8 PM / Sleep: Change wakeup statistics
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have at the moment, so modify them to follow Android more closely.
This removes the struct wakeup_source's hit_cout field, which is very rough and therefore not very useful, and adds two new fields, wakeup_count and expire_count. The first one tracks how many times the wakeup source is activated with events_check_enabled set (which roughly corresponds to the situations when a system power transition to a sleep state is in progress and should be aborted by this wakeup source if it is the only active one at that time) and the second one is the number of times the wakeup source has been activated with a timeout that expired.
Additionally, the last_time field is now updated when the wakeup source is deactivated too (previously it was only updated during the wakeup source's activation), which seems to be what Android does with the analogous counter for wakelocks.
--- drivers/base/power/sysfs.c | 30 +++++++++++++++++++++++----- drivers/base/power/wakeup.c | 47 +++++++++++++++++--------------------------- include/linux/pm_wakeup.h | 12 +++++++---- 3 files changed, 52 insertions(+), 37 deletions(-)
include/linux/pm_wakeup.h +8 -4
@@ -33,12 +33,14 @@* * @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 activated last time. +* @last_time: Monotonic clock when the wakeup source's was touched last time. * @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. -* @hit_count: Number of times the wakeup sorce might abort system suspend. +* @expire_count: Number of times the wakeup source's timeout has expired. +* @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. +* @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source {char *name;
@@ -52,8 +54,10 @@ struct wakeup_source {unsigned long event_count;unsigned long active_count;unsigned long relax_count; -unsigned long hit_count; - unsigned int active:1; +unsigned long expire_count; +unsigned long wakeup_count; + bool active:1; + bool has_timeout:1; };#ifdef CONFIG_PM_SLEEP
drivers/base/power/wakeup.c +19 -28
111 lines not on this page · raw message
drivers/base/power/sysfs.c +25 -5
@@ -288,22 +288,41 @@ static ssize_t wakeup_active_count_show( static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); -static ssize_t wakeup_hit_count_show (struct device *dev, -struct device_attribute *attr,char *buf) +static ssize_t wakeup_wakeup_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +unsigned long count =0 ; +bool enabled = false; + +spin_lock_irq (&dev->power.lock); +if (dev->power.wakeup) { + count = dev->power.wakeup->wakeup_count; + enabled = true; + } +spin_unlock_irq (&dev->power.lock); +return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); +} + +static DEVICE_ATTR(wakeup_wakeup_count, 0444, wakeup_wakeup_count_show, NULL); + +static ssize_t wakeup_expire_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) {unsigned long count =0 ;bool enabled = false;spin_lock_irq (&dev->power.lock);if (dev->power.wakeup) { - count = dev->power.wakeup->hit_count; + count = dev->power.wakeup->expire_count; enabled = true; }spin_unlock_irq (&dev->power.lock);return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); } -static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); +static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);static ssize_t wakeup_active_show (struct device *dev,struct device_attribute *attr,char *buf)
@@ -460,7 +479,8 @@ static struct attribute *wakeup_attrs[] &dev_attr_wakeup.attr, &dev_attr_wakeup_count.attr, &dev_attr_wakeup_active_count.attr, - &dev_attr_wakeup_hit_count.attr, + &dev_attr_wakeup_wakeup_count.attr, + &dev_attr_wakeup_expire_count.attr, &dev_attr_wakeup_active.attr, &dev_attr_wakeup_total_time_ms.attr, &dev_attr_wakeup_max_time_ms.attr,
Re: [RFC][PATCH 5/8] PM / Sleep: Change wakeup statistics
Quoted 4 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have at the moment, so modify them to follow Android more closely.
...
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 448
Quoted 6 lines
drivers/base/power/wakeup.c +2
-0@@ -438,6 +444,11 @@ static void wakeup_source_deactivate(str if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration; + ws->last_time = now; + if (ws->has_timeout && time_after(jiffies, ws->timer_expires))
time_after_eq may work better (or increment the count from the timer). I applied this patch and the expire counts I see for wakeup-sources that always time-out do not match the active count.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 5/8] PM / Sleep: Change wakeup statistics
Quoted 6 lines · Arve Hjønnevåg
Quoted 4 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have at the moment, so modify them to follow Android more closely.
...
Quoted 8 lines
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 448
Quoted 6 lines
drivers/base/power/wakeup.c +2
-0@@ -438,6 +444,11 @@ static void wakeup_source_deactivate(strif (ktime_to_ns (duration) >ktime_to_ns (ws->max_time)) ws->max_time = duration; + ws->last_time = now; +if (ws->has_timeout &&time_after (jiffies, ws->timer_expires))
time_after_eqmay work better (or increment the count from the timer).
I think incrementing the count from the timer is a better approach.
Quoted 2 lines
I applied this patch and the expire counts I see for wakeup-sources that always time-out do not match the active count.
I see. The reason may also be that __pm_wakeup_event() increments ws->event_count even if the wakeup source is already active.
Thanks,
Rafael
Re: [RFC][PATCH 5/8] PM / Sleep: Change wakeup statistics
2012/2/15 Rafael J. Wysocki <[email protected]>:
Quoted 11 lines · Arve Hjønnevåg
Quoted 10 lines · Rafael J. Wysocki
Quoted 4 lines
From: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have at the moment, so modify them to follow Android more closely.
...
Quoted 4 lines
drivers/base/power/wakeup.c +0
-0@@ -438,6 +444,11 @@ static void wakeup_source_deactivate(str if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration;
Quoted 7 lines
Quoted 4 lines
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 448
Quoted 2 lines
drivers/base/power/wakeup.c +2
-0+ ws->last_time = now; + if (ws->has_timeout && time_after(jiffies, ws->timer_expires))
time_after_eqmay work better (or increment the count from the timer).I think incrementing the count from the timer is a better approach.
OK.
Quoted 6 lines
Quoted 2 lines
I applied this patch and the expire counts I see for wakeup-sources that always time-out do not match the active count.
I see. The reason may also be that
__pm_wakeup_event()incrementsws->event_counteven if the wakeup source is already active.
The active count, which is what I was looking at, only changes if it was not already active though.
Signature · 1 line
Arve Hjønnevåg
RFC PATCH 6/8 PM / Sleep: Implement opportunistic sleep
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, a freezable ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues it self after every execution until user space writes "off" to /sys/power/autosleep. That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- drivers/base/power/wakeup.c | 38 ++++++++------ include/linux/suspend.h | 13 ++++ kernel/power/Kconfig | 8 +++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 115 ++++++++++++++++++++++++++++++++++++++++++++ kernel/power/main.c | 93 +++++++++++++++++++++++++++++------ kernel/power/power.h | 18 ++++++ 7 files changed, 254 insertions(+), 32 deletions(-)
kernel/power/Makefile +1 -0
@@ -8,5 +8,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -269,3 +269,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -372,7 +372,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -423,6 +423,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +115 -0
115 lines not on this page · raw message
kernel/power/main.c +78 -15
141 lines not on this page · raw message
drivers/base/power/wakeup.c +22 -16
@@ -458,8 +458,10 @@ static void wakeup_source_deactivate(stratomic_add (MAX_IN_PROGRESS , &combined_event_count);split_counters (&cnt, &inpr); -if (!inpr) +if (!inpr) {wake_up_all (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/**
@@ -610,29 +612,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
RFC PATCH 6/8 PM / Sleep: Implement opportunistic sleep
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 14 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, a freezable ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues it self after every execution until user space writes "off" to/sys/power/autosleep. That work item enables the detection of wakeup events using the functions already defined indrivers/base/power/wakeup.c(with one small modification) and calls eitherpm_suspend(), orhibernate()to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
OK, so before somebody points that out to me, the completion was redundant (it was a leftover from one of the previous versions of the patch, sorry about that).
Moreover, try_to_suspend() is racy with respect to wakeup_count_store() (in theory, an automatic suspend without checking wakeup sources may happen if the latter is used carelessly when autosleep is enabled).
Thus below is an updated patch (it requires [8/8] to be updated too because of the changes in pm_autosleep_set_state(), but that's rather trivial).
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep.
That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- drivers/base/power/wakeup.c | 38 ++++++++------ include/linux/suspend.h | 13 ++++- kernel/power/Kconfig | 8 +++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ kernel/power/main.c | 105 ++++++++++++++++++++++++++++++++++------- kernel/power/power.h | 18 +++++++ 7 files changed, 262 insertions(+), 33 deletions(-)
kernel/power/Makefile +1 -0
@@ -8,5 +8,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -269,3 +269,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -372,7 +372,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -423,6 +423,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +112 -0
112 lines not on this page · raw message
kernel/power/main.c +89 -16
161 lines not on this page · raw message
drivers/base/power/wakeup.c +22 -16
@@ -458,8 +458,10 @@ static void wakeup_source_deactivate(stratomic_add (MAX_IN_PROGRESS , &combined_event_count);split_counters (&cnt, &inpr); -if (!inpr) +if (!inpr) {wake_up_all (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/**
@@ -610,29 +612,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
RFC PATCH 7/8 PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- drivers/base/power/wakeup.c | 61 +++++++++++++++++++++++++++++++++++++++++--- include/linux/pm_wakeup.h | 4 ++ include/linux/suspend.h | 1 kernel/power/autosleep.c | 2 + 4 files changed, 64 insertions(+), 4 deletions(-)
include/linux/pm_wakeup.h +4 -0
@@ -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,6 +52,8 @@ 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;
@@ -58,6 +61,7 @@ struct wakeup_source {unsigned long wakeup_count; bool active:1; bool has_timeout:1; + bool autosleep_enabled:1; };#ifdef CONFIG_PM_SLEEP
drivers/base/power/wakeup.c +57 -4
116 lines not on this page · raw message
include/linux/suspend.h +1 -0
@@ -374,6 +374,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 ) {
kernel/power/autosleep.c +2 -0
@@ -78,11 +78,13 @@ int pm_autosleep_set_state(suspend_stateif (state ==PM_SUSPEND_ON && autosleep_state !=PM_SUSPEND_ON ) { autosleep_state =PM_SUSPEND_ON ;__pm_relax (autosleep_ws); +pm_wakep_autosleep_enabled (false);mutex_unlock (&autosleep_lock);wait_for_completion (&suspend_completion); }else if (state >PM_SUSPEND_ON ) { autosleep_state = state;__pm_relax (autosleep_ws); +pm_wakep_autosleep_enabled (true);queue_up_suspend_work ();mutex_unlock (&autosleep_lock); }
RFC PATCH 8/8 PM / Sleep: Add user space interface for manipulating wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Android allows user space to manipulate wakelocks using two sysfs file located in /sys/power/, wake_lock and wake_unlock. Writing a wakelock name and optionally a timeout to the wake_lock file causes the wakelock whose name was written to be acquired (it is created before is necessary), optionally with the given timeout. Writing the name of a wakelock to wake_unlock causes that wakelock to be released.
Implement an analogous interface for user space using wakeup sources. Add the /sys/power/wake_lock and /sys/power/wake_unlock files allowing user space to create, activate and deactivate wakeup sources, such that writing a name and optionally a timeout to wake_lock causes the wakeup source of that name to be activated, optionally with the given timeout. If that wakeup source doesn't exist, it will be created and then activated. Writing a name to wake_unlock causes the wakeup source of that name, if there is one, to be deactivated. Wakeup sources created with the help of wake_lock that haven't been used for more than 5 minutes are garbage collected and destroyed. Moreover, there can be only WL_NUMBER_LIMIT wakeup sources created with the help of wake_lock present at a time.
The data type used to track wakeup sources created by user space is called "struct wakelock" to indicate the origins of this feature.
--- drivers/base/power/wakeup.c | 1 kernel/power/Kconfig | 8 + kernel/power/Makefile | 1 kernel/power/main.c | 41 ++++++++ kernel/power/power.h | 9 + kernel/power/wakelock.c | 218 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 278 insertions(+)
kernel/power/main.c +41 -0
@@ -415,6 +415,43 @@ static ssize_t autosleep_store(struct kopower_attr (autosleep);#endif /* CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS +static ssize_t wake_lock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, true); +} + +static ssize_t wake_lock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_lock (buf); +return error ? error : n; +} + +power_attr (wake_lock); + +static ssize_t wake_unlock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, false); +} + +static ssize_t wake_unlock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_unlock (buf); +return error ? error : n; +} + +power_attr (wake_unlock); + +#endif /* CONFIG_PM_WAKELOCKS */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE
@@ -471,6 +508,10 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr,#endif +#ifdef CONFIG_PM_WAKELOCKS + &wake_lock_attr.attr, + &wake_unlock_attr.attr, +#endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif
kernel/power/power.h +9 -0
@@ -287,3 +287,12 @@ static inline void pm_autosleep_unlock(vstatic inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; }#endif /* !CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS + +/* kernel/power/wakelock.c */ +extern ssize_t pm_show_wakelocks (char *buf,bool show_active); +extern int pm_wake_lock (const char *buf); +extern int pm_wake_unlock (const char *buf); + +#endif /* !CONFIG_PM_WAKELOCKS */
kernel/power/Kconfig +8 -0
@@ -111,6 +111,14 @@ config PM_AUTOSLEEP Allow the kernel to trigger a system transition into a global sleep state automatically whenever there are no active wakeup sources. +config PM_WAKELOCKS + bool "User space wakeup sources interface" + depends on PM_SLEEP + default n + ---help--- + Allow user space to create, activate and deactivate wakeup source + objects with the help of a sysfs-based interface. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/wakelock.c new file +218 -0
218 lines not on this page · raw message
kernel/power/Makefile +1 -0
@@ -9,5 +9,6 @@ obj-$(CONFIG_PM_TEST_SUSPEND) += suspend obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o +obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
drivers/base/power/wakeup.c +1 -0
@@ -107,6 +107,7 @@ void wakeup_source_add(struct wakeup_souspin_lock_init (&ws->lock);setup_timer (&ws->timer, pm_wakeup_timer_fn, (unsigned long )ws); ws->active = false; + ws->last_time =ktime_get ();spin_lock_irq (&events_lock);list_add_rcu (&ws->entry, &wakeup_sources);
Quoted 87 lines · Rafael J. Wysocki
Hi all,
This series tests the theory that the easiest way to sell a once rejected feature is to advertise it under a different name.
Well, there actually are two different features, although they are closely related to each other. First, patch [6/8] introduces a feature that allows the kernel to trigger system suspend (or more generally a transition into a sleep state) whenever there are no active wakeup sources (no, they aren't called wakelocks). It is called "autosleep" here, but it was called a few different names in the past ("opportunistic suspend" was probably the most popular one). Second, patch [8/8] introduces "wake locks" that are, essentially, wakeup sources which may be created and manipulated by user space. Using them user space may control the autosleep feature introduced earlier.
This also is a kind of a proof of concept for the people who wanted me to show a kernel-based implementation of automatic suspend, so there you go. Please note, however, that it is done so that the user space "wake locks" interface is compatible with Android in support of its user space. I don't really like this interface, but since the Android's user space seems to rely on it, I'm fine with using it as is. YMMV.
Let me say a few words about every patch in the series individually.
[1/8] - This really is a bug fix, so it's v3.4 material. Nobody has stepped
on this bug so far, but it should be fixed anyway.[2/8] - This is a freezer cleanup, worth doing anyway IMO, so v3.4 material too.
[3/8] - This is something we can do no problem, although completely optional
without the autosleep feature. Rather necessary with it, though.[4/8] - This kind of reintroduces my original idea of using a wait queue for
waiting until there are no wakeup events in progress. Alan convinced me that it would be better to poll the counter to preventwakeup_source_deactivate()from having to callwake_up_all()occasionally (that may be costly in fast paths), but then quite some people told me that the wait queue migh be better. I think that the polling will make much less sense with autosleep and user space "wake locks". Anyway, [4/8] is something we can do without those things too.The patches above were given Sign-off-by tags, because I think they make some sense regardless of the features introcuded by the remaining patches that in turn are total RFC.
[5/8] - This changes wakeup source statistics so that they are more similar to
the statistics collected for wakelocks on Android. The file those statistics may be read from is still located in debugfs, though (I don't think it belongs to proc and its name is different from the analogous Android's file name anyway). It could be done without autosleep, but then it would be a bit pointless. BTW, this changes interfaces that _in_ _theory_ may be used by someone, but I'm not aware of anyone using them. If you are one, I'll be pleased to learn about that, so please tell me who you are. :-)[6/8] - Autosleep implementation. I think the changelog explains the idea
quite well and the code is really nothing special. It doesn't really add anything new to the kernel in terms of infrastructure etc., it just uses the existing stuff to implement an alternative method of triggering system sleep transitions. Note, though, that the interface here is different from the Android's one, because Android actually modifies/sys/power/stateto trigger something called "early suspend" (that is never going to be implemented in the "stock" kernel as long as I have any influence on it) and we simply can't do that in the mainline.[7/8] - This adds a wakeup source statistics that only makes sense with
autosleep and (I believe) is analogous to the Android'sprevent_suspend_timestatistics. Nothing really special, but I didn't want wakeup_source_activate/deactivate() to take a common lock to avoid congestion.[8/8] - This adds a user space interface to create, activate and deactivate
wakeup sources. Since the files it consists of are calledwake_lockandwake_unlock, to follow Android, the objects the wakeup sources are wrapped into are called "wakelocks" (for added confusion). Since the interface doesn't provide any means to destroy those "wakelocks", I added a garbage collection mechanism to get rid of the unused ones, if any. I also tought it might be a good idea to put a limit on the number of those things that user space can operate simultaneously, so I did that too.All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Quoted 5 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
Ouch. Sorry for breaking the Greg's address. Please replace it with the correct one when you reply.
Thanks,
Rafael
Quoted 7 lines · On Tue, 7 Feb 2012 02:00:55 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Quoted 5 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
I've got opinions!!!
I'll try to avoid the obvious bike-shedding about interface design...
The key point I want to make is that doing this in the kernel has one very import difference to doing it in userspace (which, as you know, I prefer) which may not be obvious to everyone at first sight. So I will try to make it apparent.
In the user-space solution that we have previously discussed, it is only necessary for the kernel to hold a wakeup_source active until the event is *visible* to user-space. So a low level driver can queue e.g. an input event and then deactivate their wakeup_source. The event can remain in the input queue without any wakeup_source being active and there is no risk of going to sleep inappropriately.
This is because - in the user-space approach - user-space must effectively poll every source of interesting wakeup events between the last wakeup_source being deactivate and the next attempt to suspend. This poll will notice the event sitting in a queue so that a well-written user-space will not go to sleep but will read the event.
(Note that this 'poll-of-every-device' need not be expensive. It can be a single 'poll' or 'select' or even 'read' on a pollfd).
In the kernel based approach that you have presented this is not the case. As the kernel will initiate suspend the moment the last wakeup_source is released (with no polling of other queues), there must be an unbroken chain of wakeup_sources from the initial interrupt all the way up to the user. In particular, any subsystem (such as 'input') must hold a wakeup_source active as long as any designated 'wakeup event' is in any of its queues. This means that the subsystem must be able to differentiate wakeup events from non-wakeup events.
This might be easy (maybe "all events are wakeup events" or "all events on this queue are wakeup events") but it is not obvious to me that that is the case.
To summarise: for this solution to be effective it also requires that
1/ every subsystem that carries wakeup events must know about wakeup_sources
and must activate/deactivate them as events are queued/dequeued.
2/ these subsystems must be able to differentiate between wakeup events and
non-wakeup events, and this must be a configurable decision.
Currently, understanding wakeup events is restricted to:
- drivers that are capable of configuring wakeup
- user-space which cares about wakeup
The proposed solution adds:
- intermediate subsystems which might queue wakeup events
I think that is a significant addition to make and not one to be made lightly. It might end up adding more code than you thought it would be :-)
Thanks for the opportunity to comment,
NeilBrown
Hi,
Quoted 10 lines · NeilBrown
Quoted 7 lines · On Tue, 7 Feb 2012 02:00:55 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Quoted 7 lines
Quoted 5 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
I've got opinions!!!
Good! :-)
It seems that no one else has.
Quoted 7 lines
I'll try to avoid the obvious bike-shedding about interface design...
The key point I want to make is that doing this in the kernel has one very import difference to doing it in userspace (which, as you know, I prefer) which may not be obvious to everyone at first sight. So I will try to make it apparent.
Quoted 13 lines
In the user-space solution that we have previously discussed, it is only necessary for the kernel to hold a
wakeup_sourceactive until the event is *visible* to user-space. So a low level driver can queue e.g. an input event and then deactivate theirwakeup_source. The event can remain in the input queue without anywakeup_sourcebeing active and there is no risk of going to sleep inappropriately.
This is because - in the user-space approach - user-space must effectively poll every source of interesting wakeup events between the lastwakeup_sourcebeing deactivate and the next attempt to suspend. This poll will notice the event sitting in a queue so that a well-written user-space will not go to sleep but will read the event.
(Note that this 'poll-of-every-device' need not be expensive. It can be a single 'poll' or 'select' or even 'read' on a pollfd).
So I see one little problem with that, which is that you'd need to teach user space developers what to do an how to do that correctly.
Also, when you say "user space", it isn't exactly clear whether you mean a power manager (that would carry out the attmepts to suspend) or applications (that would need to communicate with the power manager to let it know what they are doing). This is important, because in general, before deactivating a wakeup source the kernel subsystem should know that the associated event has become visible not only to the "polling" application, but also (perhaps indirectly) to the power manager, so that it doesn't trigger suspend too early.
Quoted 18 lines
In the kernel based approach that you have presented this is not the case. As the kernel will initiate suspend the moment the last
wakeup_sourceis released (with no polling of other queues), there must be an unbroken chain ofwakeup_sourcesfrom the initial interrupt all the way up to the user. In particular, any subsystem (such as 'input') must hold awakeup_sourceactive as long as any designated 'wakeup event' is in any of its queues. This means that the subsystem must be able to differentiate wakeup events from non-wakeup events.
This might be easy (maybe "all events are wakeup events" or "all events on this queue are wakeup events") but it is not obvious to me that that is the case.To summarise: for this solution to be effective it also requires that
1/ every subsystem that carries wakeup events must know aboutwakeup_sources
and must activate/deactivate them as events are queued/dequeued.
2/ these subsystems must be able to differentiate between wakeup events and
non-wakeup events, and this must be a configurable decision.
Quoted 8 lines
Currently, understanding wakeup events is restricted to:
- drivers that are capable of configuring wakeup
- user-space which cares about wakeup
The proposed solution adds:
- intermediate subsystems which might queue wakeup eventsI think that is a significant addition to make and not one to be made lightly. It might end up adding more code than you thought it would be :-)
I'm aware of that and I expect people to come up with patches adding the handling of wakeup events to a number of subsystems (this is kind of needed regardless of autosleep if we want to be sure that user space has actually consumed events we want it to take from us before suspending). However, I'm not expecting that to be a lot of code (I think we both can only speculate about that at this point) and those subsystems have maintainers and the decision whether or not to take that code is theirs.
That may be a long process, but at least we can see from Android what's needed and where.
Still, the point here is to give people something to start with so that they can take the Android user space, test it against the mainline and see what doesn't work and why and come up with fixes. Perhaps they will have better ideas than we think right now, but surely nothing more is going to happen without this starting point.
I'd like us and Android to use the same low-level data structures for power management and the same API eventually, at least for drivers. This is not the case at the moment and it's actively hurting us as a project quite a bit. If Android needs to add patches on top of whatever we have to get the desired functionality, I'm fine with that, as long as they don't require drivers to use APIs that are incompatible with the mainline. Insisting that Android should use a user-space-based autosleep implementation wouldn't help at all, because realistically this isn't going to happen.
Quoted 1 line
Thanks for the opportunity to comment,
No need to thank for that, it's Open Source after all ...
Thanks,
Rafael
Quoted 17 lines · Rafael J. Wysocki
Hi,
Quoted 14 lines · NeilBrown
Quoted 11 lines · On Tue, 7 Feb 2012 02:00:55 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
IOW, I need to know your opinions!
I've got opinions!!!
Good! :-)
It seems that no one else has.
I'm sorry I've been really bad this last year about my email latency.
Quoted 24 lines
Quoted 7 lines
I'll try to avoid the obvious bike-shedding about interface design...
The key point I want to make is that doing this in the kernel has one very import difference to doing it in userspace (which, as you know, I prefer) which may not be obvious to everyone at first sight. So I will try to make it apparent.
Quoted 13 lines
In the user-space solution that we have previously discussed, it is only necessary for the kernel to hold a
wakeup_sourceactive until the event is *visible* to user-space. So a low level driver can queue e.g. an input event and then deactivate theirwakeup_source. The event can remain in the input queue without anywakeup_sourcebeing active and there is no risk of going to sleep inappropriately.
This is because - in the user-space approach - user-space must effectively poll every source of interesting wakeup events between the lastwakeup_sourcebeing deactivate and the next attempt to suspend. This poll will notice the event sitting in a queue so that a well-written user-space will not go to sleep but will read the event.
(Note that this 'poll-of-every-device' need not be expensive. It can be a single 'poll' or 'select' or even 'read' on a pollfd).So I see one little problem with that, which is that you'd need to teach user space developers what to do an how to do that correctly.
Quoted 8 lines
Also, when you say "user space", it isn't exactly clear whether you mean a power manager (that would carry out the attmepts to suspend) or applications (that would need to communicate with the power manager to let it know what they are doing). This is important, because in general, before deactivating a wakeup source the kernel subsystem should know that the associated event has become visible not only to the "polling" application, but also (perhaps indirectly) to the power manager, so that it doesn't trigger suspend too early.
yup, an explicit user mode acknowledgment of the wake event would be appropriate.
Quoted 44 lines
Quoted 18 lines
In the kernel based approach that you have presented this is not the case. As the kernel will initiate suspend the moment the last
wakeup_sourceis released (with no polling of other queues), there must be an unbroken chain ofwakeup_sourcesfrom the initial interrupt all the way up to the user. In particular, any subsystem (such as 'input') must hold awakeup_sourceactive as long as any designated 'wakeup event' is in any of its queues. This means that the subsystem must be able to differentiate wakeup events from non-wakeup events.
This might be easy (maybe "all events are wakeup events" or "all events on this queue are wakeup events") but it is not obvious to me that that is the case.To summarise: for this solution to be effective it also requires that
1/ every subsystem that carries wakeup events must know aboutwakeup_sources
and must activate/deactivate them as events are queued/dequeued.
2/ these subsystems must be able to differentiate between wakeup events and
non-wakeup events, and this must be a configurable decision.Quoted 8 lines
Currently, understanding wakeup events is restricted to:
- drivers that are capable of configuring wakeup
- user-space which cares about wakeup
The proposed solution adds:
- intermediate subsystems which might queue wakeup eventsI think that is a significant addition to make and not one to be made lightly. It might end up adding more code than you thought it would be :-)
I'm aware of that and I expect people to come up with patches adding the handling of wakeup events to a number of subsystems (this is kind of needed regardless of autosleep if we want to be sure that user space has actually consumed events we want it to take from us before suspending). However, I'm not expecting that to be a lot of code (I think we both can only speculate about that at this point) and those subsystems have maintainers and the decision whether or not to take that code is theirs.
That may be a long process, but at least we can see from Android what's needed and where.
Still, the point here is to give people something to start with so that they can take the Android user space, test it against the mainline and see what doesn't work and why and come up with fixes. Perhaps they will have better ideas than we think right now, but surely nothing more is going to happen without this starting point.
Quoted 8 lines
I'd like us and Android to use the same low-level data structures for power management and the same API eventually, at least for drivers. This is not the case at the moment and it's actively hurting us as a project quite a bit. If Android needs to add patches on top of whatever we have to get the desired functionality, I'm fine with that, as long as they don't require drivers to use APIs that are incompatible with the mainline. Insisting that Android should use a user-space-based autosleep implementation wouldn't help at all, because realistically this isn't going to happen.
why not? I don't think having the PMS explicitly acknowledge a wake event is a big ask at all.
--mark
Quoted 6 lines
Quoted 1 line
Thanks for the opportunity to comment,
No need to thank for that, it's Open Source after all ...
Thanks,
Rafael
Quoted 1 line · mark gross
On Fri, Feb 10, 2012 at 01:44:10AM +0100, Rafael J. Wysocki wrote:
[...]
Quoted 11 lines
Quoted 8 lines
I'd like us and Android to use the same low-level data structures for power management and the same API eventually, at least for drivers. This is not the case at the moment and it's actively hurting us as a project quite a bit. If Android needs to add patches on top of whatever we have to get the desired functionality, I'm fine with that, as long as they don't require drivers to use APIs that are incompatible with the mainline. Insisting that Android should use a user-space-based autosleep implementation wouldn't help at all, because realistically this isn't going to happen.
why not? I don't think having the PMS explicitly acknowledge a wake event is a big ask at all.
I'd like to hear what the Android people think about that, but somehow it seems to me they won't like it. :-)
Thanks,
Rafael
Quoted 12 lines · Rafael J. Wysocki
Quoted 1 line · mark gross
On Fri, Feb 10, 2012 at 01:44:10AM +0100, Rafael J. Wysocki wrote:
[...]
Quoted 9 lines
Quoted 8 lines
I'd like us and Android to use the same low-level data structures for power management and the same API eventually, at least for drivers. This is not the case at the moment and it's actively hurting us as a project quite a bit. If Android needs to add patches on top of whatever we have to get the desired functionality, I'm fine with that, as long as they don't require drivers to use APIs that are incompatible with the mainline. Insisting that Android should use a user-space-based autosleep implementation wouldn't help at all, because realistically this isn't going to happen.
Quoted 6 lines
Quoted 2 lines
why not? I don't think having the PMS explicitly acknowledge a wake event is a big ask at all.
I'd like to hear what the Android people think about that, but somehow it seems to me they won't like it. :-)
Correct.
The android power manager service does not handle wake events and therefore does not know when it is safe to acknowledge a wake event (assuming this acknowledgement re-triggers suspend). Other components handle the event and only notify the power manager if the event should change a state (e.g. turn the screen on). Some wake events, like the alarm used for battery monitoring, don't signal user space at all if the user visible state did not change. Other wake events are processed by lower level user-space services than the system-server where the power manager runs.
Signature · 1 line
Arve Hjønnevåg
Quoted 22 lines · Arve Hjønnevåg
Quoted 12 lines · Rafael J. Wysocki
Quoted 1 line · mark gross
On Fri, Feb 10, 2012 at 01:44:10AM +0100, Rafael J. Wysocki wrote:
[...]
Quoted 9 lines
Quoted 8 lines
I'd like us and Android to use the same low-level data structures for power management and the same API eventually, at least for drivers. This is not the case at the moment and it's actively hurting us as a project quite a bit. If Android needs to add patches on top of whatever we have to get the desired functionality, I'm fine with that, as long as they don't require drivers to use APIs that are incompatible with the mainline. Insisting that Android should use a user-space-based autosleep implementation wouldn't help at all, because realistically this isn't going to happen.
Quoted 6 lines
Quoted 2 lines
why not? I don't think having the PMS explicitly acknowledge a wake event is a big ask at all.
I'd like to hear what the Android people think about that, but somehow it seems to me they won't like it. :-)
Correct.
Quoted 9 lines
The android power manager service does not handle wake events and therefore does not know when it is safe to acknowledge a wake event (assuming this acknowledgement re-triggers suspend). Other components handle the event and only notify the power manager if the event should change a state (e.g. turn the screen on). Some wake events, like the alarm used for battery monitoring, don't signal user space at all if the user visible state did not change. Other wake events are processed by lower level user-space services than the system-server where the power manager runs.
So you are all good with the wake event suspend race condition never ever getting corrected or the fact that we have to sprinkle overlapping kernel wake locks up and down the stack if we want to attempt to implement correct code or that there is *no* way to deal with the hand off of a wake lock critical section between kernel and user mode on wake events without having a somewhat arbitrary time out wake lock dropping in kernel mode?
Fine, if you don't like having the PMS ack wake events how about having the services that handle them do it?
The basic problem with wake locks is that there is no explicit wake event acknowledgment required before re-suspending. How about helping us come up with a solution to that.
--mark
Quoted 2 lines
Signature · 1 line
Arve Hjønnevåg
Quoted 25 lines · NeilBrown
Quoted 7 lines · On Tue, 7 Feb 2012 02:00:55 +0100 "Rafael J. Wysocki" <[email protected]> wrote:
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Quoted 5 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
I've got opinions!!!
I'll try to avoid the obvious bike-shedding about interface design...
The key point I want to make is that doing this in the kernel has one very import difference to doing it in userspace (which, as you know, I prefer) which may not be obvious to everyone at first sight. So I will try to make it apparent.
Quoted 11 lines
In the user-space solution that we have previously discussed, it is only necessary for the kernel to hold a
wakeup_sourceactive until the event is *visible* to user-space. So a low level driver can queue e.g. an input event and then deactivate theirwakeup_source. The event can remain in the input queue without anywakeup_sourcebeing active and there is no risk of going to sleep inappropriately.
This is because - in the user-space approach - user-space must effectively poll every source of interesting wakeup events between the lastwakeup_sourcebeing deactivate and the next attempt to suspend. This poll will notice the event sitting in a queue so that a well-written user-space will not go to sleep but will read the event.
<sarcasm>
its on running on 100's of millions of devices today... It must be well written. Right?
</sarcasm>
Quoted 14 lines
single 'poll' or 'select' or even 'read' on a pollfd).
In the kernel based approach that you have presented this is not the case. As the kernel will initiate suspend the moment the last
wakeup_sourceis released (with no polling of other queues), there must be an unbroken chain ofwakeup_sourcesfrom the initial interrupt all the way up to the user. In particular, any subsystem (such as 'input') must hold awakeup_sourceactive as long as any designated 'wakeup event' is in any of its queues. This means that the subsystem must be able to differentiate wakeup events from non-wakeup events.
This might be easy (maybe "all events are wakeup events" or "all events on this queue are wakeup events") but it is not obvious to me that that is the case.
And this brings us to a wake acknowledgement of wake events from user mode before re-suspending type of design.
Quoted 6 lines
To summarise: for this solution to be effective it also requires that
1/ every subsystem that carries wakeup events must know aboutwakeup_sources
and must activate/deactivate them as events are queued/dequeued.
2/ these subsystems must be able to differentiate between wakeup events and
non-wakeup events, and this must be a configurable decision.
Quoted 8 lines
Currently, understanding wakeup events is restricted to:
- drivers that are capable of configuring wakeup
- user-space which cares about wakeup
The proposed solution adds:
- intermediate subsystems which might queue wakeup eventsI think that is a significant addition to make and not one to be made lightly. It might end up adding more code than you thought it would be :-)
you mean wake lock-itis sprinkling time out wake locks all over the place?
--mark
Quoted 2 lines
Thanks for the opportunity to comment,
NeilBrown
Quoted 55 lines · Rafael J. Wysocki
Hi all,
This series tests the theory that the easiest way to sell a once rejected feature is to advertise it under a different name.
Well, there actually are two different features, although they are closely related to each other. First, patch [6/8] introduces a feature that allows the kernel to trigger system suspend (or more generally a transition into a sleep state) whenever there are no active wakeup sources (no, they aren't called wakelocks). It is called "autosleep" here, but it was called a few different names in the past ("opportunistic suspend" was probably the most popular one). Second, patch [8/8] introduces "wake locks" that are, essentially, wakeup sources which may be created and manipulated by user space. Using them user space may control the autosleep feature introduced earlier.
This also is a kind of a proof of concept for the people who wanted me to show a kernel-based implementation of automatic suspend, so there you go. Please note, however, that it is done so that the user space "wake locks" interface is compatible with Android in support of its user space. I don't really like this interface, but since the Android's user space seems to rely on it, I'm fine with using it as is. YMMV.
Let me say a few words about every patch in the series individually.
[1/8] - This really is a bug fix, so it's v3.4 material. Nobody has stepped
on this bug so far, but it should be fixed anyway.[2/8] - This is a freezer cleanup, worth doing anyway IMO, so v3.4 material too.
[3/8] - This is something we can do no problem, although completely optional
without the autosleep feature. Rather necessary with it, though.[4/8] - This kind of reintroduces my original idea of using a wait queue for
waiting until there are no wakeup events in progress. Alan convinced me that it would be better to poll the counter to preventwakeup_source_deactivate()from having to callwake_up_all()occasionally (that may be costly in fast paths), but then quite some people told me that the wait queue migh be better. I think that the polling will make much less sense with autosleep and user space "wake locks". Anyway, [4/8] is something we can do without those things too.The patches above were given Sign-off-by tags, because I think they make some sense regardless of the features introcuded by the remaining patches that in turn are total RFC.
[5/8] - This changes wakeup source statistics so that they are more similar to
the statistics collected for wakelocks on Android. The file those statistics may be read from is still located in debugfs, though (I don't think it belongs to proc and its name is different from the analogous Android's file name anyway). It could be done without autosleep, but then it would be a bit pointless. BTW, this changes interfaces that _in_ _theory_ may be used by someone, but I'm not aware of anyone using them. If you are one, I'll be pleased to learn about that, so please tell me who you are. :-)
Quoted 9 lines
[6/8] - Autosleep implementation. I think the changelog explains the idea
quite well and the code is really nothing special. It doesn't really add anything new to the kernel in terms of infrastructure etc., it just uses the existing stuff to implement an alternative method of triggering system sleep transitions. Note, though, that the interface here is different from the Android's one, because Android actually modifies/sys/power/stateto trigger something called "early suspend" (that is never going to be implemented in the "stock" kernel as long as I have any influence on it) and we simply can't do that in the mainline.
dude early suspend is the hallmark of enlightend coding for implementing a kernel / user mode handshake to user mode when the display is turned off. How can you not like that shit?
Quoted 23 lines
[7/8] - This adds a wakeup source statistics that only makes sense with
autosleep and (I believe) is analogous to the Android'sprevent_suspend_timestatistics. Nothing really special, but I didn't want wakeup_source_activate/deactivate() to take a common lock to avoid congestion.[8/8] - This adds a user space interface to create, activate and deactivate
wakeup sources. Since the files it consists of are calledwake_lockandwake_unlock, to follow Android, the objects the wakeup sources are wrapped into are called "wakelocks" (for added confusion). Since the interface doesn't provide any means to destroy those "wakelocks", I added a garbage collection mechanism to get rid of the unused ones, if any. I also tought it might be a good idea to put a limit on the number of those things that user space can operate simultaneously, so I did that too.All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Quoted 5 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
IOW, I need to know your opinions!
my opinion is "sigh".
FWIW we need to bring Android wakelocks into the main line so we can fix them WRT wake event notification handling. But, I'll have to take a look at the patches to see if I still have heart burn over the race between wake sources and wake lock dropping in kernel mode.
/me goes and looks now....
--mark
Quoted 4 lines
Thanks,
Rafael
On Mon, Feb 6, 2012 at 5:00 PM, Rafael J. Wysocki <[email protected]> wrote:
...
Quoted 7 lines
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Replacing a working solution with an untested one takes time. That said, I have recently tried replacing all our kernel wake-locks with a thin wrapper around wake-sources. This appears to mostly work, but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init to wakeup_source_add still require the struct to be zero initialized and the name set manually.
I needed to use two wake-sources per wake-lock since calling __pm_stay_awake after __pm_wakeup_event on a wake-source does not cancel the timeout. Unless there is a reason to keep this behavior I would like __pm_stay_awake to cancel any active timeout.
Destroying a wake-source also has some problems. If you call wakeup_source_destroy it will spin forever if the wake-source is active without a timeout. And, if you call __pm_relax then wakeup_source_destroy it could free the wake-source memory while the timer function is still running. It also looks as if the wake_source can be immediately deactivated if you call __pm_wakeup_event at the same time as the previous timeout expired.
Signature · 1 line
Arve Hjønnevåg
Quoted 11 lines · Arve Hjønnevåg
On Mon, Feb 6, 2012 at 5:00 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
All in all, it's not as much code as I thought it would be and it seems to be relatively simple (which rises the question why the Android people didn't even _try_ to do something like this instead of slapping the "real" wakelocks onto the kernel FWIW). IMHO it doesn't add anything really new to the kernel, except for the user space interfaces that should be maintainable. At least I think I should be able to maintain them. :-)
Replacing a working solution with an untested one takes time.
Sure, that's pretty obvious. :-)
Quoted 2 lines
That said, I have recently tried replacing all our kernel wake-locks with a thin wrapper around wake-sources. This appears to mostly work,
Good!
Quoted 5 lines
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init to
wakeup_source_addstill require the struct to be zero initialized and the name set manually.
That should be easy to fix. What about the appended patch?
Quoted 4 lines
I needed to use two wake-sources per wake-lock since calling
__pm_stay_awakeafter__pm_wakeup_eventon a wake-source does not cancel the timeout. Unless there is a reason to keep this behavior I would like__pm_stay_awaketo cancel any active timeout.
That actually is a bug. At least it's not consistent with __pm_wakeup_event() that will replace the existing timeout with a new one.
I'll post a patch to fix that in the next couple of days, stay tuned. :-)
Quoted 5 lines
Destroying a wake-source also has some problems. If you call
wakeup_source_destroyit will spin forever if the wake-source is active without a timeout. And, if you call__pm_relaxthenwakeup_source_destroyit could free the wake-source memory while the timer function is still running.
This also is a bug that needs fixing anyway.
Quoted 2 lines
It also looks as if the
wake_sourcecan be immediately deactivated if you call__pm_wakeup_eventat the same time as the previous timeout expired.
Yes, there is a race window if the timer function has already started. It looks like I wanted to make it too simple. :-) Will fix.
Thanks,
Rafael
--- drivers/base/power/wakeup.c | 44 +++++++++++++++++++++++++++++++++++++------- include/linux/pm_wakeup.h | 9 +++++++++ 2 files changed, 46 insertions(+), 7 deletions(-)
drivers/base/power/wakeup.c +37 -7
@@ -53,6 +53,28 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_init - Initialize a struct wakeup_source object. +* @ws: Wakeup source to initialize. +* @name: Name of the new wakeup source. +*/ +int wakeup_source_init (struct wakeup_source *ws,const char *name) +{ +int ret =0 ; + +if (!ws) +return -EINVAL ; + +memset (ws,0 ,sizeof (*ws)); +if (name) { + ws->name =kstrdup (name,GFP_KERNEL ); +if (!ws->name) + ret = -ENOMEM ; + } +return ret; +} +EXPORT_SYMBOL_GPL (wakeup_source_init); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */
@@ -60,22 +82,20 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_init (ws, name);return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;
@@ -91,6 +111,16 @@ void wakeup_source_destroy(struct wakeupspin_unlock_irq (&ws->lock);kfree (ws->name); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +wakeup_source_drop (ws);kfree (ws); }EXPORT_SYMBOL_GPL (wakeup_source_destroy);
include/linux/pm_wakeup.h +9 -0
@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern int wakeup_source_init (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);
@@ -103,11 +105,18 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline int wakeup_source_init (struct wakeup_source *ws,const char *name) +{ +return -ENOSYS ; +} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}
2012/2/14 Rafael J. Wysocki <[email protected]>:
Quoted 11 lines · Arve Hjønnevåg
Quoted 7 lines · Rafael J. Wysocki
...
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init towakeup_source_addstill require the struct to be zero initialized and the name set manually.That should be easy to fix. What about the appended patch?
That works, but I still have to call more than one function before I can use the wakeup-source (wakeup_source_init and wakeup_source_add) and more than one function before I can free it (__pm_relax, wakeup_source_remove and wakeup_source_drop). Is there any reason to keep these separate?
Also, not copying the name when the caller provides the memory for the wakeup-source would be a closer match to the wakelock api. Most of our wakelocks pass a string constant as the name, and making a copy of that string is not useful. wake_lock_init is also safe to call from atomic context, but I don't know if anyone relies on this.
Signature · 1 line
Arve Hjønnevåg
Quoted 10 lines · Arve Hjønnevåg
2012/2/14 Rafael J. Wysocki <[email protected]>:
Quoted 9 lines · Arve Hjønnevåg
Quoted 7 lines · Rafael J. Wysocki
...
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init towakeup_source_addstill require the struct to be zero initialized and the name set manually.
Quoted 8 lines
Quoted 2 lines
That should be easy to fix. What about the appended patch?
That works, but I still have to call more than one function before I can use the wakeup-source (
wakeup_source_initandwakeup_source_add) and more than one function before I can free it (__pm_relax,wakeup_source_removeandwakeup_source_drop). Is there any reason to keep these separate?
Yes, there is. I think that wakeup_source_create/_destroy() should use the same initialization functions internally that will be used for externally allocated wakeup sources (to make sure that all wakeup source objects are initialized in exactly the same way).
Quoted 5 lines
Also, not copying the name when the caller provides the memory for the wakeup-source would be a closer match to the wakelock api. Most of our wakelocks pass a string constant as the name, and making a copy of that string is not useful.
wake_lock_initis also safe to call from atomic context, but I don't know if anyone relies on this.
OK, below is another go. It doesn't copy the name if wakeup_source_init() is used (which also does the _add this time). I think, though, that copying the name is generally safer, because someone might use wakeup_source_init() with the name string allocated on the stack or otherwise temporary, which would be a bug with the new version.
Thanks,
Rafael
--- drivers/base/power/wakeup.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/pm_wakeup.h | 20 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-)
drivers/base/power/wakeup.c +34 -7
@@ -53,6 +53,23 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_prepare - Prepare a new wakeup source for initialization. +* @ws: Wakeup source to prepare. +* @name: Pointer to the name of the new wakeup source. +* +* Callers must ensure that the @name string won't be freed when @ws is still in +* use. +*/ +void wakeup_source_prepare (struct wakeup_source *ws,const char *name) +{ +if (ws) { +memset (ws,0 ,sizeof (*ws)); + ws->name = name; + } +} +EXPORT_SYMBOL_GPL (wakeup_source_prepare); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */
@@ -60,31 +77,41 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_prepare (ws, name ?kstrdup (name,GFP_KERNEL ) :NULL );return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. * * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never * be run in parallel with this function for the same wakeup source object. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;del_timer_sync (&ws->timer);__pm_relax (ws); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +* +* Use only for wakeup source objects created with wakeup_source_create(). +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +wakeup_source_drop (ws);kfree (ws->name);kfree (ws); }
include/linux/pm_wakeup.h +20 -0
@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern void wakeup_source_prepare (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);
@@ -103,11 +105,16 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline void wakeup_source_prepare (struct wakeup_source *ws, +const char *name) {} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}
@@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struc#endif /* !CONFIG_PM_SLEEP */ +static inline void wakeup_source_init (struct wakeup_source *ws, +const char *name) +{ +wakeup_source_prepare (ws, name); +wakeup_source_add (ws); +} + +static inline void wakeup_source_trash (struct wakeup_source *ws) +{ +wakeup_source_remove (ws); +wakeup_source_drop (ws); +} +#endif /* _LINUX_PM_WAKEUP_H */
Quoted 31 lines · Rafael J. Wysocki
Quoted 10 lines · Arve Hjønnevåg
2012/2/14 Rafael J. Wysocki <[email protected]>:
Quoted 9 lines · Arve Hjønnevåg
Quoted 7 lines · Rafael J. Wysocki
...
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init towakeup_source_addstill require the struct to be zero initialized and the name set manually.Quoted 8 lines
Quoted 2 lines
That should be easy to fix. What about the appended patch?
That works, but I still have to call more than one function before I can use the wakeup-source (
wakeup_source_initandwakeup_source_add) and more than one function before I can free it (__pm_relax,wakeup_source_removeandwakeup_source_drop). Is there any reason to keep these separate?Yes, there is. I think that wakeup_source_create/_destroy() should use the same initialization functions internally that will be used for externally allocated wakeup sources (to make sure that all wakeup source objects are initialized in exactly the same way).
Quoted 5 lines
Also, not copying the name when the caller provides the memory for the wakeup-source would be a closer match to the wakelock api. Most of our wakelocks pass a string constant as the name, and making a copy of that string is not useful.
wake_lock_initis also safe to call from atomic context, but I don't know if anyone relies on this.
Quoted 5 lines
OK, below is another go. It doesn't copy the name if
wakeup_source_init()is used (which also does the_addthis time). I think, though, that copying the name is generally safer, because someone might usewakeup_source_init()with the name string allocated on the stack or otherwise temporary, which would be a bug with the new version.
So, is the new version more suitable than the previous one?
Rafael
Quoted 138 lines
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/wakeup.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/pm_wakeup.h | 20 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-)Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +34
-7@@ -53,6 +53,23 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_prepare - Prepare a new wakeup source for initialization. +* @ws: Wakeup source to prepare. +* @name: Pointer to the name of the new wakeup source. +* +* Callers must ensure that the @name string won't be freed when @ws is still in +* use. +*/ +void wakeup_source_prepare (struct wakeup_source *ws,const char *name) +{ +if (ws) { +memset (ws,0 ,sizeof (*ws)); + ws->name = name; + } +} +EXPORT_SYMBOL_GPL (wakeup_source_prepare); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */ @@ -60,31 +77,41 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_prepare (ws, name ?kstrdup (name,GFP_KERNEL ) :NULL );return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. * * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never * be run in parallel with this function for the same wakeup source object. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;del_timer_sync (&ws->timer);__pm_relax (ws); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +* +* Use only for wakeup source objects created with wakeup_source_create(). +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +wakeup_source_drop (ws);kfree (ws->name);kfree (ws); }Index:
linux/include/linux/pm_wakeup.h
===================================================================include/linux/pm_wakeup.h +20
-1@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern void wakeup_source_prepare (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);@@ -103,11 +105,16 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline void wakeup_source_prepare (struct wakeup_source *ws, +const char *name) {} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}@@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struc#endif /* !CONFIG_PM_SLEEP */ +static inline void wakeup_source_init (struct wakeup_source *ws, +const char *name) +{ +wakeup_source_prepare (ws, name); +wakeup_source_add (ws); +} + +static inline void wakeup_source_trash (struct wakeup_source *ws) +{ +wakeup_source_remove (ws); +wakeup_source_drop (ws); +} +#endif /* _LINUX_PM_WAKEUP_H */ --To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
2012/2/16 Rafael J. Wysocki <[email protected]>:
...
Quoted 3 lines
So, is the new version more suitable than the previous one?
Yes, I think it is.
Signature · 1 line
Arve Hjønnevåg
PATCH 1/1 PM / Sleep: Add more wakeup source initialization routines
From: Rafael J. Wysocki <[email protected]>
The existing wakeup source initialization routines are not particularly useful for wakeup sources that aren't created by wakeup_source_create(), because their users have to open code filling the objects with zeros and setting their names. For this reason, introduce routines that can be used for initializing, for example, static wakeup source objects.
---
This patch is on top of the linux-next branch of the linux-pm tree.
Thanks,
Rafael
--- drivers/base/power/wakeup.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/pm_wakeup.h | 20 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-)
drivers/base/power/wakeup.c +34 -7
@@ -53,6 +53,23 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_prepare - Prepare a new wakeup source for initialization. +* @ws: Wakeup source to prepare. +* @name: Pointer to the name of the new wakeup source. +* +* Callers must ensure that the @name string won't be freed when @ws is still in +* use. +*/ +void wakeup_source_prepare (struct wakeup_source *ws,const char *name) +{ +if (ws) { +memset (ws,0 ,sizeof (*ws)); + ws->name = name; + } +} +EXPORT_SYMBOL_GPL (wakeup_source_prepare); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */
@@ -60,31 +77,41 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_prepare (ws, name ?kstrdup (name,GFP_KERNEL ) :NULL );return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. * * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never * be run in parallel with this function for the same wakeup source object. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;del_timer_sync (&ws->timer);__pm_relax (ws); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +* +* Use only for wakeup source objects created with wakeup_source_create(). +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +wakeup_source_drop (ws);kfree (ws->name);kfree (ws); }
include/linux/pm_wakeup.h +20 -0
@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern void wakeup_source_prepare (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);
@@ -103,11 +105,16 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline void wakeup_source_prepare (struct wakeup_source *ws, +const char *name) {} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}
@@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struc#endif /* !CONFIG_PM_SLEEP */ +static inline void wakeup_source_init (struct wakeup_source *ws, +const char *name) +{ +wakeup_source_prepare (ws, name); +wakeup_source_add (ws); +} + +static inline void wakeup_source_trash (struct wakeup_source *ws) +{ +wakeup_source_remove (ws); +wakeup_source_drop (ws); +} +#endif /* _LINUX_PM_WAKEUP_H */
PATCH 1/1 PM / Sleep: Add more wakeup source initialization routines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Add more wakeup source initialization routines
The existing wakeup source initialization routines are not particularly useful for wakeup sources that aren't created by wakeup_source_create(), because their users have to open code filling the objects with zeros and setting their names. For this reason, introduce routines that can be used for initializing, for example, static wakeup source objects.
---
The name member of struct wakeup_source has to be of type (const char *) due to the new dependencies between the arguments of the new initializers. That also reflects the fact that that string is not supposed to be modified.
Thanks,
Rafael
--- drivers/base/power/wakeup.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/pm_wakeup.h | 22 +++++++++++++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-)
drivers/base/power/wakeup.c +34 -7
@@ -53,6 +53,23 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_prepare - Prepare a new wakeup source for initialization. +* @ws: Wakeup source to prepare. +* @name: Pointer to the name of the new wakeup source. +* +* Callers must ensure that the @name string won't be freed when @ws is still in +* use. +*/ +void wakeup_source_prepare (struct wakeup_source *ws,const char *name) +{ +if (ws) { +memset (ws,0 ,sizeof (*ws)); + ws->name = name; + } +} +EXPORT_SYMBOL_GPL (wakeup_source_prepare); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */
@@ -60,31 +77,41 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_prepare (ws, name ?kstrdup (name,GFP_KERNEL ) :NULL );return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. * * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never * be run in parallel with this function for the same wakeup source object. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;del_timer_sync (&ws->timer);__pm_relax (ws); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +* +* Use only for wakeup source objects created with wakeup_source_create(). +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +wakeup_source_drop (ws);kfree (ws->name);kfree (ws); }
include/linux/pm_wakeup.h +21 -1
@@ -41,7 +41,7 @@* @active: Status of the wakeup source. */ struct wakeup_source { -char *name; +const char *name;struct list_head entry;spinlock_t lock;struct timer_list timer;
@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern void wakeup_source_prepare (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);
@@ -103,11 +105,16 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline void wakeup_source_prepare (struct wakeup_source *ws, +const char *name) {} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}
@@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struc#endif /* !CONFIG_PM_SLEEP */ +static inline void wakeup_source_init (struct wakeup_source *ws, +const char *name) +{ +wakeup_source_prepare (ws, name); +wakeup_source_add (ws); +} + +static inline void wakeup_source_trash (struct wakeup_source *ws) +{ +wakeup_source_remove (ws); +wakeup_source_drop (ws); +} +#endif /* _LINUX_PM_WAKEUP_H */
PATCH 1/1 PM / Sleep: Add more wakeup source initialization routines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Add more wakeup source initialization routines
The existing wakeup source initialization routines are not particularly useful for wakeup sources that aren't created by wakeup_source_create(), because their users have to open code filling the objects with zeros and setting their names. For this reason, introduce routines that can be used for initializing, for example, static wakeup source objects.
---
Make sure that wakeup_source_unregister() won't crash or trigger the WARN_ON() in wakeup_source_remove() if a NULL pointer is passed to it.
Thanks,
Rafael
--- drivers/base/power/wakeup.c | 50 ++++++++++++++++++++++++++++++++++++-------- include/linux/pm_wakeup.h | 22 ++++++++++++++++++- 2 files changed, 62 insertions(+), 10 deletions(-)
drivers/base/power/wakeup.c +41 -9
@@ -53,6 +53,23 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources);/** +* wakeup_source_prepare - Prepare a new wakeup source for initialization. +* @ws: Wakeup source to prepare. +* @name: Pointer to the name of the new wakeup source. +* +* Callers must ensure that the @name string won't be freed when @ws is still in +* use. +*/ +void wakeup_source_prepare (struct wakeup_source *ws,const char *name) +{ +if (ws) { +memset (ws,0 ,sizeof (*ws)); + ws->name = name; + } +} +EXPORT_SYMBOL_GPL (wakeup_source_prepare); + +/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source. */
@@ -60,31 +77,44 @@ struct wakeup_source *wakeup_source_crea {struct wakeup_source *ws; - ws =kzalloc (sizeof (*ws),GFP_KERNEL ); + ws =kmalloc (sizeof (*ws),GFP_KERNEL );if (!ws)return NULL ; -if (name) - ws->name =kstrdup (name,GFP_KERNEL ); - +wakeup_source_prepare (ws, name ?kstrdup (name,GFP_KERNEL ) :NULL );return ws; }EXPORT_SYMBOL_GPL (wakeup_source_create);/** -* wakeup_source_destroy - Destroy a struct wakeup_source object. -* @ws: Wakeup source to destroy. +* wakeup_source_drop - Prepare a struct wakeup_source object for destruction. +* @ws: Wakeup source to prepare for destruction. * * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never * be run in parallel with this function for the same wakeup source object. */ -void wakeup_source_destroy (struct wakeup_source *ws) +void wakeup_source_drop (struct wakeup_source *ws) {if (!ws)return ;del_timer_sync (&ws->timer);__pm_relax (ws); +} +EXPORT_SYMBOL_GPL (wakeup_source_drop); + +/** +* wakeup_source_destroy - Destroy a struct wakeup_source object. +* @ws: Wakeup source to destroy. +* +* Use only for wakeup source objects created with wakeup_source_create(). +*/ +void wakeup_source_destroy (struct wakeup_source *ws) +{ +if (!ws) +return ; + +wakeup_source_drop (ws);kfree (ws->name);kfree (ws); }
@@ -147,8 +177,10 @@ EXPORT_SYMBOL_GPL(wakeup_source_register*/ void wakeup_source_unregister (struct wakeup_source *ws) { -wakeup_source_remove (ws); -wakeup_source_destroy (ws); +if (ws) { +wakeup_source_remove (ws); +wakeup_source_destroy (ws); + } }EXPORT_SYMBOL_GPL (wakeup_source_unregister);
include/linux/pm_wakeup.h +21 -1
@@ -41,7 +41,7 @@* @active: Status of the wakeup source. */ struct wakeup_source { -char *name; +const char *name;struct list_head entry;spinlock_t lock;struct timer_list timer;
@@ -73,7 +73,9 @@ static inline bool device_may_wakeup(str }/* drivers/base/power/wakeup.c */ +extern void wakeup_source_prepare (struct wakeup_source *ws,const char *name);extern struct wakeup_source *wakeup_source_create (const char *name); +extern void wakeup_source_drop (struct wakeup_source *ws);extern void wakeup_source_destroy (struct wakeup_source *ws);extern void wakeup_source_add (struct wakeup_source *ws);extern void wakeup_source_remove (struct wakeup_source *ws);
@@ -103,11 +105,16 @@ static inline bool device_can_wakeup(strreturn dev->power.can_wakeup; } +static inline void wakeup_source_prepare (struct wakeup_source *ws, +const char *name) {} +static inline struct wakeup_source *wakeup_source_create (const char *name) {return NULL ; } +static inline void wakeup_source_drop (struct wakeup_source *ws) {} +static inline void wakeup_source_destroy (struct wakeup_source *ws) {}static inline void wakeup_source_add (struct wakeup_source *ws) {}
@@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struc#endif /* !CONFIG_PM_SLEEP */ +static inline void wakeup_source_init (struct wakeup_source *ws, +const char *name) +{ +wakeup_source_prepare (ws, name); +wakeup_source_add (ws); +} + +static inline void wakeup_source_trash (struct wakeup_source *ws) +{ +wakeup_source_remove (ws); +wakeup_source_drop (ws); +} +#endif /* _LINUX_PM_WAKEUP_H */
2012/2/15 Rafael J. Wysocki <[email protected]>:
Quoted 20 lines · Arve Hjønnevåg
Quoted 10 lines
2012/2/14 Rafael J. Wysocki <[email protected]>:
Quoted 9 lines · Arve Hjønnevåg
Quoted 7 lines · Rafael J. Wysocki
...
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init towakeup_source_addstill require the struct to be zero initialized and the name set manually.Quoted 8 lines
Quoted 2 lines
That should be easy to fix. What about the appended patch?
That works, but I still have to call more than one function before I can use the wakeup-source (
wakeup_source_initandwakeup_source_add) and more than one function before I can free it (__pm_relax,wakeup_source_removeandwakeup_source_drop). Is there any reason to keep these separate?
Quoted 5 lines
Yes, there is. I think that wakeup_source_create/_destroy() should use the same initialization functions internally that will be used for externally allocated wakeup sources (to make sure that all wakeup source objects are initialized in exactly the same way).
I agree with that, but is it useful to export these helper functions?
Quoted 6 lines
Quoted 5 lines
Also, not copying the name when the caller provides the memory for the wakeup-source would be a closer match to the wakelock api. Most of our wakelocks pass a string constant as the name, and making a copy of that string is not useful.
wake_lock_initis also safe to call from atomic context, but I don't know if anyone relies on this.
Quoted 6 lines
OK, below is another go. It doesn't copy the name if
wakeup_source_init()is used (which also does the_addthis time). I think, though, that copying the name is generally safer, because someone might usewakeup_source_init()with the name string allocated on the stack or otherwise temporary, which would be a bug with the new version.
I prefer this version. I have not seen a bug where someone passed a temporary as the wakelock name, I assume since this will show up immediately in the stats file.
Signature · 1 line
Arve Hjønnevåg
Quoted 21 lines · Arve Hjønnevåg
2012/2/15 Rafael J. Wysocki <[email protected]>:
Quoted 20 lines · Arve Hjønnevåg
Quoted 18 lines
2012/2/14 Rafael J. Wysocki <[email protected]>:
Quoted 11 lines · Arve Hjønnevåg
Quoted 7 lines · Rafael J. Wysocki
...
but the wake-source timeout feature has some bugs or incompatible apis. An init api would also be useful for embedding wake-sources in other data structures without adding another memory allocation. Your patch to move the spinlock init towakeup_source_addstill require the struct to be zero initialized and the name set manually.That should be easy to fix. What about the appended patch?
That works, but I still have to call more than one function before I can use the wakeup-source (
wakeup_source_initandwakeup_source_add) and more than one function before I can free it (__pm_relax,wakeup_source_removeandwakeup_source_drop). Is there any reason to keep these separate?
Quoted 7 lines
Quoted 5 lines
Yes, there is. I think that wakeup_source_create/_destroy() should use the same initialization functions internally that will be used for externally allocated wakeup sources (to make sure that all wakeup source objects are initialized in exactly the same way).
I agree with that, but is it useful to export these helper functions?
Well, we need to export either them or the ones that will call them internally and in principle someone may want to do something between _prepare() and _add() sometimes ...
Quoted 13 lines
Quoted 6 lines
Quoted 5 lines
Also, not copying the name when the caller provides the memory for the wakeup-source would be a closer match to the wakelock api. Most of our wakelocks pass a string constant as the name, and making a copy of that string is not useful.
wake_lock_initis also safe to call from atomic context, but I don't know if anyone relies on this.Quoted 6 lines
OK, below is another go. It doesn't copy the name if
wakeup_source_init()is used (which also does the_addthis time). I think, though, that copying the name is generally safer, because someone might usewakeup_source_init()with the name string allocated on the stack or otherwise temporary, which would be a bug with the new version.
Quoted 3 lines
I prefer this version. I have not seen a bug where someone passed a temporary as the wakelock name, I assume since this will show up immediately in the stats file.
OK
Thanks,
Rafael
cover PM: Implement autosleep and "wake locks", take 2
In reply to Rafael J. WysockiHi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 23 lines · Rafael J. Wysocki
Hi all,
This series tests the theory that the easiest way to sell a once rejected feature is to advertise it under a different name.
Well, there actually are two different features, although they are closely related to each other. First, patch [6/8] introduces a feature that allows the kernel to trigger system suspend (or more generally a transition into a sleep state) whenever there are no active wakeup sources (no, they aren't called wakelocks). It is called "autosleep" here, but it was called a few different names in the past ("opportunistic suspend" was probably the most popular one). Second, patch [8/8] introduces "wake locks" that are, essentially, wakeup sources which may be created and manipulated by user space. Using them user space may control the autosleep feature introduced earlier.
This also is a kind of a proof of concept for the people who wanted me to show a kernel-based implementation of automatic suspend, so there you go. Please note, however, that it is done so that the user space "wake locks" interface is compatible with Android in support of its user space. I don't really like this interface, but since the Android's user space seems to rely on it, I'm fine with using it as is. YMMV.
Quoted 6 lines
Let me say a few words about every patch in the series individually.
[1/8] - This really is a bug fix, so it's v3.4 material. Nobody has stepped
on this bug so far, but it should be fixed anyway.[2/8] - This is a freezer cleanup, worth doing anyway IMO, so v3.4 material too.
The above two are in linux-pm/linux-next now. There are a few more fixes related to wakeup sources in there and the patches below are based on that branch.
Quoted 2 lines
[3/8] - This is something we can do no problem, although completely optional
without the autosleep feature. Rather necessary with it, though.
Now [1/7] - Look for wakeup events in later stages of device suspend.
Quoted 8 lines
[4/8] - This kind of reintroduces my original idea of using a wait queue for
waiting until there are no wakeup events in progress. Alan convinced me that it would be better to poll the counter to preventwakeup_source_deactivate()from having to callwake_up_all()occasionally (that may be costly in fast paths), but then quite some people told me that the wait queue migh be better. I think that the polling will make much less sense with autosleep and user space "wake locks". Anyway, [4/8] is something we can do without those things too.
Now [2/7] - Use wait queue to signal "no wakeup events in progress"
With a couple of improvements suggested by Neil.
Quoted 3 lines
The patches above were given Sign-off-by tags, because I think they make some sense regardless of the features introcuded by the remaining patches that in turn are total RFC.
This time all of the patches are signed-off and include the requisite documentation changes (hopefully, I haven't forgotten about anything).
Quoted 8 lines
[5/8] - This changes wakeup source statistics so that they are more similar to
the statistics collected for wakelocks on Android. The file those statistics may be read from is still located in debugfs, though (I don't think it belongs to proc and its name is different from the analogous Android's file name anyway). It could be done without autosleep, but then it would be a bit pointless. BTW, this changes interfaces that _in_ _theory_ may be used by someone, but I'm not aware of anyone using them. If you are one, I'll be pleased to learn about that, so please tell me who you are. :-)
Now [3/7] - Change wakeup source statistics to follow Android.
Rebased and reworked in accordance with the Arve's feedback.
[4/7] - Add ioctl to block suspend while event queue is not empty.
Originally posted by Arve as http://marc.info/?l=linux-pm&m=132711288825973&w=4
Reworked and with modified changelog (I wonder what Dmity thinks about this).
It has some minor problems (for example, in some situations the queue wakeup source may be activated for events that are not coming from a wakeup device), but I think it's simple enough, at least for illustration. The ioctls introduced here will be used by Android user space anyway, although perhaps under different names, AFAICS.
Quoted 9 lines
[6/8] - Autosleep implementation. I think the changelog explains the idea
quite well and the code is really nothing special. It doesn't really add anything new to the kernel in terms of infrastructure etc., it just uses the existing stuff to implement an alternative method of triggering system sleep transitions. Note, though, that the interface here is different from the Android's one, because Android actually modifies/sys/power/stateto trigger something called "early suspend" (that is never going to be implemented in the "stock" kernel as long as I have any influence on it) and we simply can't do that in the mainline.
Now [5/7] - Implement opportunistic sleep
Rebased and simplified (most notably, I've dropped the "main" wakeup source, since it wasn't really necessary).
Quoted 5 lines
[7/8] - This adds a wakeup source statistics that only makes sense with
autosleep and (I believe) is analogous to the Android'sprevent_suspend_timestatistics. Nothing really special, but I didn't want wakeup_source_activate/deactivate() to take a common lock to avoid congestion.
Now [6/7] - Add "prevent autosleep time" statistics to wakeup sources.
Rebased.
Quoted 8 lines
[8/8] - This adds a user space interface to create, activate and deactivate
wakeup sources. Since the files it consists of are calledwake_lockandwake_unlock, to follow Android, the objects the wakeup sources are wrapped into are called "wakelocks" (for added confusion). Since the interface doesn't provide any means to destroy those "wakelocks", I added a garbage collection mechanism to get rid of the unused ones, if any. I also tought it might be a good idea to put a limit on the number of those things that user space can operate simultaneously, so I did that too.
Now [7/7] - Add user space interface for manipulating wakeup sources.
Quoted 3 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
The above is still accurate, but I also verified that the patches don't break my PC test boxes (at least as long as the new features aren't used ;-)).
Thanks,
Rafael
RFC PATCH 1/7 PM / Sleep: Look for wakeup events in later stages of device suspend
From: Rafael J. Wysocki <[email protected]>
Currently, the device suspend code in drivers/base/power/main.c only checks if there have been any wakeup events, and therefore the ongoing system transition to a sleep state should be aborted, during the first (i.e. "suspend") device suspend phase. However, wakeup events may be reported later as well, so it's reasonable to look for them in the in the subsequent (i.e. "late suspend" and "suspend noirq") phases.
--- drivers/base/power/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/base/power/main.c +10 -0
@@ -889,6 +889,11 @@ static int dpm_suspend_noirq(pm_message_if (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_noirq_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
@@ -962,6 +967,11 @@ static int dpm_suspend_late(pm_message_tif (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_late_early_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
RFC PATCH 2/7 PM / Sleep: Use wait queue to signal "no wakeup events in progress"
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires pm_get_wakeup_count() to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.
--- drivers/base/power/wakeup.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/base/power/wakeup.c +13 -3
@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition.
@@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare.
@@ -442,6 +442,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;
@@ -476,6 +477,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +wake_up (&wakeup_count_wait_queue); }/**
@@ -667,14 +672,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
RFC PATCH 3/7 PM / Sleep: Change wakeup source statistics to follow Android
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have in wakeup sources at the moment and there aren't any known users of those statistics other than Android, so modify them to make it easier for Android to switch to wakeup sources.
This removes the struct wakeup_source's hit_cout field, which is very rough and therefore not very useful, and adds two new fields, wakeup_count and expire_count. The first one tracks how many times the wakeup source is activated with events_check_enabled set (which roughly corresponds to the situations when a system power transition to a sleep state is in progress and would be aborted by this wakeup source if it were the only active one at that time) and the second one is the number of times the wakeup source has been activated with a timeout that expired.
Additionally, the last_time field is now updated when the wakeup source is deactivated too (previously it was only updated during the wakeup source's activation), which seems to be what Android does with the analogous counter for wakelocks.
--- Documentation/ABI/testing/sysfs-devices-power | 24 ++++++--- drivers/base/power/sysfs.c | 30 ++++++++++-- drivers/base/power/wakeup.c | 64 +++++++++++--------------- include/linux/pm_wakeup.h | 11 ++-- 4 files changed, 77 insertions(+), 52 deletions(-)
include/linux/pm_wakeup.h +7 -4
@@ -33,12 +33,14 @@* * @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 activated last time. +* @last_time: Monotonic clock when the wakeup source's was touched last time. * @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. -* @hit_count: Number of times the wakeup sorce might abort system suspend. +* @expire_count: Number of times the wakeup source's timeout has expired. +* @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. +* @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source {const char *name;
@@ -52,8 +54,9 @@ struct wakeup_source {unsigned long event_count;unsigned long active_count;unsigned long relax_count; -unsigned long hit_count; - unsigned int active:1; +unsigned long expire_count; +unsigned long wakeup_count; + bool active:1; };#ifdef CONFIG_PM_SLEEP
drivers/base/power/wakeup.c +28 -36
138 lines not on this page · raw message
drivers/base/power/sysfs.c +25 -5
@@ -288,22 +288,41 @@ static ssize_t wakeup_active_count_show( static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); -static ssize_t wakeup_hit_count_show (struct device *dev, -struct device_attribute *attr,char *buf) +static ssize_t wakeup_abort_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +unsigned long count =0 ; +bool enabled = false; + +spin_lock_irq (&dev->power.lock); +if (dev->power.wakeup) { + count = dev->power.wakeup->wakeup_count; + enabled = true; + } +spin_unlock_irq (&dev->power.lock); +return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); +} + +static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); + +static ssize_t wakeup_expire_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) {unsigned long count =0 ;bool enabled = false;spin_lock_irq (&dev->power.lock);if (dev->power.wakeup) { - count = dev->power.wakeup->hit_count; + count = dev->power.wakeup->expire_count; enabled = true; }spin_unlock_irq (&dev->power.lock);return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); } -static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); +static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);static ssize_t wakeup_active_show (struct device *dev,struct device_attribute *attr,char *buf)
@@ -460,7 +479,8 @@ static struct attribute *wakeup_attrs[] &dev_attr_wakeup.attr, &dev_attr_wakeup_count.attr, &dev_attr_wakeup_active_count.attr, - &dev_attr_wakeup_hit_count.attr, + &dev_attr_wakeup_abort_count.attr, + &dev_attr_wakeup_expire_count.attr, &dev_attr_wakeup_active.attr, &dev_attr_wakeup_total_time_ms.attr, &dev_attr_wakeup_max_time_ms.attr,
Documentation/ABI/testing/sysfs-devices-power +17 -7
@@ -96,16 +96,26 @@ Description: 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/wakeup_hit_count -Date: September 2010 +What: /sys/devices/.../power/wakeup_abort_count +Date: February 2012 Contact: Rafael J. Wysocki <[email protected]> Description: - The /sys/devices/.../wakeup_hit_count attribute contains the + The /sys/devices/.../wakeup_abort_count attribute contains the 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 not - present. + the device might have aborted system transition into a sleep + state in progress. 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/wakeup_expire_count +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/devices/.../wakeup_expire_count attribute contains the + number of times a wakeup event associated with the device has + been reported with a timeout that expired. 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/wakeup_active Date: September 2010
RFC PATCH 4/7 Input / PM: Add ioctl to block suspend while event queue is not empty
In reply to Rafael J. WysockiFrom: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and pm_get_wakeup_count() will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the /sys/power/wakeup_count interface), the system won't be able to go into a sleep state until the queue is empty.
This allows user space processes to handle situations in which they want to do a select() on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).
[rjw: Removed unnecessary checks, changed the names of the new ioctls
and the names of the functions that add/remove wakeup source objects to/from evdev clients, modified the changelog.]
--- drivers/input/evdev.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/input.h | 3 ++ 2 files changed, 58 insertions(+)
drivers/input/evdev.c +55 -0
@@ -43,6 +43,7 @@ struct evdev_client {unsigned int tail;unsigned int packet_head;/* [future] position of the first element of next packet */ spinlock_t buffer_lock;/* protects access to buffer, head and tail */ +struct wakeup_source *wakeup_source;struct fasync_struct *fasync;struct evdev *evdev;struct list_head node;
@@ -75,10 +76,12 @@ static void evdev_pass_event(struct evde client->buffer[client->tail].value =0 ; client->packet_head = client->tail; +__pm_relax (client->wakeup_source); }if (event->type ==EV_SYN && event->code ==SYN_REPORT ) { client->packet_head = client->head; +__pm_stay_awake (client->wakeup_source);kill_fasync (&client->fasync,SIGIO ,POLL_IN ); }
@@ -255,6 +258,8 @@ static int evdev_release(struct inode *imutex_unlock (&evdev->mutex);evdev_detach_client (evdev, client); +wakeup_source_unregister (client->wakeup_source); +kfree (client);evdev_close_device (evdev);
@@ -373,6 +378,8 @@ static int evdev_fetch_next_event(structif (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize -1 ; +if (client->packet_head == client->tail) +__pm_relax (client->wakeup_source); }spin_unlock_irq (&client->buffer_lock);
@@ -623,6 +630,45 @@ static int evdev_handle_set_keycode_v2(sreturn input_set_keycode (dev, &ke); } +static int evdev_attach_wakeup_source (struct evdev *evdev, +struct evdev_client *client) +{ +struct wakeup_source *ws; +char name[28 ]; + +if (client->wakeup_source) +return 0 ; + +snprintf (name,sizeof (name),"%s-%d" , +dev_name (&evdev->dev),task_tgid_vnr (current)); + + ws =wakeup_source_register (name); +if (!ws) +return -ENOMEM ; + +spin_lock_irq (&client->buffer_lock); + client->wakeup_source = ws; +if (client->packet_head != client->tail) +__pm_stay_awake (client->wakeup_source); +spin_unlock_irq (&client->buffer_lock); +return 0 ; +} + +static int evdev_detach_wakeup_source (struct evdev *evdev, +struct evdev_client *client) +{ +struct wakeup_source *ws; + +spin_lock_irq (&client->buffer_lock); + ws = client->wakeup_source; + client->wakeup_source =NULL ; +spin_unlock_irq (&client->buffer_lock); + +wakeup_source_unregister (ws); + +return 0 ; +} +static long evdev_do_ioctl (struct file *file,unsigned int cmd,void __user *p,int compat_mode) {
@@ -696,6 +742,15 @@ static long evdev_do_ioctl(struct file *case EVIOCSKEYCODE_V2 :return evdev_handle_set_keycode_v2 (dev, p); + +case EVIOCGWAKEUPSRC : +return put_user (!!client->wakeup_source, ip); + +case EVIOCSWAKEUPSRC : +if (p) +return evdev_attach_wakeup_source (evdev, client); +else +return evdev_detach_wakeup_source (evdev, client); } size =_IOC_SIZE (cmd);
include/linux/input.h +3 -0
@@ -129,6 +129,9 @@ struct input_keymap_entry {#define EVIOCGRAB _IOW('E', 0x90, int)/* Grab/Release device */ +#define EVIOCGWAKEUPSRC _IOR('E', 0x91, int)/* Check if wakeup handling is enabled */ +#define EVIOCSWAKEUPSRC _IOW('E', 0x91, int)/* Enable/disable wakeup handling */ +/* * Device properties and quirks */
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.
Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).
I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct epoll_event ev;
epfd = epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
epfd = epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Quoted 59 lines
[rjw: Removed unnecessary checks, changed the names of the new ioctls
and the names of the functions that add/remove wakeup source objects to/from evdev clients, modified the changelog.Signed-off-by Arve Hjønnevåg <[email protected]>Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/input/evdev.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/input.h | 3 ++ 2 files changed, 58 insertions(+)Index:
linux/drivers/input/evdev.c
===================================================================drivers/input/evdev.c +8
-0@@ -43,6 +43,7 @@ struct evdev_client {unsigned int tail;unsigned int packet_head;/* [future] position of the first element of next packet */ spinlock_t buffer_lock;/* protects access to buffer, head and tail */ +struct wakeup_source *wakeup_source;struct fasync_struct *fasync;struct evdev *evdev;struct list_head node;@@ -75,10 +76,12 @@ static void evdev_pass_event(struct evde client->buffer[client->tail].value =0 ; client->packet_head = client->tail; +__pm_relax (client->wakeup_source); }if (event->type ==EV_SYN && event->code ==SYN_REPORT ) { client->packet_head = client->head; +__pm_stay_awake (client->wakeup_source);kill_fasync (&client->fasync,SIGIO ,POLL_IN ); }@@ -255,6 +258,8 @@ static int evdev_release(struct inode *imutex_unlock (&evdev->mutex);evdev_detach_client (evdev, client); +wakeup_source_unregister (client->wakeup_source); +kfree (client);evdev_close_device (evdev);@@ -373,6 +378,8 @@ static int evdev_fetch_next_event(structif (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize -1 ; +if (client->packet_head == client->tail) +__pm_relax (client->wakeup_source); }spin_unlock_irq (&client->buffer_lock);@@ -623,6 +630,45 @@ static int evdev_handle_set_keycode_v2(sreturn input_set_keycode (dev, &ke); } +static int evdev_attach_wakeup_source(struct evdev *evdev,
drivers/input/evdev.c static int evdev_handle_set_keycode_v2(s · line 643
Quoted 10 lines
drivers/input/evdev.c +10
-0+ struct evdev_client *client) +{ +struct wakeup_source *ws; +char name[28 ]; + +if (client->wakeup_source) +return 0 ; + +snprintf (name,sizeof (name),"%s-%d" , +dev_name (&evdev->dev),task_tgid_vnr (current));
This does not look like it will work well with tasks in different pid namespaces. What should happen, I think, is the wakeup_source should hold a reference to either the struct pid of current or current itself. Then when someone reads the file you should get the pid vnr in the reader's pid namespace. That way instead of a bogus pid vnr 0 would show up if "current" here is not in the reader's pid namepsace.
Cheers,
-Matt Helsley
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 23 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).
Quoted 8 lines
I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
This is an interesting idea, but I'm not sure how well it would work.
I looked at the epoll code and it looks like it is possible to activate the wakeup-source from the wait queue function it uses. The epoll callback will happen without holding evdev client buffer_lock, so the wakeup-source and buffer state will not always be in sync (this may be OK, but require more thought). This callback is also called if no data was added to the queue we are polling on because another client has grabbed the input device (is this a bug or intended?).
There is no call into the epoll code when input queue is emptied, so we can't deactivate the wakeup-source until epoll_wait is called again. This also should be workable, but result in different stats.
It does not look like the normal poll and select interfaces can be extended the same way (since they remove themselves from the wait-queue before returning to user-space), so user-space has to be changed to use epoll even if select or poll would be a better fit.
I don't know how many other drivers this would work for. The input driver will wake up user-space from the same thread or interrupt handler that queued the event, but other drivers may defer this to another thread which makes an epoll wakeup-source insufficient.
...
Quoted 10 lines
Quoted 2 lines
+ snprintf(name, sizeof(name), "%s-%d",
+ dev_name(&evdev->dev),task_tgid_vnr(current));This does not look like it will work well with tasks in different pid namespaces. What should happen, I think, is the
wakeup_sourceshould hold a reference to either the struct pid of current or current itself. Then when someone reads the file you should get the pid vnr in the reader's pid namespace. That way instead of a bogus pid vnr 0 would show up if "current" here is not in the reader's pid namepsace.
The pid here is only used for debugging purposes, and used less than the dev_name. I don't think tracking pid namespaces is worth the trouble here, so if this is a real problem we can just drop the pid from the name for now.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 33 lines · Arve Hjønnevåg
Quoted 23 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).Quoted 8 lines
I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
Quoted 4 lines
This is an interesting idea, but I'm not sure how well it would work.
I looked at the epoll code and it looks like it is possible to activate the wakeup-source from the wait queue function it uses.
I'm not sure I'm following you here. How exactly would you like to do that?
In particular, what data structure would the wakeup source object be associated with?
Quoted 6 lines
The epoll callback will happen without holding evdev client
buffer_lock, so the wakeup-source and buffer state will not always be in sync (this may be OK, but require more thought). This callback is also called if no data was added to the queue we are polling on because another client has grabbed the input device (is this a bug or intended?).
Quoted 8 lines
There is no call into the epoll code when input queue is emptied, so we can't deactivate the wakeup-source until
epoll_waitis called again. This also should be workable, but result in different stats.It does not look like the normal poll and select interfaces can be extended the same way (since they remove themselves from the wait-queue before returning to user-space), so user-space has to be changed to use epoll even if select or poll would be a better fit.
Well, epoll without EPOLLET is equivalent to poll, so the only potential issue is select. How serious may the problem with that be?
Quoted 4 lines
I don't know how many other drivers this would work for. The input driver will wake up user-space from the same thread or interrupt handler that queued the event, but other drivers may defer this to another thread which makes an epoll wakeup-source insufficient.
If we go for new ioctls insread, we'll have to add them to all of those drivers, so I would prefer the epoll-based approach if that's viable at least for a subset of the relevant drivers.
Quoted 12 lines
...
Quoted 10 lines
drivers/input/evdev.c static int evdev_handle_set_keycode_v2(s · line 643
Quoted 2 lines
drivers/input/evdev.c +2
-0+snprintf (name,sizeof (name),"%s-%d" , +dev_name (&evdev->dev),task_tgid_vnr (current));This does not look like it will work well with tasks in different pid namespaces. What should happen, I think, is the
wakeup_sourceshould hold a reference to either the struct pid of current or current itself. Then when someone reads the file you should get the pid vnr in the reader's pid namespace. That way instead of a bogus pid vnr 0 would show up if "current" here is not in the reader's pid namepsace.
Quoted 4 lines
The pid here is only used for debugging purposes, and used less than the
dev_name. I don't think tracking pid namespaces is worth the trouble here, so if this is a real problem we can just drop the pid from the name for now.
OK
Thanks,
Rafael
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
In reply to Arve HjønnevågQuoted 43 lines · Arve Hjønnevåg
Quoted 23 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).Quoted 8 lines
I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
This is an interesting idea, but I'm not sure how well it would work.
I looked at the epoll code and it looks like it is possible to activate the wakeup-source from the wait queue function it uses. The epoll callback will happen without holding evdev client
buffer_lock, so the wakeup-source and buffer state will not always be in sync (this may be OK, but require more thought). This callback is also called if no data was added to the queue we are polling on because another client has grabbed the input device (is this a bug or intended?).
Quoted 7 lines
There is no call into the epoll code when input queue is emptied, so we can't deactivate the wakeup-source until
epoll_waitis called again. This also should be workable, but result in different stats.It does not look like the normal poll and select interfaces can be extended the same way (since they remove themselves from the wait-queue before returning to user-space), so user-space has to be
Yup, that is exactly why epoll is so well suited to this.
Quoted 1 line
changed to use epoll even if select or poll would be a better fit.
Either way, modification of application code is necessary, right?
Quoted 4 lines
I don't know how many other drivers this would work for. The input driver will wake up user-space from the same thread or interrupt handler that queued the event, but other drivers may defer this to another thread which makes an epoll wakeup-source insufficient.
I don't understand how this would be insufficient. So long as the interrupt causes the wakeup source to prevent the machine from suspending before finishing interrupt handling does it matter whether the event handling itself is deferred?
In case there's some confusion: I'm not saying that this idea will solve all of the problems, especially:
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Of course, if
they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the
last one).
(endquote)
Quoted 13 lines
...
Quoted 10 lines
Quoted 2 lines
+ snprintf(name, sizeof(name), "%s-%d",
+ dev_name(&evdev->dev),task_tgid_vnr(current));This does not look like it will work well with tasks in different pid namespaces. What should happen, I think, is the
wakeup_sourceshould hold a reference to either the struct pid of current or current itself. Then when someone reads the file you should get the pid vnr in the reader's pid namespace. That way instead of a bogus pid vnr 0 would show up if "current" here is not in the reader's pid namepsace.
Quoted 4 lines
The pid here is only used for debugging purposes, and used less than the
dev_name. I don't think tracking pid namespaces is worth the trouble here, so if this is a real problem we can just drop the pid from the name for now.
I think dropping the pid would be the best choice. If it's absolutely necessary in the output then it should be made to work with pid namespaces because the interface will be maintained forever.
Cheers,
-Matt
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
In reply to Matt HelsleyQuoted 37 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct
epoll_eventev;epfd =
epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Quoted 7 lines
Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
epfd =
epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Do you mean something like the patch below, or something different?
Rafael
--- drivers/input/evdev.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ fs/eventpoll.c | 15 +++++++++++- include/linux/eventpoll.h | 6 +++++ include/linux/fs.h | 1 4 files changed, 76 insertions(+), 1 deletion(-)
include/linux/fs.h +1 -0
@@ -1604,6 +1604,7 @@ struct file_operations {ssize_t (*aio_write) (struct kiocb *,const struct iovec *,unsigned long ,loff_t );int (*readdir) (struct file *,void *,filldir_t );unsigned int (*poll) (struct file *,struct poll_table_struct *); +void (*epoll_ctl) (struct file *,int ,unsigned int );long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *);
fs/eventpoll.c +14 -1
@@ -609,6 +609,10 @@ static int ep_remove(struct eventpoll *eunsigned long flags;struct file *file = epi->ffd.file; +/* Notify the underlying driver that the polling has completed */ +if (file->f_op->epoll_ctl) + file->f_op->epoll_ctl (file,EPOLL_CTL_DEL , epi->event.events); +/* * Removes poll wait queue hooks. We _have_ to do this without holding * the "ep->lock" otherwise a deadlock might occur. This because of the
@@ -1094,6 +1098,10 @@ static int ep_insert(struct eventpoll *e epq.epi = epi;init_poll_funcptr (&epq.pt, ep_ptable_queue_proc); +/* Notify the underlying driver that we want to poll it */ +if (tfile->f_op->epoll_ctl) + tfile->f_op->epoll_ctl (tfile,EPOLL_CTL_ADD , event->events); +/* * Attach the item to the poll hooks and get current event bits. * We can safely use the file* here because its usage count has
@@ -1185,6 +1193,7 @@ error_unregister:*/ static int ep_modify (struct eventpoll *ep,struct epitem *epi,struct epoll_event *event) { +struct file *file = epi->ffd.file;int pwake =0 ;unsigned int revents;
@@ -1196,11 +1205,15 @@ static int ep_modify(struct eventpoll *e epi->event.events = event->events; epi->event.data = event->data;/* protected by mtx */ +/* Notify the underlying driver of the change */ +if (file->f_op->epoll_ctl) + file->f_op->epoll_ctl (file,EPOLL_CTL_MOD , event->events); +/* * Get current event bits. We can safely use the file* here because * its usage count has been increased by the caller of this function. */ - revents = epi->ffd.file->f_op->poll (epi->ffd.file,NULL ); + revents = file->f_op->poll (file,NULL );/* * If the item is "hot" and it is not registered inside the ready
drivers/input/evdev.c +55 -0
101 lines not on this page · raw message
include/linux/eventpoll.h +6 -0
@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 42 lines · Rafael J. Wysocki
Quoted 41 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct
epoll_eventev;epfd =
epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
Quoted 5 lines
Quoted 3 lines
epfd =
epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Do you mean something like the patch below, or something different?
Yeah, this was sort of what I was thinking of. It nicely avoids the ioctl() bits. I guess my only issue is the fop mimics the epoll interface -- should it just be an fop to manage the file as a wakeup source rather than a generic hook into epoll?
Cheers,
-Matt Helsley
Quoted 203 lines
Rafael
--- drivers/input/evdev.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ fs/eventpoll.c | 15 +++++++++++- include/linux/eventpoll.h | 6 +++++ include/linux/fs.h | 1 4 files changed, 76 insertions(+), 1 deletion(-)Index:
linux/include/linux/fs.h
===================================================================include/linux/fs.h +1
-0@@ -1604,6 +1604,7 @@ struct file_operations {ssize_t (*aio_write) (struct kiocb *,const struct iovec *,unsigned long ,loff_t );int (*readdir) (struct file *,void *,filldir_t );unsigned int (*poll) (struct file *,struct poll_table_struct *); +void (*epoll_ctl) (struct file *,int ,unsigned int );long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *);Index:
linux/fs/eventpoll.c
===================================================================fs/eventpoll.c +14
-1@@ -609,6 +609,10 @@ static int ep_remove(struct eventpoll *eunsigned long flags;struct file *file = epi->ffd.file; +/* Notify the underlying driver that the polling has completed */ +if (file->f_op->epoll_ctl) + file->f_op->epoll_ctl (file,EPOLL_CTL_DEL , epi->event.events); +/* * Removes poll wait queue hooks. We _have_ to do this without holding * the "ep->lock" otherwise a deadlock might occur. This because of the @@ -1094,6 +1098,10 @@ static int ep_insert(struct eventpoll *e epq.epi = epi;init_poll_funcptr (&epq.pt, ep_ptable_queue_proc); +/* Notify the underlying driver that we want to poll it */ +if (tfile->f_op->epoll_ctl) + tfile->f_op->epoll_ctl (tfile,EPOLL_CTL_ADD , event->events); +/* * Attach the item to the poll hooks and get current event bits. * We can safely use the file* here because its usage count has @@ -1185,6 +1193,7 @@ error_unregister:*/ static int ep_modify (struct eventpoll *ep,struct epitem *epi,struct epoll_event *event) { +struct file *file = epi->ffd.file;int pwake =0 ;unsigned int revents;@@ -1196,11 +1205,15 @@ static int ep_modify(struct eventpoll *e epi->event.events = event->events; epi->event.data = event->data;/* protected by mtx */ +/* Notify the underlying driver of the change */ +if (file->f_op->epoll_ctl) + file->f_op->epoll_ctl (file,EPOLL_CTL_MOD , event->events); +/* * Get current event bits. We can safely use the file* here because * its usage count has been increased by the caller of this function. */ - revents = epi->ffd.file->f_op->poll (epi->ffd.file,NULL ); + revents = file->f_op->poll (file,NULL );/* * If the item is "hot" and it is not registered inside the ready Index:
linux/drivers/input/evdev.c
===================================================================drivers/input/evdev.c +55
-0@@ -16,6 +16,7 @@#define EVDEV_BUF_PACKETS 8#include <linux/poll.h> +#include <linux/eventpoll.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> @@ -43,6 +44,7 @@ struct evdev_client {unsigned int tail;unsigned int packet_head;/* [future] position of the first element of next packet */ spinlock_t buffer_lock;/* protects access to buffer, head and tail */ +struct wakeup_source *wakeup_source;struct fasync_struct *fasync;struct evdev *evdev;struct list_head node;@@ -75,10 +77,12 @@ static void evdev_pass_event(struct evde client->buffer[client->tail].value =0 ; client->packet_head = client->tail; +__pm_relax (client->wakeup_source); }if (event->type ==EV_SYN && event->code ==SYN_REPORT ) { client->packet_head = client->head; +__pm_stay_awake (client->wakeup_source);kill_fasync (&client->fasync,SIGIO ,POLL_IN ); }@@ -255,6 +259,8 @@ static int evdev_release(struct inode *imutex_unlock (&evdev->mutex);evdev_detach_client (evdev, client); +wakeup_source_unregister (client->wakeup_source); +kfree (client);evdev_close_device (evdev);@@ -373,6 +379,8 @@ static int evdev_fetch_next_event(structif (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize -1 ; +if (client->packet_head == client->tail) +__pm_relax (client->wakeup_source); }spin_unlock_irq (&client->buffer_lock);@@ -433,6 +441,52 @@ static unsigned int evdev_poll(struct fireturn mask; } +static void evdev_client_attach_wakeup_source (struct evdev_client *client) +{ +struct wakeup_source *ws; + + ws =wakeup_source_register (dev_name (&client->evdev->dev)); +spin_lock_irq (&client->buffer_lock); + client->wakeup_source = ws; +if (client->packet_head != client->tail) +__pm_stay_awake (client->wakeup_source); +spin_unlock_irq (&client->buffer_lock); +} + +static void evdev_client_detach_wakeup_source (struct evdev_client *client) +{ +struct wakeup_source *ws; + +spin_lock_irq (&client->buffer_lock); + ws = client->wakeup_source; + client->wakeup_source =NULL ; +spin_unlock_irq (&client->buffer_lock); +wakeup_source_unregister (ws); +} + +static void evdev_epoll_ctl (struct file *file,int op, +unsigned int events) +{ +struct evdev_client *client = file->private_data; + +switch (op) { +case EPOLL_CTL_ADD : +if ((events &EPOLLWAKEUP ) && !client->wakeup_source) +evdev_client_attach_wakeup_source (client); +break ; +case EPOLL_CTL_DEL : +if (events &EPOLLWAKEUP ) +evdev_client_detach_wakeup_source (client); +break ; +case EPOLL_CTL_MOD : +/* 'events' is the new events mask (after the change) */ +if ((events &EPOLLWAKEUP ) && !client->wakeup_source) +evdev_client_attach_wakeup_source (client); +else if (!(events &EPOLLWAKEUP )) +evdev_client_detach_wakeup_source (client); + } +} +#ifdef CONFIG_COMPAT #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)@@ -845,6 +899,7 @@ static const struct file_operations evde .read = evdev_read, .write = evdev_write, .poll = evdev_poll, + .epoll_ctl = evdev_epoll_ctl, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl,Index:
linux/include/linux/eventpoll.h
===================================================================include/linux/eventpoll.h +6
-0@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 47 lines · Matt Helsley
Quoted 46 lines · Rafael J. Wysocki
Quoted 44 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct
epoll_eventev;epfd =
epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
epfd =
epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Quoted 6 lines
Quoted 1 line
Do you mean something like the patch below, or something different?
Yeah, this was sort of what I was thinking of. It nicely avoids the
ioctl()bits. I guess my only issue is the fop mimics the epoll interface -- should it just be an fop to manage the file as a wakeup source rather than a generic hook into epoll?
I'm not exactly sure what you mean, could you be a bit more specific, please?
Rafael
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
In reply to Rafael J. WysockiQuoted 46 lines · Rafael J. Wysocki
Quoted 37 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct
epoll_eventev;epfd =
epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Quoted 7 lines
Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
epfd =
epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
Quoted 5 lines
Do you mean something like the patch below, or something different?
Rafael
---
I don't think it is useful to tie an evdev implementation to epoll that way. You just replaced the ioctl with a new control function.
The code below tries to implement the same flag without modifying evdev at all. The behavior of this is different as it will keep the device awake until user-space calls epoll_wait again. I also used an extra wakeup source to handle the function that runs without the spin_lock held which means that non-wakeup files in same epoll list could abort suspend.
Signature · 3 lines
Arve Hjønnevåg ----
fs/eventpoll.c +14 -2
@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h>
@@ -79,7 +80,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4
@@ -146,6 +147,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };
@@ -186,6 +190,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user; };
@@ -492,6 +499,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* in a lockless way. */ spin_lock_irqsave (&ep->lock, flags); +__pm_stay_awake (ep->ws);list_splice_init (&ep->rdllist, &txlist); ep->ovflist =NULL ;spin_unlock_irqrestore (&ep->lock, flags);
@@ -515,9 +523,12 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } } +/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after * releasing the lock, events will be queued in the normal way inside
@@ -529,6 +540,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/*
@@ -583,6 +595,9 @@ static int ep_remove(struct eventpoll *ep, struct
epitem *epi)
list_del_init(&epi->rdllink);
spin_unlock_irqrestore(&ep->lock, flags);
+ if (epi->ws)
+ wakeup_source_unregister(epi->ws);
+
/* At this point it is safe to free the eventpoll item */ kmem_cache_free(epi_cache, epi);
@@ -633,6 +648,8 @@ static void ep_free(struct eventpoll *ep)
mutex_unlock(&epmutex); mutex_destroy(&ep->mtx); free_uid(ep->user);
+ if (ep->ws)
+ wakeup_source_unregister(ep->ws);
kfree(ep);
}
@@ -661,6 +678,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,
* callback, but it's not actually ready, as far as
* caller requested events goes. We can remove it here. */
+ __pm_relax(epi->ws);
list_del_init(&epi->rdllink);
}
}
@@ -851,8 +869,10 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k
}
/* If this file is already in the ready list we exit soon */
- if (!ep_is_linked(&epi->rdllink))
+ if (!ep_is_linked(&epi->rdllink)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
+ __pm_stay_awake(epi->ws);
+ }
/*
* Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -915,6 +935,30 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi)
rb_insert_color(&epi->rbn, &ep->rbr);
}
+static int ep_create_wakeup_source(struct epitem *epi)
+{
+ const char *name;
+
+ if (!epi->ep->ws) {
+ epi->ep->ws = wakeup_source_register("eventpoll");
+ if (!epi->ep->ws)
+ return -ENOMEM;
+ }
+
+ name = epi->ffd.file->f_path.dentry->d_name.name;
+ epi->ws = wakeup_source_register(name);
+ if (!epi->ws)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ep_destroy_wakeup_source(struct epitem *epi) +{
+ wakeup_source_unregister(epi->ws);
+ epi->ws = NULL;
+}
+
/*
* Must be called with "mtx" held.
*/
@@ -942,6 +986,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;
+ if (epi->event.events & EPOLLWAKEUP) {
+ error = ep_create_wakeup_source(epi);
+ if (error)
+ goto error_create_wakeup_source;
+ } else {
+ epi->ws = NULL;
+ }
/* Initialize the poll table using the queue callback */ epq.epi = epi;
@@ -982,6 +1033,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
+ __pm_stay_awake(epi->ws);
/* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq))
@@ -1014,6 +1066,10 @@ error_unregister:
list_del_init(&epi->rdllink);
spin_unlock_irqrestore(&ep->lock, flags);
+ if (epi->ws)
+ wakeup_source_unregister(epi->ws);
+
+error_create_wakeup_source:
kmem_cache_free(epi_cache, epi);
return error;
@@ -1035,6 +1091,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even
*/
epi->event.events = event->events; epi->event.data = event->data; /* protected by mtx */
+ if (epi->event.events & EPOLLWAKEUP) {
+ if (!epi->ws)
+ ep_create_wakeup_source(epi);
+ } else if (epi->ws) {
+ ep_destroy_wakeup_source(epi);
+ }
/*
* Get current event bits. We can safely use the file* here because
@@ -1050,6 +1112,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even
spin_lock_irq(&ep->lock);
if (!ep_is_linked(&epi->rdllink)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
+ __pm_stay_awake(epi->ws);
/* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq))
@@ -1085,6 +1148,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
!list_empty(head) && eventcnt < esed->maxevents;) {
epi = list_first_entry(head, struct epitem, rdllink);
+ __pm_relax(epi->ws);
list_del_init(&epi->rdllink);
revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) &
@@ -1100,6 +1164,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
if (__put_user(revents, &uevent->events) ||
__put_user(epi->event.data, &uevent->data)) {
list_add(&epi->rdllink, head);
+ __pm_stay_awake(epi->ws);
return eventcnt ? eventcnt : -EFAULT;
}
eventcnt++;
@@ -1119,6 +1184,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
* poll callback will queue them in ep->ovflist. */
list_add_tail(&epi->rdllink, &ep->rdllist);
+ __pm_stay_awake(epi->ws);
}
}
}
include/linux/eventpoll.h +6 -0
@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Re: [RFC][PATCH 4/7] Input / PM: Add ioctl to block suspend while event queue is not empty
Quoted 56 lines · Arve Hjønnevåg
Quoted 46 lines · Rafael J. Wysocki
Quoted 37 lines · Matt Helsley
Quoted 11 lines · Rafael J. Wysocki
From: Arve Hjønnevåg <[email protected]>
Add a new ioctl, EVIOCSWAKEUPSRC, to attach a wakeup source object to an evdev client event queue, such that it will be active whenever the queue is not empty. Then, all events in the queue will be regarded as wakeup events in progress and
pm_get_wakeup_count()will block (or return false if woken up by a signal) until they are removed from the queue. In consequence, if the checking of wakeup events is enabled (e.g. throught the/sys/power/wakeup_countinterface), the system won't be able to go into a sleep state until the queue is empty.Quoted 10 lines
This allows user space processes to handle situations in which they want to do a
select()on an evdev descriptor, so they go to sleep until there are some events to read from the device's queue, and then they don't want the system to go into a sleep state until all the events are read (presumably for further processing). Of course, if they don't want the system to go into a sleep state _after_ all the events have been read from the queue, they have to use a separate mechanism that will prevent the system from doing that and it has to be activated before reading the first event (that also may be the last one).I haven't seen this idea mentioned before but I must admit I haven't been following this thread too closely so apologies (and don't bother rehashing) if it has:
Could you just add this to epoll so that any fd userspace chooses would be capable of doing this without introducing potentially ecclectic ioctl interfaces?
struct
epoll_eventev;epfd =
epoll_create1(EPOLL_STAY_AWAKE_SET);
ev.data.ptr = foo;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Quoted 7 lines
Which could be useful because you can put one epollfd in another's epoll set. Or maybe as an EPOLLKEEPAWAKE flag in the event struct sort of like EPOLLET:
epfd =
epoll_create1(0);
ev.events = EPOLLIN|EPOLLKEEPAWAKE;epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);Quoted 5 lines
Do you mean something like the patch below, or something different?
Rafael
---
I don't think it is useful to tie an evdev implementation to epoll that way. You just replaced the ioctl with a new control function.
Quoted 6 lines
The code below tries to implement the same flag without modifying evdev at all. The behavior of this is different as it will keep the device awake until user-space calls
epoll_waitagain. I also used an extra wakeup source to handle the function that runs without thespin_lockheld which means that non-wakeup files in same epoll list could abort suspend.
Well, if that works for you, it will be better than adding ioctls to evdev (and presumably a number of other devices).
Care to resubmit with a proper changelog and sign-off?
Rafael
PATCH 1/2 epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
When an epoll_event, that has the EPOLLWAKEUP flag set, is ready, a wakeup_source will be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
The current implementation uses an extra wakeup_source when ep_scan_ready_list runs. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.
--- fs/eventpoll.c | 71 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/eventpoll.h | 6 ++++ 2 files changed, 74 insertions(+), 3 deletions(-)
fs/eventpoll.c +68 -3
@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h>
@@ -88,7 +89,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4
@@ -155,6 +156,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };
@@ -195,6 +199,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;
@@ -524,6 +531,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* in a lockless way. */ spin_lock_irqsave (&ep->lock, flags); +__pm_stay_awake (ep->ws);list_splice_init (&ep->rdllist, &txlist); ep->ovflist =NULL ;spin_unlock_irqrestore (&ep->lock, flags);
@@ -547,8 +555,10 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
@@ -561,6 +571,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/*
@@ -615,6 +626,9 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);
@@ -665,6 +679,8 @@ static void ep_free(struct eventpoll *ep)mutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +if (ep->ws) +wakeup_source_unregister (ep->ws);kfree (ep); }
@@ -693,6 +709,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }
@@ -877,8 +894,10 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -1034,6 +1053,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */
@@ -1061,6 +1104,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;
@@ -1106,6 +1156,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1146,6 +1197,10 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;
@@ -1167,6 +1222,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even*/ epi->event.events = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because
@@ -1182,6 +1243,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_evenspin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1217,6 +1279,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) &
@@ -1232,6 +1295,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,if (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;
@@ -1251,6 +1315,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }
include/linux/eventpoll.h +6 -0
@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Signature · 1 line
1.7.7.3
PATCH 2/2 PM / Sleep: Add wakeup_source_activate and wakeup_source_deactivate tracepoints
Add tracepoints to wakeup_source_activate and wakeup_source_deactivate. Useful for checking that specific wakeup sources overlap as expected.
--- drivers/base/power/wakeup.c | 12 +++++++++--- include/trace/events/power.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-)
drivers/base/power/wakeup.c +9 -3
@@ -14,6 +14,7 @@#include <linux/suspend.h> #include <linux/seq_file.h> #include <linux/debugfs.h> +#include <trace/events/power.h> #include "power.h"
@@ -375,6 +376,8 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_enable);*/ static void wakeup_source_activate (struct wakeup_source *ws) { +unsigned int cec; + ws->active = true; ws->active_count++; ws->last_time =ktime_get ();
@@ -382,7 +385,9 @@ static void wakeup_source_activate(struct wakeup_source *ws) ws->start_prevent_time = ws->last_time;/* Increment the counter of events in progress. */ -atomic_inc (&combined_event_count); + cec =atomic_inc_return (&combined_event_count); + +trace_wakeup_source_activate (ws->name, cec); }/**
@@ -468,7 +473,7 @@ static inline void update_prevent_sleep_time(struct wakeup_source *ws,*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { -unsigned int cnt, inpr; +unsigned int cnt, inpr, cec;ktime_t duration;ktime_t now;
@@ -506,7 +511,8 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)* Increment the counter of registered wakeup events and decrement the * couter of wakeup events in progress simultaneously. */ -atomic_add (MAX_IN_PROGRESS , &combined_event_count); + cec =atomic_add_return (MAX_IN_PROGRESS , &combined_event_count); +trace_wakeup_source_deactivate (ws->name, cec);split_counters (&cnt, &inpr);if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {
include/trace/events/power.h +34 -0
@@ -65,6 +65,40 @@ TRACE_EVENT(machine_suspend,TP_printk ("state=%lu" , (unsigned long )__entry->state) ); +DECLARE_EVENT_CLASS (wakeup_source, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state), + +TP_STRUCT__entry ( + __string( name, name ) +__field ( u64, state ) + ), + +TP_fast_assign ( + __assign_str(name, name); + __entry->state = state; + ), + +TP_printk ("%s state=0x%lx" ,__get_str (name), + (unsigned long )__entry->state) +); + +DEFINE_EVENT (wakeup_source, wakeup_source_activate, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state) +); + +DEFINE_EVENT (wakeup_source, wakeup_source_deactivate, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state) +); +/* This code will be removed after deprecation time exceeded (2.6.41) */ #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED
Signature · 1 line
1.7.7.3
RFC PATCH 5/7 PM / Sleep: Implement opportunistic sleep
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep.
That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- Documentation/ABI/testing/sysfs-power | 17 +++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++- kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 98 ++++++++++++++++++++++++++++++ kernel/power/main.c | 108 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 266 insertions(+), 35 deletions(-)
kernel/power/Makefile +1 -0
@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +98 -0
98 lines not on this page · raw message
kernel/power/main.c +90 -18
160 lines not on this page · raw message
drivers/base/power/wakeup.c +22 -16
@@ -492,8 +492,10 @@ static void wakeup_source_deactivate(stratomic_add (MAX_IN_PROGRESS , &combined_event_count);split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/**
@@ -654,29 +656,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Documentation/ABI/testing/sysfs-power +17 -0
@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.
Re: [RFC][PATCH 5/7] PM / Sleep: Implement opportunistic sleep
Quoted 156 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.That work item enables the detection of wakeup events using the functions already defined in
drivers/base/power/wakeup.c(with one small modification) and calls eitherpm_suspend(), orhibernate()to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.Signed-off-by Rafael J. Wysocki <[email protected]>--- Documentation/ABI/testing/sysfs-power | 17 +++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++- kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 98 ++++++++++++++++++++++++++++++ kernel/power/main.c | 108 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 266 insertions(+), 35 deletions(-)Index: linux/kernel/power/Makefile
===================================================================kernel/power/Makefile +1
-0@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.oIndex: linux/kernel/power/Kconfig
===================================================================kernel/power/Kconfig +8
-0@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIMIndex:
linux/kernel/power/power.h
===================================================================kernel/power/power.h +18
-0@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */ Index:
linux/include/linux/suspend.h
===================================================================include/linux/suspend.h +12
-1@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture Index:
linux/kernel/power/autosleep.c
===================================================================kernel/power/autosleep.c new file +30
-0@@ -0,0 +1,98 @@ +/* +* kernel/power/autosleep.c +* +* Opportunistic sleep support. +* +* Copyright (C) 2012 Rafael J. Wysocki <[email protected]> +*/ + +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/pm_wakeup.h> + +#include "power.h" + +static suspend_state_t autosleep_state; +static struct workqueue_struct *autosleep_wq; +static DEFINE_MUTEX (autosleep_lock); + +static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out;
kernel/power/autosleep.c line 40
Quoted 10 lines
kernel/power/autosleep.c new file +10
-0+ } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate (); +else +pm_suspend (autosleep_state);
We are calling pm_suspend() or hibernate() directly here.
Won't this break build when CONFIG_SUSPEND or CONFIG_HIBERNATION is not set? CONFIG_PM_AUTOSLEEP depends only on PM_SLEEP which means we could enable either one of suspend or hibernation and yet come to this point, breaking the option which was not enabled.
Regards,
Srivatsa S. Bhat
Quoted 319 lines
kernel/power/autosleep.c new file +58
-0+ +mutex_unlock (&autosleep_lock); + +if (!pm_get_wakeup_count (&final_count, false)) + goto out; + +if (final_count == initial_count) +schedule_timeout (HZ /2 ); + + out: +queue_up_suspend_work (); +} + +static DECLARE_WORK(suspend_work, try_to_suspend); + +void queue_up_suspend_work (void ) +{ +if (!work_pending (&suspend_work) && autosleep_state >PM_SUSPEND_ON ) +queue_work (autosleep_wq, &suspend_work); +} + +suspend_state_t pm_autosleep_state (void ) +{ +return autosleep_state; +} + +int pm_autosleep_set_state (suspend_state_t state) +{ +#ifndef CONFIG_HIBERNATION +if (state >=PM_SUSPEND_MAX ) +return -EINVAL ; +#endif +mutex_lock (&autosleep_lock); +if (state ==PM_SUSPEND_ON && autosleep_state !=PM_SUSPEND_ON ) { + autosleep_state =PM_SUSPEND_ON ; + }else if (state >PM_SUSPEND_ON ) { + autosleep_state = state; +queue_up_suspend_work (); + } +mutex_unlock (&autosleep_lock); +return 0 ; +} + +void pm_autosleep_lock (void ) +{ +mutex_lock (&autosleep_lock); +} + +void pm_autosleep_unlock (void ) +{ +mutex_unlock (&autosleep_lock); +} + +int __init pm_autosleep_init (void ) +{ + autosleep_wq =alloc_ordered_workqueue ("autosleep" ,0 ); +return autosleep_wq ?0 : -ENOMEM ; +}Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c +90
-18@@ -269,8 +269,7 @@ static ssize_t state_show(struct kobjectreturn (s - buf); } -static ssize_t state_store (struct kobject *kobj,struct kobj_attribute *attr, -const char *buf,size_t n) +static suspend_state_t decode_state (const char *buf,size_t n) {#ifdef CONFIG_SUSPEND suspend_state_t state =PM_SUSPEND_STANDBY ;@@ -278,27 +277,43 @@ static ssize_t state_store(struct kobjec#endif char *p;int len; -int error = -EINVAL ; p =memchr (buf,'\n' , n); len = p ? p - buf : n; -/* First, check if we are requested to hibernate */ -if (len ==4 && !strncmp (buf,"disk" , len)) { - error =hibernate (); - goto Exit; - } +/* Check hibernation first. */ +if (len ==4 && !strncmp (buf,"disk" , len)) +return PM_SUSPEND_MAX ;#ifdef CONFIG_SUSPEND -for (s = &pm_states[state]; state <PM_SUSPEND_MAX ; s++, state++) { -if (*s && len ==strlen (*s) && !strncmp (buf, *s, len)) { - error =pm_suspend (state); -break ; - } - } +for (s = &pm_states[state]; state <PM_SUSPEND_MAX ; s++, state++) +if (*s && len ==strlen (*s) && !strncmp (buf, *s, len)) +return state;#endif - Exit: +return PM_SUSPEND_ON ; +} + +static ssize_t state_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +suspend_state_t state; +int error = -EINVAL ; + +pm_autosleep_lock (); +if (pm_autosleep_state () >PM_SUSPEND_ON ) { + error = -EBUSY ; + goto out; + } + + state =decode_state (buf, n); +if (state <PM_SUSPEND_MAX ) + error =pm_suspend (state); +else if (state >PM_SUSPEND_ON ) + error =hibernate (); + + out: +pm_autosleep_unlock ();return error ? error : n; }@@ -339,7 +354,8 @@ static ssize_t wakeup_count_show(struct {unsigned int val; -return pm_get_wakeup_count (&val) ?sprintf (buf,"%u\n" , val) : -EINTR ; +return pm_get_wakeup_count (&val, true) ? +sprintf (buf,"%u\n" , val) : -EINTR ; } static ssize_t wakeup_count_store(struct kobject *kobj,@@ -347,15 +363,65 @@ static ssize_t wakeup_count_store(struct const char *buf, size_t n) { unsigned int val; +int error = -EINVAL ; + +pm_autosleep_lock (); +if (pm_autosleep_state () >PM_SUSPEND_ON ) { + error = -EBUSY ; + goto out; + }if (sscanf (buf,"%u" , &val) ==1 ) {if (pm_save_wakeup_count (val))return n; } -return -EINVAL ; + + out: +pm_autosleep_unlock (); +return error; }power_attr (wakeup_count); + +#ifdef CONFIG_PM_AUTOSLEEP +static ssize_t autosleep_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +suspend_state_t state =pm_autosleep_state (); + +if (state ==PM_SUSPEND_ON ) +return sprintf (buf,"off\n" ); + +#ifdef CONFIG_SUSPEND +if (state <PM_SUSPEND_MAX ) +return sprintf (buf,"%s\n" ,valid_state (state) ? + pm_states[state] :"error" ); +#endif +#ifdef CONFIG_HIBERNATION +return sprintf (buf,"disk\n" ); +#else +return sprintf (buf,"error" ); +#endif +} + +static ssize_t autosleep_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +suspend_state_t state =decode_state (buf, n); +int error; + +if (state ==PM_SUSPEND_ON &&strncmp (buf,"off" ,3 ) + &&strncmp (buf,"off\n" ,4 )) +return -EINVAL ; + + error =pm_autosleep_set_state (state); +return error ? error : n; +} + +power_attr (autosleep); +#endif /* CONFIG_PM_AUTOSLEEP */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE @@ -409,6 +475,9 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, &wakeup_count_attr.attr, +#ifdef CONFIG_PM_AUTOSLEEP + &autosleep_attr.attr, +#endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif@@ -444,7 +513,10 @@ static int __init pm_init(void) power_kobj =kobject_create_and_add ("power" ,NULL );if (!power_kobj)return -ENOMEM ; -return sysfs_create_group (power_kobj, &attr_group); + error =sysfs_create_group (power_kobj, &attr_group); +if (error) +return error; +return pm_autosleep_init (); }core_initcall (pm_init);Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +22
-16@@ -492,8 +492,10 @@ static void wakeup_source_deactivate(stratomic_add (MAX_IN_PROGRESS , &combined_event_count);split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/** @@ -654,29 +656,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;Index: linux/Documentation/ABI/testing/sysfs-power
===================================================================Documentation/ABI/testing/sysfs-power +17
-0@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.
Re: [RFC][PATCH 5/7] PM / Sleep: Implement opportunistic sleep
Quoted 167 lines · Srivatsa S. Bhat
Quoted 165 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.That work item enables the detection of wakeup events using the functions already defined in
drivers/base/power/wakeup.c(with one small modification) and calls eitherpm_suspend(), orhibernate()to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.Signed-off-by Rafael J. Wysocki <[email protected]>--- Documentation/ABI/testing/sysfs-power | 17 +++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++- kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 98 ++++++++++++++++++++++++++++++ kernel/power/main.c | 108 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 266 insertions(+), 35 deletions(-)Index: linux/kernel/power/Makefile
===================================================================kernel/power/Makefile +1
-0@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.oIndex: linux/kernel/power/Kconfig
===================================================================kernel/power/Kconfig +8
-0@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIMIndex:
linux/kernel/power/power.h
===================================================================kernel/power/power.h +18
-0@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */ Index:
linux/include/linux/suspend.h
===================================================================include/linux/suspend.h +12
-1@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture Index:
linux/kernel/power/autosleep.c
===================================================================kernel/power/autosleep.c new file +39
-0@@ -0,0 +1,98 @@ +/* +* kernel/power/autosleep.c +* +* Opportunistic sleep support. +* +* Copyright (C) 2012 Rafael J. Wysocki <[email protected]> +*/ + +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/pm_wakeup.h> + +#include "power.h" + +static suspend_state_t autosleep_state; +static struct workqueue_struct *autosleep_wq; +static DEFINE_MUTEX (autosleep_lock); + +static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out; + } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate (); +else
Quoted 8 lines
kernel/power/autosleep.c line 40
Quoted 1 line
kernel/power/autosleep.c new file +1
-0+pm_suspend (autosleep_state);We are calling
pm_suspend()orhibernate()directly here.
Won't this break build whenCONFIG_SUSPENDorCONFIG_HIBERNATIONis not set?CONFIG_PM_AUTOSLEEPdepends only onPM_SLEEPwhich means we could enable either one of suspend or hibernation and yet come to this point, breaking the option which was not enabled.
Both pm_suspend() and hibernate() have appropriate static inline definitions for !CONFIG_SUSPEND and !CONFIG_HIBERNATION (in suspend.h), as far as I can say.
Thanks,
Rafael
Re: [RFC][PATCH 5/7] PM / Sleep: Implement opportunistic sleep
Quoted 177 lines · Rafael J. Wysocki
Quoted 167 lines · Srivatsa S. Bhat
Quoted 165 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.That work item enables the detection of wakeup events using the functions already defined in
drivers/base/power/wakeup.c(with one small modification) and calls eitherpm_suspend(), orhibernate()to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.Signed-off-by Rafael J. Wysocki <[email protected]>--- Documentation/ABI/testing/sysfs-power | 17 +++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++- kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 98 ++++++++++++++++++++++++++++++ kernel/power/main.c | 108 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 266 insertions(+), 35 deletions(-)Index: linux/kernel/power/Makefile
===================================================================kernel/power/Makefile +1
-0@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.oIndex: linux/kernel/power/Kconfig
===================================================================kernel/power/Kconfig +8
-0@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIMIndex:
linux/kernel/power/power.h
===================================================================kernel/power/power.h +18
-0@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern void pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline void pm_autosleep_lock (void ) {} +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */ Index:
linux/include/linux/suspend.h
===================================================================include/linux/suspend.h +12
-1@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture Index:
linux/kernel/power/autosleep.c
===================================================================kernel/power/autosleep.c new file +39
-0@@ -0,0 +1,98 @@ +/* +* kernel/power/autosleep.c +* +* Opportunistic sleep support. +* +* Copyright (C) 2012 Rafael J. Wysocki <[email protected]> +*/ + +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/pm_wakeup.h> + +#include "power.h" + +static suspend_state_t autosleep_state; +static struct workqueue_struct *autosleep_wq; +static DEFINE_MUTEX (autosleep_lock); + +static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out; + } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate (); +else Quoted 8 lines
kernel/power/autosleep.c line 40
Quoted 1 line
kernel/power/autosleep.c new file +1
-0+pm_suspend (autosleep_state);We are calling
pm_suspend()orhibernate()directly here.
Won't this break build whenCONFIG_SUSPENDorCONFIG_HIBERNATIONis not set?CONFIG_PM_AUTOSLEEPdepends only onPM_SLEEPwhich means we could enable either one of suspend or hibernation and yet come to this point, breaking the option which was not enabled.
Quoted 3 lines
Both
pm_suspend()andhibernate()have appropriate static inline definitions for !CONFIG_SUSPEND and !CONFIG_HIBERNATION (insuspend.h), as far as I can say.
Oh, you are right.. I overlooked that, sorry!
Regards,
Srivatsa S. Bhat
RFC PATCH 6/7 PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- Documentation/ABI/testing/sysfs-devices-power | 11 ++++ drivers/base/power/sysfs.c | 24 ++++++++++ drivers/base/power/wakeup.c | 61 ++++++++++++++++++++++++-- include/linux/pm_wakeup.h | 4 + include/linux/suspend.h | 1 kernel/power/autosleep.c | 2 6 files changed, 99 insertions(+), 4 deletions(-)
include/linux/pm_wakeup.h +4 -0
@@ -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
drivers/base/power/wakeup.c +57 -4
116 lines not on this page · raw message
include/linux/suspend.h +1 -0
@@ -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 ) {
kernel/power/autosleep.c +2 -0
@@ -73,8 +73,10 @@ int pm_autosleep_set_state(suspend_statemutex_lock (&autosleep_lock);if (state ==PM_SUSPEND_ON && autosleep_state !=PM_SUSPEND_ON ) { autosleep_state =PM_SUSPEND_ON ; +pm_wakep_autosleep_enabled (false); }else if (state >PM_SUSPEND_ON ) { autosleep_state = state; +pm_wakep_autosleep_enabled (true);queue_up_suspend_work (); }mutex_unlock (&autosleep_lock);
drivers/base/power/sysfs.c +24 -0
@@ -391,6 +391,27 @@ static ssize_t wakeup_last_time_show(str } 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
@@ -485,6 +506,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, };
Documentation/ABI/testing/sysfs-devices-power +11 -0
@@ -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 <[email protected]> +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 <[email protected]>
RFC PATCH 7/7 PM / Sleep: Add user space interface for manipulating wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Android allows user space to manipulate wakelocks using two sysfs file located in /sys/power/, wake_lock and wake_unlock. Writing a wakelock name and optionally a timeout to the wake_lock file causes the wakelock whose name was written to be acquired (it is created before is necessary), optionally with the given timeout. Writing the name of a wakelock to wake_unlock causes that wakelock to be released.
Implement an analogous interface for user space using wakeup sources. Add the /sys/power/wake_lock and /sys/power/wake_unlock files allowing user space to create, activate and deactivate wakeup sources, such that writing a name and optionally a timeout to wake_lock causes the wakeup source of that name to be activated, optionally with the given timeout. If that wakeup source doesn't exist, it will be created and then activated. Writing a name to wake_unlock causes the wakeup source of that name, if there is one, to be deactivated. Wakeup sources created with the help of wake_lock that haven't been used for more than 5 minutes are garbage collected and destroyed. Moreover, there can be only WL_NUMBER_LIMIT wakeup sources created with the help of wake_lock present at a time.
The data type used to track wakeup sources created by user space is called "struct wakelock" to indicate the origins of this feature.
--- Documentation/ABI/testing/sysfs-power | 42 ++++++ drivers/base/power/wakeup.c | 1 kernel/power/Kconfig | 8 + kernel/power/Makefile | 1 kernel/power/main.c | 41 ++++++ kernel/power/power.h | 9 + kernel/power/wakelock.c | 218 ++++++++++++++++++++++++++++++++++ 7 files changed, 320 insertions(+)
kernel/power/main.c +41 -0
@@ -422,6 +422,43 @@ static ssize_t autosleep_store(struct kopower_attr (autosleep);#endif /* CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS +static ssize_t wake_lock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, true); +} + +static ssize_t wake_lock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_lock (buf); +return error ? error : n; +} + +power_attr (wake_lock); + +static ssize_t wake_unlock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, false); +} + +static ssize_t wake_unlock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_unlock (buf); +return error ? error : n; +} + +power_attr (wake_unlock); + +#endif /* CONFIG_PM_WAKELOCKS */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE
@@ -478,6 +515,10 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr,#endif +#ifdef CONFIG_PM_WAKELOCKS + &wake_lock_attr.attr, + &wake_unlock_attr.attr, +#endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif
kernel/power/power.h +9 -0
@@ -282,3 +282,12 @@ static inline void pm_autosleep_unlock(vstatic inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; }#endif /* !CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS + +/* kernel/power/wakelock.c */ +extern ssize_t pm_show_wakelocks (char *buf,bool show_active); +extern int pm_wake_lock (const char *buf); +extern int pm_wake_unlock (const char *buf); + +#endif /* !CONFIG_PM_WAKELOCKS */
kernel/power/Kconfig +8 -0
@@ -111,6 +111,14 @@ config PM_AUTOSLEEP Allow the kernel to trigger a system transition into a global sleep state automatically whenever there are no active wakeup sources. +config PM_WAKELOCKS + bool "User space wakeup sources interface" + depends on PM_SLEEP + default n + ---help--- + Allow user space to create, activate and deactivate wakeup source + objects with the help of a sysfs-based interface. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/wakelock.c new file +218 -0
218 lines not on this page · raw message
kernel/power/Makefile +1 -0
@@ -10,5 +10,6 @@ obj-$(CONFIG_PM_TEST_SUSPEND) += suspend obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o +obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
drivers/base/power/wakeup.c +1 -0
@@ -132,6 +132,7 @@ void wakeup_source_add(struct wakeup_souspin_lock_init (&ws->lock);setup_timer (&ws->timer, pm_wakeup_timer_fn, (unsigned long )ws); ws->active = false; + ws->last_time =ktime_get ();spin_lock_irq (&events_lock);list_add_rcu (&ws->entry, &wakeup_sources);
Documentation/ABI/testing/sysfs-power +42 -0
45 lines not on this page · raw message
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take 2
In reply to Rafael J. WysockiQuoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash D ffff880015714010
===============================
[ INFO: suspicious RCU usage. ]
3.3.0-rc3john+ #131 Not tainted
-------------------------------kernel/sched/core.c:4784 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 1, debug_locks = 0 5 locks held by kworker/u:1/10:
#0: (autosleep){.+.+.+}, at: [<ffffffff81066db8>] process_one_work+0x2d8/0x8c0
#1: (suspend_work){+.+.+.}, at: [<ffffffff81066db8>] process_one_work+0x2d8/0x8c0
#2: (autosleep_lock){+.+.+.}, at: [<ffffffff810a2d3d>] try_to_suspend+0x2d/0xe0
#3: (pm_mutex){+.+.+.}, at: [<ffffffff8109b9fc>] pm_suspend+0x8c/0x210
#4: (tasklist_lock){.+.+..}, at: [<ffffffff8109b0f1>] try_to_freeze_tasks+0x2d1/0x400stack backtrace:
Pid: 10, comm: kworker/u:1 Not tainted 3.3.0-rc3john+ #131
Call Trace:
[<ffffffff81040d82>] ? vprintk+0x242/0x530
[<ffffffff810b0fdb>] lockdep_rcu_suspicious+0xeb/0x100
[<ffffffff81083371>] sched_show_task+0x121/0x180
[<ffffffff8109b1e5>] try_to_freeze_tasks+0x3c5/0x400
[<ffffffff810a2d10>] ? pm_autosleep_set_state+0x80/0x80
[<ffffffff8109b2eb>] freeze_processes+0x3b/0xb0
[<ffffffff8109baad>] pm_suspend+0x13d/0x210
[<ffffffff810a2d5d>] try_to_suspend+0x4d/0xe0
[<ffffffff81066f02>] process_one_work+0x422/0x8c0
[<ffffffff81066db8>] ? process_one_work+0x2d8/0x8c0
[<ffffffff810b063e>] ? put_lock_stats+0xe/0x40
[<ffffffff81067a16>] worker_thread+0x476/0x550
[<ffffffff810675a0>] ? rescuer_thread+0x200/0x200
[<ffffffff810706fe>] kthread+0xae/0xc0
[<ffffffff81af4cb4>] kernel_thread_helper+0x4/0x10
[<ffffffff81af3078>] ? retint_restore_args+0x13/0x13
[<ffffffff81070650>] ? __init_kthread_worker+0x70/0x70
[<ffffffff81af4cb0>] ? gs_change+0x13/0x13
0 1981 1980 0x00020004
ffff880015715d88 0000000000000046 ffff880015715c88 ffffffff8102c22b
ffff880015714010 ffff880015715fd8 ffff880015714010 ffff880015714000
ffff880015715fd8 ffff880015714000 ffff880015c4e3c0 ffff88001342e540
Call Trace:
[<ffffffff8102c22b>] ? kvm_clock_read+0x6b/0x90
[<ffffffff810b1f2d>] ? mark_held_locks+0xad/0x150
[<ffffffff81af10bf>] schedule+0x3f/0x60
[<ffffffff81aef33b>] mutex_lock_nested+0x1cb/0x4c0
[<ffffffff810a2cae>] ? pm_autosleep_set_state+0x1e/0x80
[<ffffffff810a2cae>] ? pm_autosleep_set_state+0x1e/0x80
[<ffffffff810a2cae>] pm_autosleep_set_state+0x1e/0x80
[<ffffffff8109a74b>] autosleep_store+0x3b/0x80
[<ffffffff813856e7>] kobj_attr_store+0x17/0x20
[<ffffffff81200dcc>] sysfs_write_file+0xec/0x170
[<ffffffff8118085f>] vfs_write+0x11f/0x1b0
[<ffffffff811809f4>] sys_write+0x54/0xa0
[<ffffffff81af4e66>] sysenter_dispatch+0x7/0x26
[<ffffffff8139238e>] ? trace_hardirqs_on_thunk+0x3a/0x3fRestarting tasks ... done.
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take 2
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010
Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the autosleep_lock is held for too long, that is, something like:
acquire autosleep_lock
modify autosleep_state
<============== "A"
pm_suspend or hibernate()
release autosleep_lock
At point marked "A", we should have released the autosleep lock and only then entered pm_suspend or hibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to /sys/power/state will end up waiting on autosleep_lock, thus failing the freezing operation.)
So the solution is to always release the autosleep lock before entering suspend/hibernation.
Regards,
Srivatsa S. Bhat
Quoted 62 lines
===============================
[ INFO: suspicious RCU usage. ]
3.3.0-rc3john+ #131 Not tainted
-------------------------------kernel/sched/core.c:4784suspiciousrcu_dereference_check()usage!other info that might help us debug this:
rcu_scheduler_active= 1,debug_locks= 0 5 locks held by kworker/u:1/10:#0: (autosleep){.+.+.+}, at: [<ffffffff81066db8>] process_one_work+0x2d8/0x8c0 #1: (suspend_work){+.+.+.}, at: [<ffffffff81066db8>] process_one_work+0x2d8/0x8c0 #2: (autosleep_lock){+.+.+.}, at: [<ffffffff810a2d3d>] try_to_suspend+0x2d/0xe0 #3: (pm_mutex){+.+.+.}, at: [<ffffffff8109b9fc>] pm_suspend+0x8c/0x210 #4: (tasklist_lock){.+.+..}, at: [<ffffffff8109b0f1>] try_to_freeze_tasks+0x2d1/0x400stack backtrace: Pid: 10, comm: kworker/u:1 Not tainted 3.3.0-rc3john+ #131 Call Trace: [<ffffffff81040d82>] ? vprintk+0x242/0x530 [<ffffffff810b0fdb>] lockdep_rcu_suspicious+0xeb/0x100 [<ffffffff81083371>] sched_show_task+0x121/0x180 [<ffffffff8109b1e5>] try_to_freeze_tasks+0x3c5/0x400 [<ffffffff810a2d10>] ? pm_autosleep_set_state+0x80/0x80 [<ffffffff8109b2eb>] freeze_processes+0x3b/0xb0 [<ffffffff8109baad>] pm_suspend+0x13d/0x210 [<ffffffff810a2d5d>] try_to_suspend+0x4d/0xe0 [<ffffffff81066f02>] process_one_work+0x422/0x8c0 [<ffffffff81066db8>] ? process_one_work+0x2d8/0x8c0 [<ffffffff810b063e>] ? put_lock_stats+0xe/0x40 [<ffffffff81067a16>] worker_thread+0x476/0x550 [<ffffffff810675a0>] ? rescuer_thread+0x200/0x200 [<ffffffff810706fe>] kthread+0xae/0xc0 [<ffffffff81af4cb4>] kernel_thread_helper+0x4/0x10 [<ffffffff81af3078>] ? retint_restore_args+0x13/0x13 [<ffffffff81070650>] ? __init_kthread_worker+0x70/0x70 [<ffffffff81af4cb0>] ? gs_change+0x13/0x13 0 1981 1980 0x00020004 ffff880015715d88 0000000000000046 ffff880015715c88 ffffffff8102c22b ffff880015714010 ffff880015715fd8 ffff880015714010 ffff880015714000 ffff880015715fd8 ffff880015714000 ffff880015c4e3c0 ffff88001342e540 Call Trace: [<ffffffff8102c22b>] ? kvm_clock_read+0x6b/0x90 [<ffffffff810b1f2d>] ? mark_held_locks+0xad/0x150 [<ffffffff81af10bf>] schedule+0x3f/0x60 [<ffffffff81aef33b>] mutex_lock_nested+0x1cb/0x4c0 [<ffffffff810a2cae>] ? pm_autosleep_set_state+0x1e/0x80 [<ffffffff810a2cae>] ? pm_autosleep_set_state+0x1e/0x80 [<ffffffff810a2cae>] pm_autosleep_set_state+0x1e/0x80 [<ffffffff8109a74b>] autosleep_store+0x3b/0x80 [<ffffffff813856e7>] kobj_attr_store+0x17/0x20 [<ffffffff81200dcc>] sysfs_write_file+0xec/0x170 [<ffffffff8118085f>] vfs_write+0x11f/0x1b0 [<ffffffff811809f4>] sys_write+0x54/0xa0 [<ffffffff81af4e66>] sysenter_dispatch+0x7/0x26 [<ffffffff8139238e>] ? trace_hardirqs_on_thunk+0x3a/0x3fRestarting tasks ... done.
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 39 lines · Srivatsa S. Bhat
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the
autosleep_lockis held for too long, that is, something like:acquire
autosleep_lock
modifyautosleep_state<============== "A"
pm_suspendorhibernate()release
autosleep_lock
Quoted 8 lines
At point marked "A", we should have released the autosleep lock and only then entered
pm_suspendorhibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to/sys/power/statewill end up waiting onautosleep_lock, thus failing the freezing operation.)So the solution is to always release the autosleep lock before entering suspend/hibernation.
Well, the autosleep lock is intentionally held around suspend/hibernation in try_to_suspend(), because otherwise it would be possible to trigger automatic suspend right after user space has disabled it.
I think the solution is to make pm_autosleep_lock() do a _trylock() and return error code if already locked.
Thanks,
Rafael
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 46 lines · Rafael J. Wysocki
Quoted 45 lines · Srivatsa S. Bhat
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the
autosleep_lockis held for too long, that is, something like:acquire
autosleep_lock
modifyautosleep_state<============== "A"
pm_suspendorhibernate()release
autosleep_lockAt point marked "A", we should have released the autosleep lock and only then entered
pm_suspendorhibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to/sys/power/statewill end up waiting onautosleep_lock, thus failing the freezing operation.)
Quoted 7 lines
Quoted 2 lines
So the solution is to always release the autosleep lock before entering suspend/hibernation.
Well, the autosleep lock is intentionally held around suspend/hibernation in
try_to_suspend(), because otherwise it would be possible to trigger automatic suspend right after user space has disabled it.
Hmm.. I was just wondering if we could avoid holding yet another lock in the suspend/hibernate path, if possible..
Quoted 3 lines
I think the solution is to make
pm_autosleep_lock()do a_trylock()and return error code if already locked.
... and also do a trylock() in pm_autosleep_set_state() right?.... that is where John hit the problem..
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Regards,
Srivatsa S. Bhat
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 65 lines · Srivatsa S. Bhat
Quoted 46 lines · Rafael J. Wysocki
Quoted 45 lines · Srivatsa S. Bhat
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the
autosleep_lockis held for too long, that is, something like:acquire
autosleep_lock
modifyautosleep_state<============== "A"
pm_suspendorhibernate()release
autosleep_lockAt point marked "A", we should have released the autosleep lock and only then entered
pm_suspendorhibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to/sys/power/statewill end up waiting onautosleep_lock, thus failing the freezing operation.)Quoted 7 lines
Quoted 2 lines
So the solution is to always release the autosleep lock before entering suspend/hibernation.
Well, the autosleep lock is intentionally held around suspend/hibernation in
try_to_suspend(), because otherwise it would be possible to trigger automatic suspend right after user space has disabled it.Hmm.. I was just wondering if we could avoid holding yet another lock in the suspend/hibernate path, if possible..
Quoted 3 lines
I think the solution is to make
pm_autosleep_lock()do a_trylock()and return error code if already locked.
Quoted 6 lines
... and also do a
trylock()inpm_autosleep_set_state()right?.... that is where John hit the problem..By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
That's a good point, so I think it may be a good idea to do mutex_lock_interruptible() in pm_autosleep_set_state() instead.
Thanks,
Rafael
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 69 lines · Rafael J. Wysocki
Quoted 68 lines · Srivatsa S. Bhat
Quoted 46 lines · Rafael J. Wysocki
Quoted 45 lines · Srivatsa S. Bhat
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the
autosleep_lockis held for too long, that is, something like:acquire
autosleep_lock
modifyautosleep_state<============== "A"
pm_suspendorhibernate()release
autosleep_lockAt point marked "A", we should have released the autosleep lock and only then entered
pm_suspendorhibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to/sys/power/statewill end up waiting onautosleep_lock, thus failing the freezing operation.)Quoted 7 lines
Quoted 2 lines
So the solution is to always release the autosleep lock before entering suspend/hibernation.
Well, the autosleep lock is intentionally held around suspend/hibernation in
try_to_suspend(), because otherwise it would be possible to trigger automatic suspend right after user space has disabled it.Hmm.. I was just wondering if we could avoid holding yet another lock in the suspend/hibernate path, if possible..
Quoted 3 lines
I think the solution is to make
pm_autosleep_lock()do a_trylock()and return error code if already locked.... and also do a
trylock()inpm_autosleep_set_state()right?.... that is where John hit the problem..
Quoted 6 lines
Quoted 3 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.
Now that I think of it, perhaps it's a good idea to just make pm_autosleep_lock() do mutex_lock_interruptible() _and_ make pm_autosleep_set_state() use pm_autosleep_lock().
What do you think?
Rafael
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 74 lines · Rafael J. Wysocki
Quoted 73 lines · Rafael J. Wysocki
Quoted 71 lines · Srivatsa S. Bhat
Quoted 46 lines · Rafael J. Wysocki
Quoted 45 lines · Srivatsa S. Bhat
Quoted 8 lines · John Stultz
Quoted 6 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 13 lines
Hey Rafael,
Thanks again for posting this! I've started playing around with it in a
kvm environment, and got the following warning after echoing off > autosleep:
...
PM: resume of devices complete after 185.615 msecs
PM: Finishing wakeup.
Restarting tasks ... done.
PM: Syncing filesystems ... done.
PM: Preparing system for mem sleep
Freezing user space processes ...
Freezing of tasks failed after 20.01 seconds (1 tasks refusing to freeze, wq_busy=0):
bash Dffff880015714010Ah.. I think I know what is the problem here..
The kernel was freezing userspace processes and meanwhile, you wrote "off" to autosleep. So, as a result, this userspace process (bash) just now entered kernel mode. Unfortunately, the
autosleep_lockis held for too long, that is, something like:acquire
autosleep_lock
modifyautosleep_state<============== "A"
pm_suspendorhibernate()release
autosleep_lockAt point marked "A", we should have released the autosleep lock and only then entered
pm_suspendorhibernate(). Since the current code holds the lock and enters suspend/hibernate, the userspace process that wrote "off" to autosleep (or even userspace process that writes to/sys/power/statewill end up waiting onautosleep_lock, thus failing the freezing operation.)Quoted 7 lines
Quoted 2 lines
So the solution is to always release the autosleep lock before entering suspend/hibernation.
Well, the autosleep lock is intentionally held around suspend/hibernation in
try_to_suspend(), because otherwise it would be possible to trigger automatic suspend right after user space has disabled it.Hmm.. I was just wondering if we could avoid holding yet another lock in the suspend/hibernate path, if possible..
Quoted 3 lines
I think the solution is to make
pm_autosleep_lock()do a_trylock()and return error code if already locked.... and also do a
trylock()inpm_autosleep_set_state()right?.... that is where John hit the problem..By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think mutex_lock_interruptible() would help us much.. Consider what would happen, if we use it:
* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the
autosleep lock - which is a different (but yet another) problem).
So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the mutex_lock_interruptible() in a loop and so on, but that gets very ugly pretty soon.
So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to /sys/power/state or
/sys/power/autosleep should not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate
attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.
And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)
Regards,
Srivatsa S. Bhat
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 5 lines · Srivatsa S. Bhat
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Srivatsa S. Bhat
On 02/23/2012 03:40 AM, Rafael J. Wysocki wrote:
[...]
Quoted 56 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think
mutex_lock_interruptible()would help us much.. Consider what would happen, if we use it:* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the
autosleep lock - which is a different (but yet another) problem).So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the
mutex_lock_interruptible()in a loop and so on, but that gets very ugly pretty soon.So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to/sys/power/stateor
/sys/power/autosleepshould not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate
attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.
Quoted 4 lines
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)
Well, you essentially are postulating to restore the "interface" wakeup source that was present in the previous version of this patch and that I dropped in order to simplify the code.
I guess I can do that ...
Thanks,
Rafael
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 68 lines · Rafael J. Wysocki
Quoted 5 lines · Srivatsa S. Bhat
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Srivatsa S. Bhat
On 02/23/2012 03:40 AM, Rafael J. Wysocki wrote:
[...]
Quoted 56 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think
mutex_lock_interruptible()would help us much.. Consider what would happen, if we use it:* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the autosleep lock - which is a different (but yet another) problem).So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the
mutex_lock_interruptible()in a loop and so on, but that gets very ugly pretty soon.So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to/sys/power/stateor/sys/power/autosleepshould not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.Quoted 4 lines
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)
Quoted 6 lines
Well, you essentially are postulating to restore the "interface" wakeup source that was present in the previous version of this patch and that I dropped in order to simplify the code.
I guess I can do that ...
If this wakeup source is reported as active whenever user-space has not requested suspend that would be useful in the stats. It does not look like your original patch did this however, but you could have a main wakeup-source that you release when any form of suspend is requested and activate when turning off auto suspend or returning from a one-shot suspend operation.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 73 lines · Arve Hjønnevåg
Quoted 72 lines · Rafael J. Wysocki
Quoted 5 lines · Srivatsa S. Bhat
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Srivatsa S. Bhat
On 02/23/2012 03:40 AM, Rafael J. Wysocki wrote:
[...]
Quoted 56 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think
mutex_lock_interruptible()would help us much.. Consider what would happen, if we use it:* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the
autosleep lock - which is a different (but yet another) problem).So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the
mutex_lock_interruptible()in a loop and so on, but that gets very ugly pretty soon.So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to/sys/power/stateor
/sys/power/autosleepshould not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate
attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.Quoted 4 lines
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)Well, you essentially are postulating to restore the "interface" wakeup source that was present in the previous version of this patch and that I dropped in order to simplify the code.
Quoted 6 lines
Quoted 2 lines
I guess I can do that ...
If this wakeup source is reported as active whenever user-space has not requested suspend that would be useful in the stats. It does not look like your original patch did this however,
No, it didn't.
Quoted 4 lines
but you could have a
main wakeup-source that you release when any form of suspend is requested and activate when turning off auto suspend or returning from a one-shot suspend operation.
I honestly don't think I can do that and handle the /sys/power/wakeup_count
-> /sys/power/state handoff (which is used by OLPC, as we've learnt recently)
sanely at the same time. OTOH, I don't want CONFIG_AUTOSLEEP to disable that interface entirely, because things like that basically prevent people from trying alternative features, which is essential to us for "interesting feedback" reasons.
So, my "main" wakeup source is only going to register the number of times user space has (successfully) written to /sysp/power/autosleep (please have a look at the updated patch I'm going to send in a reply to Srivatsa in a little while).
Thanks,
Rafael
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
In reply to Rafael J. WysockiQuoted 63 lines · Rafael J. Wysocki
Quoted 5 lines · Srivatsa S. Bhat
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Srivatsa S. Bhat
On 02/23/2012 03:40 AM, Rafael J. Wysocki wrote:
[...]
Quoted 56 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think
mutex_lock_interruptible()would help us much.. Consider what would happen, if we use it:* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the
autosleep lock - which is a different (but yet another) problem).So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the
mutex_lock_interruptible()in a loop and so on, but that gets very ugly pretty soon.So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to/sys/power/stateor
/sys/power/autosleepshould not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate
attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.
Quoted 9 lines
Quoted 4 lines
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)Well, you essentially are postulating to restore the "interface" wakeup source that was present in the previous version of this patch and that I dropped in order to simplify the code.
Oh is it? I guess I haven't followed this thread very closely...
Quoted 2 lines
I guess I can do that ...
Oh by the way, this scheme doesn't solve all problems. It might be effective in reacting "instantly" to a request by the user to *switch off* autosleep. But say, when the user wants to switch to suspend instead of hibernate as the autosleep preference, for example, I don't think it would be as quick in responding... (I mean, it might do the old operation one more time before switching to the new one..)
But I guess at this point it might be wiser to say "sigh.. we can do only so much..." instead of complicating the code too much in an attempt to meet everybody's expectations :-)
Regards,
Srivatsa S. Bhat
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 89 lines · Srivatsa S. Bhat
Quoted 63 lines · Rafael J. Wysocki
Quoted 5 lines · Srivatsa S. Bhat
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Srivatsa S. Bhat
On 02/23/2012 03:40 AM, Rafael J. Wysocki wrote:
[...]
Quoted 56 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
By the way, I am just curious.. how difficult will this make it for userspace to disable autosleep? I mean, would a trylock mean that the user has to keep fighting until he finally gets a chance to disable autosleep?
Quoted 9 lines
Quoted 2 lines
That's a good point, so I think it may be a good idea to do
mutex_lock_interruptible()inpm_autosleep_set_state()instead.Now that I think of it, perhaps it's a good idea to just make
pm_autosleep_lock()domutex_lock_interruptible()_and_ makepm_autosleep_set_state()usepm_autosleep_lock().What do you think?
Well, I don't think
mutex_lock_interruptible()would help us much.. Consider what would happen, if we use it:* pm-suspend got initiated as part of autosleep. Acquired autosleep lock.
* Userspace is about to get frozen.
* Now, the user tries to write "off" to autosleep. And hence, he is waiting for autosleep lock, interruptibly.
* The freezer sent a fake signal to all userspace processes and hence this process also got interrupted.. it is no longer waiting on autosleep lock - it got the signal and returned, and got frozen.
(And when the userspace gets thawed later, this process won't have the
autosleep lock - which is a different (but yet another) problem).So ultimately the only thing we achieved is to ensure that freezing of userspace goes smoothly. But the user process could not succeed in disabling autosleep. Of course we can work around that by having the
mutex_lock_interruptible()in a loop and so on, but that gets very ugly pretty soon.So, I would suggest the following solution:
We want to achieve 2 things here:
a. A user process trying to write to/sys/power/stateor
/sys/power/autosleepshould not cause freezing failures.
b. When a user process writes "off" to autosleep, the suspend/hibernate
attempt that is on-going, if any, must be immediately aborted, to give the user the feeling that his preference has been noticed and respected.And to achieve this, we note that a user process can write "off" to autosleep only until the userspace gets frozen. No chance after that.
So, let's do this:
1. Drop the autosleep lock before entering pm-suspend/hibernate.
2. This means, a user process can get hold of this lock and successfully disable autosleep a moment after we initiated suspend, but before userspace got frozen fully.
3. So, to respect the user's wish, we add a check immediately after the freezing of userspace is complete - we check if the user disabled autosleep and bail out, if he did. Otherwise, we continue and suspend the machine.Quoted 9 lines
Quoted 4 lines
IOW, this is like hitting 2 birds with one stone ;-)
We don't hold autosleep lock throughout suspend/hibernate, but still react instantly when the user disables autosleep. And of course, freezing of tasks won't fail, ever! :-)Well, you essentially are postulating to restore the "interface" wakeup source that was present in the previous version of this patch and that I dropped in order to simplify the code.
Oh is it? I guess I haven't followed this thread very closely...
Quoted 2 lines
I guess I can do that ...
Oh by the way, this scheme doesn't solve all problems. It might be effective in reacting "instantly" to a request by the user to *switch off* autosleep. But say, when the user wants to switch to suspend instead of hibernate as the autosleep preference, for example, I don't think it would be as quick in responding... (I mean, it might do the old operation one more time before switching to the new one..)
Quoted 3 lines
But I guess at this point it might be wiser to say "sigh.. we can do only so much..." instead of complicating the code too much in an attempt to meet everybody's expectations :-)
I think we can do something like in the updated patch [5/7] below.
It uses a special wakeup source object called "autosleep" to bump up the number of wakeup events in progress before acquiring autosleep_lock in pm_autosleep_set_state(). This way, either pm_autosleep_set_state() will acquire autosleep_lock before try_to_suspend(), in which case the latter will see the change of autosleep_state immediately (after autosleep_lock has been passed to it), or try_to_suspend() will get it first, but then pm_save_wakeup_count() or pm_suspend()/hibernate() will see the nonzero counter of wakeup events in progress and return error code (sooner or later).
The drawback is that writes to /sys/power/autosleep may interfere with the /sys/power/wakeup_count + /sys/power/state interface by interrupting transitions started by writing to /sys/power/state, for example (although I think that's highly unlikely).
Additionally, I made pm_autosleep_lock() use mutex_trylock_interruptible() to prevent operations on /sys/power/wakeup_count and/or /sys/power/state from failing the freezing of tasks started by try_to_suspend().
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep.
That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- Documentation/ABI/testing/sysfs-power | 17 ++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++ kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 113 ++++++++++++++++++++++++++++++++ kernel/power/main.c | 117 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 290 insertions(+), 35 deletions(-)
kernel/power/Makefile +1 -0
@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern int pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline int pm_autosleep_lock (void ) {return 0 ; } +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +113 -0
113 lines not on this page · raw message
kernel/power/main.c +99 -18
169 lines not on this page · raw message
drivers/base/power/wakeup.c +22 -16
@@ -492,8 +492,10 @@ static void wakeup_source_deactivate(stratomic_add (MAX_IN_PROGRESS , &combined_event_count);split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/**
@@ -654,29 +656,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Documentation/ABI/testing/sysfs-power +17 -0
@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.
Re: [RFC][PATCH 0/7] PM: Implement autosleep and "wake locks", take2
Quoted 12 lines · Rafael J. Wysocki
I think we can do something like in the updated patch [5/7] below.
It uses a special wakeup source object called "autosleep" to bump up the number of wakeup events in progress before acquiring
autosleep_lockinpm_autosleep_set_state(). This way, eitherpm_autosleep_set_state()will acquireautosleep_lockbeforetry_to_suspend(), in which case the latter will see the change ofautosleep_stateimmediately (afterautosleep_lockhas been passed to it), ortry_to_suspend()will get it first, but thenpm_save_wakeup_count()or pm_suspend()/hibernate() will see the nonzero counter of wakeup events in progress and return error code (sooner or later).
Quoted 4 lines
The drawback is that writes to
/sys/power/autosleepmay interfere with the/sys/power/wakeup_count+/sys/power/stateinterface by interrupting transitions started by writing to/sys/power/state, for example (although I think that's highly unlikely).
Yes, but I think we can live with that.. It doesn't look like a big issue.
Quoted 2 lines
Additionally, I made
pm_autosleep_lock()usemutex_trylock_interruptible()
You have used mutex_lock_interruptible() in the code below.. It wouldn't matter as long as you have used some form of "interruptible" but I think mutex_trylock_interruptible would be even better..
Quoted 6 lines
to prevent operations on
/sys/power/wakeup_countand/or/sys/power/statefrom failing the freezing of tasks started bytry_to_suspend().Thanks,
Rafael
The approach taken by the patch below looks good to me. I don't see any obvious problems, except for the minor ones listed below.
Quoted 85 lines
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleepIntroduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.That work item enables the detection of wakeup events using the functions already defined in
drivers/base/power/wakeup.c(with one small modification) and calls eitherpm_suspend(), orhibernate()to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.Signed-off-by Rafael J. Wysocki <[email protected]>Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c +22
-15@@ -269,8 +269,7 @@ static ssize_t state_show(struct kobjectreturn (s - buf); } -static ssize_t state_store (struct kobject *kobj,struct kobj_attribute *attr, -const char *buf,size_t n) +static suspend_state_t decode_state (const char *buf,size_t n) {#ifdef CONFIG_SUSPEND suspend_state_t state =PM_SUSPEND_STANDBY ;@@ -278,27 +277,48 @@ static ssize_t state_store(struct kobjec#endif char *p;int len; -int error = -EINVAL ; p =memchr (buf,'\n' , n); len = p ? p - buf : n; -/* First, check if we are requested to hibernate */ -if (len ==4 && !strncmp (buf,"disk" , len)) { - error =hibernate (); - goto Exit; - } +/* Check hibernation first. */ +if (len ==4 && !strncmp (buf,"disk" , len)) +return PM_SUSPEND_MAX ;#ifdef CONFIG_SUSPEND -for (s = &pm_states[state]; state <PM_SUSPEND_MAX ; s++, state++) { -if (*s && len ==strlen (*s) && !strncmp (buf, *s, len)) { - error =pm_suspend (state); -break ; - } - } +for (s = &pm_states[state]; state <PM_SUSPEND_MAX ; s++, state++) +if (*s && len ==strlen (*s) && !strncmp (buf, *s, len)) +return state;#endif - Exit: +return PM_SUSPEND_ON ; +} + +static ssize_t state_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +suspend_state_t state; +int error; + + error =pm_autosleep_lock (); +if (error) +return error; + +if (pm_autosleep_state () >PM_SUSPEND_ON ) { + error = -EBUSY ;
kernel/power/main.c static ssize_t state_store(struct kobjec · line 318
Quoted 10 lines
kernel/power/main.c +10
-0+ goto out; + } + + state =decode_state (buf, n); +if (state <PM_SUSPEND_MAX ) + error =pm_suspend (state); +else if (state >PM_SUSPEND_ON ) + error =hibernate (); +else + error = -EINVAL ;
By the way, the condition checks in the above if-else block look kinda odd, considering what is done in other similar places, which are more readable. It would be great if you could make them consistent.
Quoted 17 lines
kernel/power/main.c +7
-1+ + out: +pm_autosleep_unlock ();return error ? error : n; }@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct {unsigned int val; -return pm_get_wakeup_count (&val) ?sprintf (buf,"%u\n" , val) : -EINTR ; +return pm_get_wakeup_count (&val, true) ? +sprintf (buf,"%u\n" , val) : -EINTR ; } + +static ssize_t autosleep_store(struct kobject *kobj,
kernel/power/main.c static ssize_t wakeup_count_show(struct · line 427
Quoted 10 lines
kernel/power/main.c +10
-0+struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); +int error; + +if (state ==PM_SUSPEND_ON &&strncmp (buf,"off" ,3 ) + &&strncmp (buf,"off\n" ,4 )) +return -EINVAL ; +
I am pretty sure you meant "if autosleep is already off, and the user wrote "off" to /sys/power/autosleep, then return -EINVAL"
But strncmp() returns 0 if the strings match, and hence the code above doesn't seem to do what you intended.
kernel/power/main.c static ssize_t wakeup_count_store(struct · line 436
Quoted 9 lines
kernel/power/main.c +6
-0+ error =pm_autosleep_set_state (state); +return error ? error : n; +} + +power_attr (autosleep); +#endif /* CONFIG_PM_AUTOSLEEP */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE
Regards,
Srivatsa S. Bhat
cover PM: Implement autosleep and "wake locks", take 3
In reply to Rafael J. WysockiHi all,
Following is the third update of the autosleep patchset.
Patches [1-4/8] are regarded as v3.5 material, the rest - depending on the feedback I get (lack of feedback will be understood as no objections, though).
Quoted 37 lines · Rafael J. Wysocki
Hi all,
After the feedback so far I've decided to follow up with a refreshed patchset. The first two patches from the previous one went to linux-pm/linux-next and I included the recent evdev patch from Arve (with some modifications) to this patchset for completness.
Quoted 29 lines · Rafael J. Wysocki
Hi all,
This series tests the theory that the easiest way to sell a once rejected feature is to advertise it under a different name.
Well, there actually are two different features, although they are closely related to each other. First, patch [6/8] introduces a feature that allows the kernel to trigger system suspend (or more generally a transition into a sleep state) whenever there are no active wakeup sources (no, they aren't called wakelocks). It is called "autosleep" here, but it was called a few different names in the past ("opportunistic suspend" was probably the most popular one). Second, patch [8/8] introduces "wake locks" that are, essentially, wakeup sources which may be created and manipulated by user space. Using them user space may control the autosleep feature introduced earlier.
This also is a kind of a proof of concept for the people who wanted me to show a kernel-based implementation of automatic suspend, so there you go. Please note, however, that it is done so that the user space "wake locks" interface is compatible with Android in support of its user space. I don't really like this interface, but since the Android's user space seems to rely on it, I'm fine with using it as is. YMMV.
Let me say a few words about every patch in the series individually.
[1/8] - This really is a bug fix, so it's v3.4 material. Nobody has stepped
on this bug so far, but it should be fixed anyway.[2/8] - This is a freezer cleanup, worth doing anyway IMO, so v3.4 material too.
The two patches above have been merged.
Quoted 8 lines
The above two are in linux-pm/linux-next now. There are a few more fixes related to wakeup sources in there and the patches below are based on that branch.
Quoted 2 lines
[3/8] - This is something we can do no problem, although completely optional
without the autosleep feature. Rather necessary with it, though.Now [1/7] - Look for wakeup events in later stages of device suspend.
[1/8] now - Look for wakeup events later down the suspend code path.
Quoted 9 lines
Quoted 8 lines
[4/8] - This kind of reintroduces my original idea of using a wait queue for
waiting until there are no wakeup events in progress. Alan convinced me that it would be better to poll the counter to preventwakeup_source_deactivate()from having to callwake_up_all()occasionally (that may be costly in fast paths), but then quite some people told me that the wait queue migh be better. I think that the polling will make much less sense with autosleep and user space "wake locks". Anyway, [4/8] is something we can do without those things too.
Quoted 3 lines
Now [2/7] - Use wait queue to signal "no wakeup events in progress"
With a couple of improvements suggested by Neil.
[2/8] now - Use wait queue to signal "no wakeup events in progress" condition.
Quoted 16 lines
Quoted 3 lines
The patches above were given Sign-off-by tags, because I think they make some sense regardless of the features introcuded by the remaining patches that in turn are total RFC.
This time all of the patches are signed-off and include the requisite documentation changes (hopefully, I haven't forgotten about anything).
Quoted 8 lines
[5/8] - This changes wakeup source statistics so that they are more similar to
the statistics collected for wakelocks on Android. The file those statistics may be read from is still located in debugfs, though (I don't think it belongs to proc and its name is different from the analogous Android's file name anyway). It could be done without autosleep, but then it would be a bit pointless. BTW, this changes interfaces that _in_ _theory_ may be used by someone, but I'm not aware of anyone using them. If you are one, I'll be pleased to learn about that, so please tell me who you are. :-)
Quoted 3 lines
Now [3/7] - Change wakeup source statistics to follow Android.
Rebased and reworked in accordance with the Arve's feedback.
[3/8] now - Change wakeup source statistics to follow Android.
[4/8] - Add tracepoints to wakeup_source_{de}activate()
[5/8] - Teach epoll to use wakeup sources if requested
This should be sufficient to ensure that a wakeup source will be kept active after a wakeup event all the way up to user space without a need to make a number of random drivers use wakeup sources.
Quoted 10 lines
Quoted 9 lines
[6/8] - Autosleep implementation. I think the changelog explains the idea
quite well and the code is really nothing special. It doesn't really add anything new to the kernel in terms of infrastructure etc., it just uses the existing stuff to implement an alternative method of triggering system sleep transitions. Note, though, that the interface here is different from the Android's one, because Android actually modifies/sys/power/stateto trigger something called "early suspend" (that is never going to be implemented in the "stock" kernel as long as I have any influence on it) and we simply can't do that in the mainline.
Quoted 4 lines
Now [5/7] - Implement opportunistic sleep
Rebased and simplified (most notably, I've dropped the "main" wakeup source, since it wasn't really necessary).
[6/8] now - Implement apportunistic sleep.
Quoted 9 lines
Quoted 5 lines
[7/8] - This adds a wakeup source statistics that only makes sense with
autosleep and (I believe) is analogous to the Android'sprevent_suspend_timestatistics. Nothing really special, but I didn't want wakeup_source_activate/deactivate() to take a common lock to avoid congestion.Now [6/7] - Add "prevent autosleep time" statistics to wakeup sources.
Rebased.
[7/8] now - Add "prevent autosleep time" statistics to wakeup sources.
Quoted 10 lines
Quoted 8 lines
[8/8] - This adds a user space interface to create, activate and deactivate
wakeup sources. Since the files it consists of are calledwake_lockandwake_unlock, to follow Android, the objects the wakeup sources are wrapped into are called "wakelocks" (for added confusion). Since the interface doesn't provide any means to destroy those "wakelocks", I added a garbage collection mechanism to get rid of the unused ones, if any. I also tought it might be a good idea to put a limit on the number of those things that user space can operate simultaneously, so I did that too.Now [7/7] - Add user space interface for manipulating wakeup sources.
[8/8] now - Add user space interface for manipulating wakeup sources.
Quoted 6 lines
Quoted 3 lines
All of the above has been tested very briefly on my test-bed Mackerel board and it quite obviously requires more thorough testing, but first I need to know if it makes sense to spend any more time on it.
The above is still accurate, but I also verified that the patches don't break my PC test boxes (at least as long as the new features aren't used ;-)).
Nothing has changed in that respect, as far as I can say.
The patches in the following series are available from the autosleep branch in the linux-pm tree.
Thanks,
Rafael
PATCH 1/8 PM / Sleep: Look for wakeup events in later stages of device suspend
From: Rafael J. Wysocki <[email protected]>
Currently, the device suspend code in drivers/base/power/main.c only checks if there have been any wakeup events, and therefore the ongoing system transition to a sleep state should be aborted, during the first (i.e. "suspend") device suspend phase. However, wakeup events may be reported later as well, so it's reasonable to look for them in the in the subsequent (i.e. "late suspend" and "suspend noirq") phases.
--- drivers/base/power/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/base/power/main.c +10 -0
@@ -889,6 +889,11 @@ static int dpm_suspend_noirq(pm_message_if (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_noirq_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
@@ -962,6 +967,11 @@ static int dpm_suspend_late(pm_message_tif (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &dpm_late_early_list);put_device (dev); + +if (pm_wakeup_pending ()) { + error = -EBUSY ; +break ; + } }mutex_unlock (&dpm_list_mtx);if (error)
PATCH 2/8 PM / Sleep: Use wait queue to signal "no wakeup events in progress"
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires pm_get_wakeup_count() to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.
--- drivers/base/power/wakeup.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/base/power/wakeup.c +13 -3
@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition.
@@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare.
@@ -442,6 +442,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;
@@ -476,6 +477,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +wake_up (&wakeup_count_wait_queue); }/**
@@ -667,14 +672,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Re: [PATCH 2/8] PM / Sleep: Use wait queue to signal "no wakeup events in progress"
Quoted 68 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
The current wakeup source deactivation code doesn't do anything when the counter of wakeup events in progress goes down to zero, which requires
pm_get_wakeup_count()to poll that counter periodically. Although this reduces the average time it takes to deactivate a wakeup source, it also may lead to a substantial amount of unnecessary polling if there are extended periods of wakeup activity. Thus it seems reasonable to use a wait queue for signaling the "no wakeup events in progress" condition and remove the polling.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/wakeup.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +10
-2@@ -17,8 +17,6 @@#include "power.h" -#define TIMEOUT 100 -/* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. @@ -52,6 +50,8 @@ static void pm_wakeup_timer_fn(unsignedstatic LIST_HEAD (wakeup_sources); +static DECLARE_WAIT_QUEUE_HEAD (wakeup_count_wait_queue); +/** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare. @@ -442,6 +442,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { +unsigned int cnt, inpr;ktime_t duration;ktime_t now;@@ -476,6 +477,10 @@ static void wakeup_source_deactivate(str* couter of wakeup events in progress simultaneously. */ atomic_add (MAX_IN_PROGRESS , &combined_event_count); + +split_counters (&cnt, &inpr); +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +wake_up (&wakeup_count_wait_queue); }/** @@ -667,14 +672,19 @@ bool pm_wakeup_pending(void)bool pm_get_wakeup_count (unsigned int *count) {unsigned int cnt, inpr; +DEFINE_WAIT (wait);for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE );split_counters (&cnt, &inpr);if (inpr ==0 ||signal_pending (current))
drivers/base/power/wakeup.c bool pm_wakeup_pending(void) · line 690
Quoted 10 lines
drivers/base/power/wakeup.c +3
-1break ;pm_wakeup_update_hit_counts (); -schedule_timeout_interruptible (msecs_to_jiffies (TIMEOUT )); + +schedule (); } +finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
PATCH 3/8 PM / Sleep: Change wakeup source statistics to follow Android
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Wakeup statistics used by Android are slightly different from what we have in wakeup sources at the moment and there aren't any known users of those statistics other than Android, so modify them to make it easier for Android to switch to wakeup sources.
This removes the struct wakeup_source's hit_cout field, which is very rough and therefore not very useful, and adds two new fields, wakeup_count and expire_count. The first one tracks how many times the wakeup source is activated with events_check_enabled set (which roughly corresponds to the situations when a system power transition to a sleep state is in progress and would be aborted by this wakeup source if it were the only active one at that time) and the second one is the number of times the wakeup source has been activated with a timeout that expired.
Additionally, the last_time field is now updated when the wakeup source is deactivated too (previously it was only updated during the wakeup source's activation), which seems to be what Android does with the analogous counter for wakelocks.
--- Documentation/ABI/testing/sysfs-devices-power | 24 ++++++--- drivers/base/power/sysfs.c | 30 ++++++++++-- drivers/base/power/wakeup.c | 64 +++++++++++--------------- include/linux/pm_wakeup.h | 11 ++-- 4 files changed, 77 insertions(+), 52 deletions(-)
include/linux/pm_wakeup.h +7 -4
@@ -33,12 +33,14 @@* * @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 activated last time. +* @last_time: Monotonic clock when the wakeup source's was touched last time. * @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. -* @hit_count: Number of times the wakeup sorce might abort system suspend. +* @expire_count: Number of times the wakeup source's timeout has expired. +* @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. +* @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source {const char *name;
@@ -52,8 +54,9 @@ struct wakeup_source {unsigned long event_count;unsigned long active_count;unsigned long relax_count; -unsigned long hit_count; - unsigned int active:1; +unsigned long expire_count; +unsigned long wakeup_count; + bool active:1; };#ifdef CONFIG_PM_SLEEP
drivers/base/power/wakeup.c +28 -36
138 lines not on this page · raw message
drivers/base/power/sysfs.c +25 -5
@@ -314,22 +314,41 @@ static ssize_t wakeup_active_count_show( static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); -static ssize_t wakeup_hit_count_show (struct device *dev, -struct device_attribute *attr,char *buf) +static ssize_t wakeup_abort_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) +{ +unsigned long count =0 ; +bool enabled = false; + +spin_lock_irq (&dev->power.lock); +if (dev->power.wakeup) { + count = dev->power.wakeup->wakeup_count; + enabled = true; + } +spin_unlock_irq (&dev->power.lock); +return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); +} + +static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); + +static ssize_t wakeup_expire_count_show (struct device *dev, +struct device_attribute *attr, +char *buf) {unsigned long count =0 ;bool enabled = false;spin_lock_irq (&dev->power.lock);if (dev->power.wakeup) { - count = dev->power.wakeup->hit_count; + count = dev->power.wakeup->expire_count; enabled = true; }spin_unlock_irq (&dev->power.lock);return enabled ?sprintf (buf,"%lu\n" , count) :sprintf (buf,"\n" ); } -static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); +static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);static ssize_t wakeup_active_show (struct device *dev,struct device_attribute *attr,char *buf)
@@ -486,7 +505,8 @@ static struct attribute *wakeup_attrs[] &dev_attr_wakeup.attr, &dev_attr_wakeup_count.attr, &dev_attr_wakeup_active_count.attr, - &dev_attr_wakeup_hit_count.attr, + &dev_attr_wakeup_abort_count.attr, + &dev_attr_wakeup_expire_count.attr, &dev_attr_wakeup_active.attr, &dev_attr_wakeup_total_time_ms.attr, &dev_attr_wakeup_max_time_ms.attr,
Documentation/ABI/testing/sysfs-devices-power +17 -7
@@ -96,16 +96,26 @@ Description: 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/wakeup_hit_count -Date: September 2010 +What: /sys/devices/.../power/wakeup_abort_count +Date: February 2012 Contact: Rafael J. Wysocki <[email protected]> Description: - The /sys/devices/.../wakeup_hit_count attribute contains the + The /sys/devices/.../wakeup_abort_count attribute contains the 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 not - present. + the device might have aborted system transition into a sleep + state in progress. 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/wakeup_expire_count +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/devices/.../wakeup_expire_count attribute contains the + number of times a wakeup event associated with the device has + been reported with a timeout that expired. 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/wakeup_active Date: September 2010
PATCH 4/8 PM / Sleep: Add wakeup_source_activate and wakeup_source_deactivate tracepoints
In reply to Rafael J. WysockiFrom: Arve Hjønnevåg <[email protected]>
Add tracepoints to wakeup_source_activate and wakeup_source_deactivate. Useful for checking that specific wakeup sources overlap as expected.
--- drivers/base/power/wakeup.c | 12 +++++++++--- include/trace/events/power.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-)
drivers/base/power/wakeup.c +9 -3
@@ -14,6 +14,7 @@#include <linux/suspend.h> #include <linux/seq_file.h> #include <linux/debugfs.h> +#include <trace/events/power.h> #include "power.h"
@@ -374,12 +375,16 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_enab*/ static void wakeup_source_activate (struct wakeup_source *ws) { +unsigned int cec; + ws->active = true; ws->active_count++; ws->last_time =ktime_get ();/* Increment the counter of events in progress. */ -atomic_inc (&combined_event_count); + cec =atomic_inc_return (&combined_event_count); + +trace_wakeup_source_activate (ws->name, cec); }/**
@@ -454,7 +459,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);*/ static void wakeup_source_deactivate (struct wakeup_source *ws) { -unsigned int cnt, inpr; +unsigned int cnt, inpr, cec;ktime_t duration;ktime_t now;
@@ -489,7 +494,8 @@ static void wakeup_source_deactivate(str* Increment the counter of registered wakeup events and decrement the * couter of wakeup events in progress simultaneously. */ -atomic_add (MAX_IN_PROGRESS , &combined_event_count); + cec =atomic_add_return (MAX_IN_PROGRESS , &combined_event_count); +trace_wakeup_source_deactivate (ws->name, cec);split_counters (&cnt, &inpr);if (!inpr &&waitqueue_active (&wakeup_count_wait_queue))
include/trace/events/power.h +34 -0
@@ -65,6 +65,40 @@ TRACE_EVENT(machine_suspend,TP_printk ("state=%lu" , (unsigned long )__entry->state) ); +DECLARE_EVENT_CLASS (wakeup_source, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state), + +TP_STRUCT__entry ( + __string( name, name ) +__field ( u64, state ) + ), + +TP_fast_assign ( + __assign_str(name, name); + __entry->state = state; + ), + +TP_printk ("%s state=0x%lx" ,__get_str (name), + (unsigned long )__entry->state) +); + +DEFINE_EVENT (wakeup_source, wakeup_source_activate, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state) +); + +DEFINE_EVENT (wakeup_source, wakeup_source_deactivate, + + TP_PROTO(const char *name, unsigned int state), + +TP_ARGS (name, state) +); +#ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED /*
RFC PATCH 5/8 epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
In reply to Rafael J. WysockiFrom: Arve Hjønnevåg <[email protected]>
When an epoll_event, that has the EPOLLWAKEUP flag set, is ready, a wakeup_source will be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
The current implementation uses an extra wakeup_source when ep_scan_ready_list runs. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.
--- fs/eventpoll.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/eventpoll.h | 6 +++ 2 files changed, 74 insertions(+), 3 deletions(-)
fs/eventpoll.c +68 -3
@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h>
@@ -87,7 +88,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4
@@ -154,6 +155,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };
@@ -194,6 +198,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;
@@ -565,6 +572,7 @@ static int ep_scan_ready_list(struct eve* in a lockless way. */ spin_lock_irqsave (&ep->lock, flags); +__pm_stay_awake (ep->ws);list_splice_init (&ep->rdllist, &txlist); ep->ovflist =NULL ;spin_unlock_irqrestore (&ep->lock, flags);
@@ -588,8 +596,10 @@ static int ep_scan_ready_list(struct eve* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
@@ -602,6 +612,7 @@ static int ep_scan_ready_list(struct eve* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/*
@@ -656,6 +667,9 @@ static int ep_remove(struct eventpoll *elist_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);
@@ -706,6 +720,8 @@ static void ep_free(struct eventpoll *epmutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +if (ep->ws) +wakeup_source_unregister (ep->ws);kfree (ep); }
@@ -737,6 +753,7 @@ static int ep_read_events_proc(struct ev* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }
@@ -932,8 +949,10 @@ static int ep_poll_callback(wait_queue_t }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -1091,6 +1110,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */
@@ -1118,6 +1161,13 @@ static int ep_insert(struct eventpoll *e epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;
@@ -1164,6 +1214,7 @@ static int ep_insert(struct eventpoll *e/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1204,6 +1255,10 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;
@@ -1229,6 +1284,12 @@ static int ep_modify(struct eventpoll *e epi->event.events = event->events; pt._key = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because
@@ -1244,6 +1305,7 @@ static int ep_modify(struct eventpoll *espin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1282,6 +1344,7 @@ static int ep_send_events_proc(struct ev !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); pt._key = epi->event.events;
@@ -1298,6 +1361,7 @@ static int ep_send_events_proc(struct evif (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;
@@ -1317,6 +1381,7 @@ static int ep_send_events_proc(struct ev* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }
include/linux/eventpoll.h +6 -0
@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 8 lines · On Sun, 22 Apr 2012 23:22:43 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: Arve Hjønnevåg <[email protected]>
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
Quoted 4 lines
The current implementation uses an extra
wakeup_sourcewhenep_scan_ready_listruns. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.
This is quite neat.
If I understand it correctly, you register file descriptors with epoll_ctl() on an fd created with epoll_create(), and set the new EPOLLWAKEUP flag. Then when a regular 'poll' or 'select' on the epoll fd reports that it is readable you:
- get a wakelock
- use epoll_wait to collect the events
- process the events
- release your wakelock
- go back to poll() or select() on the epoll fd.
Correct? As long as there are ready events with EPOLLWAKEUP set a wakeup_source is held active and the system won't go to sleep.
My concern with this is about permissions. It appears that any process could wait of some fd (maybe a pipe they created themselves) with EPOLLWAKEUP, and then simply never epoll_wait() for the event. Then they would be keeping the system awake. I don't think that is acceptable.
So there needs to be some way to limit who can effectively block suspend by using EPOLLWAKEUP.
(This is one of the reasons I like an all-user-space solution. Policy issues like this can easily be decided in user-space but are clumsy to put into the kernel).
Also, I'm having trouble understanding the ep->ws wakeup_source. The epi->ws makes lots of sense and I think I understand it all. However I don't see why you need a wakeup_source for the 'struct eventpoll'.
Every time that 'poll' decides to call the ->poll fop for the eventpoll, this wakeup_source will be activated and deactivated which will abort any current suspend cycle even if there are no events to report.
I suspect it can just go away.
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides wakeup_source for almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would get wakeup_source handling for free.
Does this seem reasonable to you. I'm afraid I don't have code yet, but hope to find time in a few weeks.
One difficulty with that is that I have noticed a number of drivers that potentially enable_irq_wake just before suspend and disable_irq_wake immediately after (e.g. gpio_keys.c). Allocating a wakeup_source on each enable_irq_wake would be an unfortunate overhead. Maybe we just allocate it the first time enable_irq_wake is called ....
Thanks,
NeilBrown
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 29 lines · NeilBrown
Quoted 8 lines · On Sun, 22 Apr 2012 23:22:43 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: Arve Hjønnevåg <[email protected]>
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.Quoted 4 lines
The current implementation uses an extra
wakeup_sourcewhenep_scan_ready_listruns. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.This is quite neat.
If I understand it correctly, you register file descriptors with
epoll_ctl()on an fd created withepoll_create(), and set the new EPOLLWAKEUP flag. Then when a regular 'poll' or 'select' on the epoll fd reports that it is readable you:
- get a wakelock
- useepoll_waitto collect the events
- process the events
- release your wakelock
- go back topoll()orselect()on the epoll fd.
Correct? As long as there are ready events with EPOLLWAKEUP set awakeup_sourceis held active and the system won't go to sleep.
Quoted 4 lines
My concern with this is about permissions. It appears that any process could wait of some fd (maybe a pipe they created themselves) with EPOLLWAKEUP, and then simply never
epoll_wait()for the event. Then they would be keeping the system awake. I don't think that is acceptable.
I wonder what Arve has to say to that, but let me say that on systems without autosleep every process can go into an infinite busy loop which is going to drain battery relatively quickly just as well and I don't see why that's so much different.
Quoted 10 lines
So there needs to be some way to limit who can effectively block suspend by using EPOLLWAKEUP.
(This is one of the reasons I like an all-user-space solution. Policy issues like this can easily be decided in user-space but are clumsy to put into the kernel).Also, I'm having trouble understanding the
ep->wswakeup_source. Theepi->wsmakes lots of sense and I think I understand it all. However I don't see why you need awakeup_sourcefor the 'struct eventpoll'.
Quoted 5 lines
Every time that 'poll' decides to call the ->poll fop for the eventpoll, this
wakeup_sourcewill be activated and deactivated which will abort any current suspend cycle even if there are no events to report.I suspect it can just go away.
I'll leave this one entirely to Arve, if you don't mind. :-)
Quoted 17 lines
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides
wakeup_sourcefor almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would getwakeup_sourcehandling for free.
Does this seem reasonable to you.
Yes, it does.
Wakeup devices have their own wakeup source objects anyway, so perhaps they may be used for this purpose somehow (just wondering).
Quoted 7 lines
I'm afraid I don't have code yet, but hope to find time in a few weeks.
One difficulty with that is that I have noticed a number of drivers that potentially
enable_irq_wakejust before suspend anddisable_irq_wakeimmediately after (e.g.gpio_keys.c). Allocating awakeup_sourceon eachenable_irq_wakewould be an unfortunate overhead. Maybe we just allocate it the first timeenable_irq_wakeis called ....
I guess we can do something in analogy with device_wakeup_enable()?
Rafael
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
2012/4/26 Rafael J. Wysocki <[email protected]>:
Quoted 16 lines · NeilBrown
Quoted 15 lines · On Sun, 22 Apr 2012 23:22:43 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
Quoted 13 lines
From: Arve Hjønnevåg <[email protected]>
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.The current implementation uses an extra
wakeup_sourcewhenep_scan_ready_listruns. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.
Quoted 6 lines
Quoted 6 lines
This is quite neat.
If I understand it correctly, you register file descriptors with
epoll_ctl()on an fd created withepoll_create(), and set the new EPOLLWAKEUP flag. Then when a regular 'poll' or 'select' on the epoll fd reports that it is readable you:
I think it makes more sense to use epoll_wait than mixing this with select or poll.
Quoted 1 line
Quoted 1 line
- get a wakelock
This may not be needed, since epoll does not reevaluate its state until you call into it again (at least using epoll_wait).
Quoted 12 lines
Quoted 11 lines
- use
epoll_waitto collect the events
- process the events
- release your wakelock
- go back topoll()orselect()on the epoll fd. Correct? As long as there are ready events with EPOLLWAKEUP set awakeup_sourceis held active and the system won't go to sleep.My concern with this is about permissions. It appears that any process could wait of some fd (maybe a pipe they created themselves) with EPOLLWAKEUP, and then simply never
epoll_wait()for the event. Then they would be keeping the system awake. I don't think that is acceptable.
Quoted 5 lines
I wonder what Arve has to say to that, but let me say that on systems without autosleep every process can go into an infinite busy loop which is going to drain battery relatively quickly just as well and I don't see why that's so much different.
I still think is useful to limit access to this feature. On a phone, a process stuck in an infinite loop will increase battery drain, but if this process does not have permission to prevent suspend, then this is only catastrophic if another process that have that permission is preventing suspend. I think we should add a capability for this. Assuming you agree, do want me to create a separate patch for that adds a capability, or roll it into this one.
Quoted 10 lines
Quoted 10 lines
So there needs to be some way to limit who can effectively block suspend by using EPOLLWAKEUP.
(This is one of the reasons I like an all-user-space solution. Policy issues like this can easily be decided in user-space but are clumsy to put into the kernel).Also, I'm having trouble understanding the
ep->wswakeup_source. Theepi->wsmakes lots of sense and I think I understand it all. However I don't see why you need awakeup_sourcefor the 'struct eventpoll'.
Quoted 8 lines
Quoted 5 lines
Every time that 'poll' decides to call the ->poll fop for the eventpoll, this
wakeup_sourcewill be activated and deactivated which will abort any current suspend cycle even if there are no events to report.I suspect it can just go away.
I'll leave this one entirely to Arve, if you don't mind. :-)
I keep the wakeup-source active whenever the epitem is on a list (ep->rdllist or the local txlist). The temporary txlist is modified without holding the lock that protects ep->rdllist. It is easier to use a separate wakeup source to prevent suspend while this list is manipulated than trying to maintain the wakeup-source state in a different way than the existing eventpoll state. I think this only causes real problems if the same epoll file is used for frequent non-wakeup events (e.g. a gyro) and wakeup events. You should be able to work around this by using two epoll files.
Quoted 18 lines
Quoted 17 lines
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides
wakeup_sourcefor almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would getwakeup_sourcehandling for free.
Does this seem reasonable to you.
Quoted 2 lines
Yes, it does.
How useful is that? Suspend already synchronizes with interrupt handlers and will not proceed until they have returned. Are threaded interrupts handlers not always run at that stage? For drivers that use work-queues instead of a threaded interrupt handler, I think the suspend-blocking work-queue patch I wrote a while back is convenient.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 49 lines · Arve Hjønnevåg
2012/4/26 Rafael J. Wysocki <[email protected]>:
Quoted 16 lines · NeilBrown
Quoted 15 lines · On Sun, 22 Apr 2012 23:22:43 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
Quoted 13 lines
From: Arve Hjønnevåg <[email protected]>
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.The current implementation uses an extra
wakeup_sourcewhenep_scan_ready_listruns. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.Quoted 6 lines
Quoted 6 lines
This is quite neat.
If I understand it correctly, you register file descriptors with
epoll_ctl()on an fd created withepoll_create(), and set the new EPOLLWAKEUP flag. Then when a regular 'poll' or 'select' on the epoll fd reports that it is readable you:I think it makes more sense to use
epoll_waitthan mixing this with select or poll.Quoted 1 line
Quoted 1 line
- get a wakelock
This may not be needed, since epoll does not reevaluate its state until you call into it again (at least using
epoll_wait).Quoted 12 lines
Quoted 11 lines
- use
epoll_waitto collect the events
- process the events
- release your wakelock
- go back topoll()orselect()on the epoll fd.
Correct? As long as there are ready events with EPOLLWAKEUP set awakeup_sourceis held active and the system won't go to sleep.My concern with this is about permissions. It appears that any process could wait of some fd (maybe a pipe they created themselves) with EPOLLWAKEUP, and then simply never
epoll_wait()for the event. Then they would be keeping the system awake. I don't think that is acceptable.Quoted 5 lines
I wonder what Arve has to say to that, but let me say that on systems without autosleep every process can go into an infinite busy loop which is going to drain battery relatively quickly just as well and I don't see why that's so much different.
Quoted 6 lines
I still think is useful to limit access to this feature. On a phone, a process stuck in an infinite loop will increase battery drain, but if this process does not have permission to prevent suspend, then this is only catastrophic if another process that have that permission is preventing suspend. I think we should add a capability for this. Assuming you agree,
I do.
Quoted 2 lines
do want me to create a separate patch for that
adds a capability, or roll it into this one.
Please roll it into this one, if that's not a problem.
Thanks,
Rafael
PATCH 1/1 epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
When an epoll_event, that has the EPOLLWAKEUP flag set, is ready, a wakeup_source will be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
The current implementation uses an extra wakeup_source when ep_scan_ready_list runs. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.
--- fs/eventpoll.c | 75 ++++++++++++++++++++++++++++++++++++++++++-- include/linux/capability.h | 5 ++- include/linux/eventpoll.h | 6 +++ 3 files changed, 82 insertions(+), 4 deletions(-)
fs/eventpoll.c +72 -3
@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h>
@@ -87,7 +88,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4
@@ -154,6 +155,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };
@@ -194,6 +198,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;
@@ -565,6 +572,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* in a lockless way. */ spin_lock_irqsave (&ep->lock, flags); +__pm_stay_awake (ep->ws);list_splice_init (&ep->rdllist, &txlist); ep->ovflist =NULL ;spin_unlock_irqrestore (&ep->lock, flags);
@@ -588,8 +596,10 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
@@ -602,6 +612,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/*
@@ -656,6 +667,9 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);
@@ -706,6 +720,8 @@ static void ep_free(struct eventpoll *ep)mutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +if (ep->ws) +wakeup_source_unregister (ep->ws);kfree (ep); }
@@ -737,6 +753,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }
@@ -932,8 +949,10 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -1091,6 +1110,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */
@@ -1118,6 +1161,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;
@@ -1164,6 +1214,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1204,6 +1255,10 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +if (epi->ws) +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;
@@ -1229,6 +1284,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even epi->event.events = event->events; pt._key = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because
@@ -1244,6 +1305,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_evenspin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1282,6 +1344,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); pt._key = epi->event.events;
@@ -1298,6 +1361,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,if (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;
@@ -1317,6 +1381,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }
@@ -1629,6 +1694,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,if (!tfile->f_op || !tfile->f_op->poll) goto error_tgt_fput; +/* Check if EPOLLWAKEUP is allowed */ +if ((epds.events &EPOLLWAKEUP ) && !capable (CAP_EPOLLWAKEUP )) + goto error_tgt_fput; +/* * We have to check that the file structure underneath the file descriptor * the user passed to us _is_ an eventpoll file. And also we do not permit
include/linux/capability.h +4 -1
@@ -360,8 +360,11 @@ struct cpu_vfs_cap_data {#define CAP_WAKE_ALARM 35 +/* Allow preventing automatic system suspends while epoll events are pending */ -#define CAP_LAST_CAP CAP_WAKE_ALARM +#define CAP_EPOLLWAKEUP 36 + +#define CAP_LAST_CAP CAP_EPOLLWAKEUP#define cap_valid (x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
include/linux/eventpoll.h +6 -0
@@ -26,6 +26,12 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Signature · 1 line
1.7.7.3
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
In reply to Arve HjønnevågQuoted 76 lines · On Thu, 26 Apr 2012 20:49:51 -0700 Arve Hjønnevåg <[email protected]> wrote:
2012/4/26 Rafael J. Wysocki <[email protected]>:
Quoted 16 lines · NeilBrown
Quoted 15 lines · On Sun, 22 Apr 2012 23:22:43 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
Quoted 13 lines
From: Arve Hjønnevåg <[email protected]>
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.The current implementation uses an extra
wakeup_sourcewhenep_scan_ready_listruns. This can cause problems if a single thread is polling on wakeup events and frequent non-wakeup events (events usually arrive during thread freezing) using the same epoll file.Quoted 6 lines
Quoted 6 lines
This is quite neat.
If I understand it correctly, you register file descriptors with
epoll_ctl()on an fd created withepoll_create(), and set the new EPOLLWAKEUP flag. Then when a regular 'poll' or 'select' on the epoll fd reports that it is readable you:I think it makes more sense to use
epoll_waitthan mixing this with select or poll.Quoted 1 line
Quoted 1 line
- get a wakelock
This may not be needed, since epoll does not reevaluate its state until you call into it again (at least using
epoll_wait).Quoted 12 lines
Quoted 11 lines
- use
epoll_waitto collect the events
- process the events
- release your wakelock
- go back topoll()orselect()on the epoll fd. Correct? As long as there are ready events with EPOLLWAKEUP set awakeup_sourceis held active and the system won't go to sleep.My concern with this is about permissions. It appears that any process could wait of some fd (maybe a pipe they created themselves) with EPOLLWAKEUP, and then simply never
epoll_wait()for the event. Then they would be keeping the system awake. I don't think that is acceptable.Quoted 5 lines
I wonder what Arve has to say to that, but let me say that on systems without autosleep every process can go into an infinite busy loop which is going to drain battery relatively quickly just as well and I don't see why that's so much different.
I still think is useful to limit access to this feature. On a phone, a process stuck in an infinite loop will increase battery drain, but if this process does not have permission to prevent suspend, then this is only catastrophic if another process that have that permission is preventing suspend. I think we should add a capability for this. Assuming you agree, do want me to create a separate patch for that adds a capability, or roll it into this one.
Quoted 10 lines
Quoted 10 lines
So there needs to be some way to limit who can effectively block suspend by using EPOLLWAKEUP.
(This is one of the reasons I like an all-user-space solution. Policy issues like this can easily be decided in user-space but are clumsy to put into the kernel).Also, I'm having trouble understanding the
ep->wswakeup_source. Theepi->wsmakes lots of sense and I think I understand it all. However I don't see why you need awakeup_sourcefor the 'struct eventpoll'.Quoted 8 lines
Quoted 5 lines
Every time that 'poll' decides to call the ->poll fop for the eventpoll, this
wakeup_sourcewill be activated and deactivated which will abort any current suspend cycle even if there are no events to report.I suspect it can just go away.
I'll leave this one entirely to Arve, if you don't mind. :-)
Quoted 9 lines
I keep the wakeup-source active whenever the epitem is on a list (
ep->rdllistor the local txlist). The temporary txlist is modified without holding the lock that protectsep->rdllist. It is easier to use a separate wakeup source to prevent suspend while this list is manipulated than trying to maintain the wakeup-source state in a different way than the existing eventpoll state. I think this only causes real problems if the same epoll file is used for frequent non-wakeup events (e.g. a gyro) and wakeup events. You should be able to work around this by using two epoll files.
Thanks for the explanation. I can now see more clearly how your patch works. I can also see why you might need the ep->ws wakeup_source. However I don't like it.
If it acted purely as a lock and prevented suspend while it was active then it would be fine. However it doesn't. It also aborts any current suspend attempt - so it is externally visible.
The way your code it written, *any* call to epoll_wait will abort the current suspend cycle, even if it is called by a completely non-privileged user. That may not obviously be harmful, but it makes the precise semantics of the system call something quite non-obvious, and it is much better to have a very clean semantic.
As you say, it can probably be worked-around but code is much safer when you don't need to work-around things.
I see two alternatives:
1/ set the 'wakeup' flag on the whole epoll-fd, not on the individual events
that it is asked to monitor. i.e. add a new flag to epoll_create1() instead of to epoll_ctl events.
Then you just need a single wakeup_source for the fd which is active whenever any event is ready.
This interface might be generally nicer, I'm not sure.
2/ Find a way to get rid of ep->ws.
Thinking about it more, I again think it isn't needed.
The reason is that suspend is already exclusive with any process running in kernel context.
One of the first things suspend does is to freeze all process and (for regular non-kernel-thread processes) this happens by sending a virtual signal which is acted up when the process returns from a system call or returns from a context switch. So while any given system call is running (e.g. epoll_wait) suspend is blocked. When epoll_wait sets TASK_INTERRUPTIBLE the 'freeze' signal will interrupt it of course, but this is the only point where suspend can interfere with epoll_wait, and you aren't holding ep->ws then anyway.
Hopefully Rafael will correct me if I got that outline wrong. But even if I did, I think we need to get rid of ep->ws.
Also, I think it is important to clearly document how to use this safely. You suggested that if any EPOLLWAKEUP event is ready, then suspend will remain disabled not only until the event is handled, but also until the next call to epoll_wait. That sounds like very useful semantics, but it isn't at all explicit in the patch. I think it should be made very clear in eventpoll.h how the flag can be used. (and then eventually get this into a man page of course).
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides
wakeup_sourcefor almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would getwakeup_sourcehandling for free.
Does this seem reasonable to you.
Quoted 9 lines
Quoted 2 lines
Yes, it does.
How useful is that? Suspend already synchronizes with interrupt handlers and will not proceed until they have returned. Are threaded interrupts handlers not always run at that stage? For drivers that use work-queues instead of a threaded interrupt handler, I think the suspend-blocking work-queue patch I wrote a while back is convenient.
Maybe it isn't useful at all - I'm still working this stuff out.
Yes, threaded interrupts are run "straight away", but what exactly does that mean? And in particular, is there any interlocking to ensure they run before suspend gets stop the CPU? Maybe the scheduling priority of the different threads is enough to make sure this works, as irq_threads are SCHED_FIFO and the suspending thread almost certainly isn't. But is that still a guarantee on an SMP machine? irq_threads aren't freezable so suspend won't block on them for that reason..
I really just want to be sure that some interlock is in place to ensure that the threaded interrupt handler runs before suspend absolutely commits to suspending. If that is already the case, when what I suggest isn't needed as you suggest. Do you know of such an interlock?
Thanks,
NeilBrown
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 1 line · NeilBrown
On Thu, 26 Apr 2012 20:49:51 -0700 Arve Hjønnevåg <[email protected]> wrote:
...
Quoted 14 lines
Quoted 9 lines
I keep the wakeup-source active whenever the epitem is on a list (
ep->rdllistor the local txlist). The temporary txlist is modified without holding the lock that protectsep->rdllist. It is easier to use a separate wakeup source to prevent suspend while this list is manipulated than trying to maintain the wakeup-source state in a different way than the existing eventpoll state. I think this only causes real problems if the same epoll file is used for frequent non-wakeup events (e.g. a gyro) and wakeup events. You should be able to work around this by using two epoll files.Thanks for the explanation. I can now see more clearly how your patch works. I can also see why you might need the
ep->wswakeup_source. However I don't like it.
Quoted 5 lines
If it acted purely as a lock and prevented suspend while it was active then it would be fine. However it doesn't. It also aborts any current suspend attempt - so it is externally visible.
The way your code it written, *any* call toepoll_waitwill abort the current suspend cycle, even if it is called by a completely non-privileged user.
With the patch I posted Friday, a non-privileged user will not be able to pass EPOLLWAKEUP and have the wakeup-source created.
Quoted 15 lines
That may not obviously be harmful, but it makes the precise semantics of the system call something quite non-obvious, and it is much better to have a very clean semantic.
As you say, it can probably be worked-around but code is much safer when you don't need to work-around things.I see two alternatives:
1/ set the 'wakeup' flag on the whole epoll-fd, not on the individual events that it is asked to monitor. i.e. add a new flag toepoll_create1()instead of toepoll_ctlevents.
Then you just need a singlewakeup_sourcefor the fd which is active whenever any event is ready.This interface might be generally nicer, I'm not sure.
Quoted 15 lines
2/ Find a way to get rid of
ep->ws.
Thinking about it more, I again think it isn't needed.
The reason is that suspend is already exclusive with any process running in kernel context.
One of the first things suspend does is to freeze all process and (for regular non-kernel-thread processes) this happens by sending a virtual signal which is acted up when the process returns from a system call or returns from a context switch. So while any given system call is running (e.g.epoll_wait) suspend is blocked. Whenepoll_waitsetsTASK_INTERRUPTIBLEthe 'freeze' signal will interrupt it of course, but this is the only point where suspend can interfere withepoll_wait, and you aren't holdingep->wsthen anyway.
Hopefully Rafael will correct me if I got that outline wrong. But even if I did, I think we need to get rid ofep->ws.
If ep_scan_ready_list is only called from freezable threads, then ep->ws is not strictly needed, but without it another suspend attempt will be triggered if there are not other wakeup-sources active. I'm also not sure if it could get called from a non-freezable thread since other subsystems can call it through the poll hook.
A third option is to only activate ep->ws when needed. This may may work:
---
fs/eventpoll.c +0 -1
@@ -572,7 +572,6 @@ static int ep_scan_ready_list(struct eventpoll *ep,* in a lockless way. */ spin_lock_irqsave (&ep->lock, flags); -__pm_stay_awake (ep->ws);list_splice_init (&ep->rdllist, &txlist); ep->ovflist =NULL ;spin_unlock_irqrestore (&ep->lock, flags);
@@ -753,6 +752,8 @@ static int ep_read_events_proc(struct eventpoll
*ep, struct list_head *head,
* callback, but it's not actually ready, as far as
* caller requested events goes. We can remove it here. */
+ if (epi->ws && epi->ws->active)
+ __pm_stay_awake(ep->ws);
__pm_relax(epi->ws); list_del_init(&epi->rdllink);
}
@@ -1344,6 +1345,8 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
!list_empty(head) && eventcnt < esed->maxevents;) {
epi = list_first_entry(head, struct epitem, rdllink);
+ if (epi->ws && epi->ws->active)
+ __pm_stay_awake(ep->ws);
__pm_relax(epi->ws);
list_del_init(&epi->rdllink);
---
Quoted 8 lines
Also, I think it is important to clearly document how to use this safely. You suggested that if any EPOLLWAKEUP event is ready, then suspend will remain disabled not only until the event is handled, but also until the next call to
epoll_wait. That sounds like very useful semantics, but it isn't at all explicit in the patch. I think it should be made very clear ineventpoll.hhow the flag can be used. (and then eventually get this into a man page of course).
OK
Quoted 39 lines
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides
wakeup_sourcefor almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would getwakeup_sourcehandling for free.
Does this seem reasonable to you.Quoted 9 lines
Quoted 2 lines
Yes, it does.
How useful is that? Suspend already synchronizes with interrupt handlers and will not proceed until they have returned. Are threaded interrupts handlers not always run at that stage? For drivers that use work-queues instead of a threaded interrupt handler, I think the suspend-blocking work-queue patch I wrote a while back is convenient.
Maybe it isn't useful at all - I'm still working this stuff out.
Yes, threaded interrupts are run "straight away", but what exactly does that mean? And in particular, is there any interlocking to ensure they run before suspend gets stop the CPU? Maybe the scheduling priority of the different threads is enough to make sure this works, as
irq_threadsareSCHED_FIFOand the suspending thread almost certainly isn't. But is that still a guarantee on an SMP machine? irq_threads aren't freezable so suspend won't block on them for that reason..
Quoted 5 lines
I really just want to be sure that some interlock is in place to ensure that the threaded interrupt handler runs before suspend absolutely commits to suspending. If that is already the case, when what I suggest isn't needed as you suggest. Do you know of such an interlock?
Normal interrupts are disabled during suspend. This synchronizes with the interrupt handler, and pending wakeup interrupts abort suspend. I have not looked at this code since threaded interrupt handlers were added, so there could be bugs there.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 5/8] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 17 lines · On Mon, 30 Apr 2012 17:52:08 -0700 Arve Hjønnevåg <[email protected]> wrote:
Quoted 1 line · NeilBrown
On Thu, 26 Apr 2012 20:49:51 -0700 Arve Hjønnevåg <[email protected]> wrote:
...
Quoted 14 lines
Quoted 9 lines
I keep the wakeup-source active whenever the epitem is on a list (
ep->rdllistor the local txlist). The temporary txlist is modified without holding the lock that protectsep->rdllist. It is easier to use a separate wakeup source to prevent suspend while this list is manipulated than trying to maintain the wakeup-source state in a different way than the existing eventpoll state. I think this only causes real problems if the same epoll file is used for frequent non-wakeup events (e.g. a gyro) and wakeup events. You should be able to work around this by using two epoll files.Thanks for the explanation. I can now see more clearly how your patch works. I can also see why you might need the
ep->wswakeup_source. However I don't like it.
Quoted 8 lines
Quoted 5 lines
If it acted purely as a lock and prevented suspend while it was active then it would be fine. However it doesn't. It also aborts any current suspend attempt - so it is externally visible.
The way your code it written, *any* call toepoll_waitwill abort the current suspend cycle, even if it is called by a completely non-privileged user.With the patch I posted Friday, a non-privileged user will not be able to pass EPOLLWAKEUP and have the wakeup-source created.
Ahhh yes. I hadn't noticed that you only create ep->ws the first time that an epi->ws is created. So that aspect is fine, thanks.
Quoted 32 lines
Quoted 16 lines
That may not obviously be harmful, but it makes the precise semantics of the system call something quite non-obvious, and it is much better to have a very clean semantic.
As you say, it can probably be worked-around but code is much safer when you don't need to work-around things.I see two alternatives:
1/ set the 'wakeup' flag on the whole epoll-fd, not on the individual events that it is asked to monitor. i.e. add a new flag toepoll_create1()instead of toepoll_ctlevents.
Then you just need a singlewakeup_sourcefor the fd which is active whenever any event is ready.This interface might be generally nicer, I'm not sure.
Quoted 15 lines
2/ Find a way to get rid of
ep->ws.
Thinking about it more, I again think it isn't needed.
The reason is that suspend is already exclusive with any process running in kernel context.
One of the first things suspend does is to freeze all process and (for regular non-kernel-thread processes) this happens by sending a virtual signal which is acted up when the process returns from a system call or returns from a context switch. So while any given system call is running (e.g.epoll_wait) suspend is blocked. Whenepoll_waitsetsTASK_INTERRUPTIBLEthe 'freeze' signal will interrupt it of course, but this is the only point where suspend can interfere withepoll_wait, and you aren't holdingep->wsthen anyway.
Hopefully Rafael will correct me if I got that outline wrong. But even if I did, I think we need to get rid ofep->ws.
Quoted 5 lines
If
ep_scan_ready_listis only called from freezable threads, thenep->wsis not strictly needed, but without it another suspend attempt will be triggered if there are not other wakeup-sources active. I'm also not sure if it could get called from a non-freezable thread since other subsystems can call it through the poll hook.
I can see that triggering another suspend cycle that we know will fail is not ideal - every thread needs to move from 'idle' to 'frozen' and back again. I wonder if a lighter-weight mechanism might achieve that better. It feels like wakeup_source is being used for two very different purposes here - one to disable suspend while some event is pending, one to lock-out suspend during a critical piece of code. I feel it would be neater and more transparent if they were two different mechanisms.
If there were a second mechanism, I would like to see it used in pm_autosleep_set_state in place of autosleep_ws as well - it seems like a similar situation.
Maybe adding a second locking mechanism can be added later - I don't think the current code is wrong, it just seems inelegant.
Quoted 2 lines
A third option is to only activate
ep->wswhen needed. This may may work:
I think this is an improvement if we stay with using a wakeup_source to protect the critical code section. If we come up with a different mechanism, them it is not necessary.
Quoted 57 lines
Quoted 9 lines
Also, I think it is important to clearly document how to use this safely. You suggested that if any EPOLLWAKEUP event is ready, then suspend will remain disabled not only until the event is handled, but also until the next call to
epoll_wait. That sounds like very useful semantics, but it isn't at all explicit in the patch. I think it should be made very clear ineventpoll.hhow the flag can be used. (and then eventually get this into a man page of course).OK
Quoted 39 lines
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
One last item that doesn't really belong here - but it is in context.
This mechanism is elegant because it provides a single implementation that provides
wakeup_sourcefor almost any sort of device. I would like to do the same thing for interrupts.
Most (maybe all) of the wakeup device on my phone have an interrupt where the body is run in a thread. When the thread has done it's work the event is visible to userspace so the EPOLLWAKEUP mechanism is all that is needed to complete the path to user-space (or for my user-space solution, nothing else is needed once it is visible to user-space).
So we just need to ensure a clear path from the "top half" interrupt handler to the threaded handler.
So I imagine attaching a wakeup source to every interrupt for which 'wakeup' is enabled, activating it when the top-half starts and relaxing it when the bottom-half completes. With this in place, almost all drivers would getwakeup_sourcehandling for free.
Does this seem reasonable to you.Quoted 9 lines
Quoted 2 lines
Yes, it does.
How useful is that? Suspend already synchronizes with interrupt handlers and will not proceed until they have returned. Are threaded interrupts handlers not always run at that stage? For drivers that use work-queues instead of a threaded interrupt handler, I think the suspend-blocking work-queue patch I wrote a while back is convenient.
Maybe it isn't useful at all - I'm still working this stuff out.
Yes, threaded interrupts are run "straight away", but what exactly does that mean? And in particular, is there any interlocking to ensure they run before suspend gets stop the CPU? Maybe the scheduling priority of the different threads is enough to make sure this works, as
irq_threadsareSCHED_FIFOand the suspending thread almost certainly isn't. But is that still a guarantee on an SMP machine? irq_threads aren't freezable so suspend won't block on them for that reason..Quoted 5 lines
I really just want to be sure that some interlock is in place to ensure that the threaded interrupt handler runs before suspend absolutely commits to suspending. If that is already the case, when what I suggest isn't needed as you suggest. Do you know of such an interlock?
Quoted 5 lines
Normal interrupts are disabled during suspend. This synchronizes with the interrupt handler, and pending wakeup interrupts abort suspend. I have not looked at this code since threaded interrupt handlers were added, so there could be bugs there.
If disabling an interrupt ensured that the interrupt thread was idle, and thus synchronised with both interrupt handlers, that would be good...
And it does. suspend_device_irqs() calls synchronize_irq() on each suspended irq which waits for any irq thread to be idle. So it's all good.
i.e. in an interrupt is marked for WAKEUP, and its handler (whether threaded or not) calls wake_up on a wait_queue_head that is used by 'poll' to detect activity, then using EPOLLWAKEUP is sufficient to collect those events without racing with suspend. So we don't need to teach multiple subsystems about wakeup_sources - eventpoll and the interrupt handling can do it all. Excellent.
Thanks,
NeilBrown
PATCH 1/1 epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
In reply to Arve HjønnevågWhen an epoll_event, that has the EPOLLWAKEUP flag set, is ready, a wakeup_source will be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
--- fs/eventpoll.c | 90 ++++++++++++++++++++++++++++++++++++++++++- include/linux/capability.h | 5 ++- include/linux/eventpoll.h | 12 ++++++ 3 files changed, 103 insertions(+), 4 deletions(-)
fs/eventpoll.c +87 -3
@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h>
@@ -87,7 +88,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4
@@ -154,6 +155,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };
@@ -194,6 +198,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;
@@ -588,8 +595,10 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
@@ -602,6 +611,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/*
@@ -656,6 +666,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);
@@ -706,6 +718,7 @@ static void ep_free(struct eventpoll *ep)mutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +wakeup_source_unregister (ep->ws);kfree (ep); }
@@ -737,6 +750,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }
@@ -927,13 +941,23 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *kif (epi->next ==EP_UNACTIVE_PTR ) { epi->next = ep->ovflist; ep->ovflist = epi; +if (epi->ws) { +/* +* Activate ep->ws since epi->ws may get +* deactivated at any time. +*/ +__pm_stay_awake (ep->ws); + } + } goto out_unlock; }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -1091,6 +1115,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */
@@ -1118,6 +1166,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;
@@ -1164,6 +1219,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1204,6 +1260,9 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;
@@ -1229,6 +1288,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even epi->event.events = event->events; pt._key = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because
@@ -1244,6 +1309,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_evenspin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))
@@ -1282,6 +1348,18 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +/* +* Activate ep->ws before deactivating epi->ws to prevent +* triggering auto-suspend here (in case we reactive epi->ws +* below). +* +* This could be rearranged to delay the deactivation of epi->ws +* instead, but then epi->ws would temporarily be out of sync +* with ep_is_linked(). +*/ +if (epi->ws && epi->ws->active) +__pm_stay_awake (ep->ws); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); pt._key = epi->event.events;
@@ -1298,6 +1376,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,if (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;
@@ -1317,6 +1396,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }
@@ -1629,6 +1709,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,if (!tfile->f_op || !tfile->f_op->poll) goto error_tgt_fput; +/* Check if EPOLLWAKEUP is allowed */ +if ((epds.events &EPOLLWAKEUP ) && !capable (CAP_EPOLLWAKEUP )) + goto error_tgt_fput; +/* * We have to check that the file structure underneath the file descriptor * the user passed to us _is_ an eventpoll file. And also we do not permit
include/linux/capability.h +4 -1
@@ -360,8 +360,11 @@ struct cpu_vfs_cap_data {#define CAP_WAKE_ALARM 35 +/* Allow preventing automatic system suspends while epoll events are pending */ -#define CAP_LAST_CAP CAP_WAKE_ALARM +#define CAP_EPOLLWAKEUP 36 + +#define CAP_LAST_CAP CAP_EPOLLWAKEUP#define cap_valid (x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
include/linux/eventpoll.h +12 -0
@@ -26,6 +26,18 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +* +* Assuming neither EPOLLET nor EPOLLONESHOT is set, automatic system suspends +* will not be re-allowed until epoll_wait is called again after consuming the +* wakeup event(s). +* +* Requires CAP_EPOLLWAKEUP +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Signature · 1 line
1.7.7.3
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 8 lines · On Mon, 30 Apr 2012 22:33:48 -0700 Arve Hjønnevåg <[email protected]> wrote:
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.Signed-off-by Arve Hjønnevåg <[email protected]>Signed-off-by Rafael J. Wysocki <[email protected]>
Thanks.
However:
1/ I think all references to "automatic system suspend" can be replaced with
"system suspend" as an active wakeup_source disables any suspend, no matter it's source
2/ I reserve to right to submit for discussion a later patch which removes
the ep->ws in favour or some other exclusion mechanism :-)
NeilBrown
Quoted 287 lines
--- fs/eventpoll.c | 90 ++++++++++++++++++++++++++++++++++++++++++- include/linux/capability.h | 5 ++- include/linux/eventpoll.h | 12 ++++++ 3 files changed, 103 insertions(+), 4 deletions(-)fs/eventpoll.c +87
-3@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h> @@ -87,7 +88,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4@@ -154,6 +155,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };@@ -194,6 +198,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;@@ -588,8 +595,10 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after @@ -602,6 +611,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/* @@ -656,6 +666,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);@@ -706,6 +718,7 @@ static void ep_free(struct eventpoll *ep)mutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +wakeup_source_unregister (ep->ws);kfree (ep); }@@ -737,6 +750,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }@@ -927,13 +941,23 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *kif (epi->next ==EP_UNACTIVE_PTR ) { epi->next = ep->ovflist; ep->ovflist = epi; +if (epi->ws) { +/* +* Activate ep->ws since epi->ws may get +* deactivated at any time. +*/ +__pm_stay_awake (ep->ws); + } + } goto out_unlock; }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll() @@ -1091,6 +1115,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */ @@ -1118,6 +1166,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;@@ -1164,6 +1219,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))@@ -1204,6 +1260,9 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;@@ -1229,6 +1288,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even epi->event.events = event->events; pt._key = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because @@ -1244,6 +1309,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_evenspin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))@@ -1282,6 +1348,18 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +/* +* Activate ep->ws before deactivating epi->ws to prevent +* triggering auto-suspend here (in case we reactive epi->ws +* below). +* +* This could be rearranged to delay the deactivation of epi->ws +* instead, but then epi->ws would temporarily be out of sync +* with ep_is_linked(). +*/ +if (epi->ws && epi->ws->active) +__pm_stay_awake (ep->ws); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); pt._key = epi->event.events;@@ -1298,6 +1376,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,if (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;@@ -1317,6 +1396,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }@@ -1629,6 +1709,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,if (!tfile->f_op || !tfile->f_op->poll) goto error_tgt_fput; +/* Check if EPOLLWAKEUP is allowed */ +if ((epds.events &EPOLLWAKEUP ) && !capable (CAP_EPOLLWAKEUP )) + goto error_tgt_fput; +/* * We have to check that the file structure underneath the file descriptor * the user passed to us _is_ an eventpoll file. And also we do not permit include/linux/capability.h +4
-1@@ -360,8 +360,11 @@ struct cpu_vfs_cap_data {#define CAP_WAKE_ALARM 35 +/* Allow preventing automatic system suspends while epoll events are pending */ -#define CAP_LAST_CAP CAP_WAKE_ALARM +#define CAP_EPOLLWAKEUP 36 + +#define CAP_LAST_CAP CAP_EPOLLWAKEUP#define cap_valid (x) ((x) >= 0 && (x) <= CAP_LAST_CAP)include/linux/eventpoll.h +12
-0@@ -26,6 +26,18 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +* +* Assuming neither EPOLLET nor EPOLLONESHOT is set, automatic system suspends +* will not be re-allowed until epoll_wait is called again after consuming the +* wakeup event(s). +* +* Requires CAP_EPOLLWAKEUP +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30)
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 8 lines · NeilBrown
Quoted 6 lines · On Mon, 30 Apr 2012 22:33:48 -0700 Arve Hjønnevåg <[email protected]> wrote:
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
Quoted 5 lines
Quoted 2 lines
Signed-off-by Arve Hjønnevåg <[email protected]>Signed-off-by Rafael J. Wysocki <[email protected]>Thanks.
Reviewed-by NeilBrown <[email protected]>
Thanks a lot for your involvement here!
Quoted 4 lines
However:
1/ I think all references to "automatic system suspend" can be replaced with
"system suspend" as an activewakeup_sourcedisables any suspend, no matter it's source
OK, I'll change that when applying the patch (although that only applies to suspends taking the wakeup events signaling through wakeup sources into account).
Quoted 2 lines
2/ I reserve to right to submit for discussion a later patch which removes
theep->wsin favour or some other exclusion mechanism :-)
Well, you can alwyas do that. :-) Of course, when the patch goes to Linus, we'll have to be careful about changes visible to user space, though.
Thanks,
Rafael
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
In reply to Arve HjønnevågArve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
It's late it the -rc series, but it strikes me that CAP_EPOLLWAKEUP is a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like: CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.
Does that seem sensible to you? I can send a patch for the name change.
Thanks,
Michael
Quoted 297 lines
Signed-off-by Arve Hjønnevåg <[email protected]>Signed-off-by Rafael J. Wysocki <[email protected]>--- fs/eventpoll.c | 90 ++++++++++++++++++++++++++++++++++++++++++- include/linux/capability.h | 5 ++- include/linux/eventpoll.h | 12 ++++++ 3 files changed, 103 insertions(+), 4 deletions(-)fs/eventpoll.c +87
-3@@ -33,6 +33,7 @@#include <linux/bitops.h> #include <linux/mutex.h> #include <linux/anon_inodes.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h> @@ -87,7 +88,7 @@*/ /* Epoll private bits inside the event mask */ -#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET) +#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)/* Maximum number of nesting allowed inside epoll sets */ #define EP_MAX_NESTS 4@@ -154,6 +155,9 @@ struct epitem {/* List header used to link this item to the "struct file" items list */ struct list_head fllink; +/* wakeup_source used when EPOLLWAKEUP is set */ +struct wakeup_source *ws; +/* The structure that describe the interested events and the source fd */ struct epoll_event event; };@@ -194,6 +198,9 @@ struct eventpoll {*/ struct epitem *ovflist; +/* wakeup_source used when ep_scan_ready_list is running */ +struct wakeup_source *ws; +/* The user that created the eventpoll descriptor */ struct user_struct *user;@@ -588,8 +595,10 @@ static int ep_scan_ready_list(struct eventpoll *ep,* queued into ->ovflist but the "txlist" might already * contain them, and the list_splice() below takes care of them. */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + } }/* * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after @@ -602,6 +611,7 @@ static int ep_scan_ready_list(struct eventpoll *ep,* Quickly re-inject items left on "txlist". */ list_splice (&txlist, &ep->rdllist); +__pm_relax (ep->ws);if (!list_empty (&ep->rdllist)) {/* @@ -656,6 +666,8 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); +/* At this point it is safe to free the eventpoll item */ kmem_cache_free (epi_cache, epi);@@ -706,6 +718,7 @@ static void ep_free(struct eventpoll *ep)mutex_unlock (&epmutex);mutex_destroy (&ep->mtx);free_uid (ep->user); +wakeup_source_unregister (ep->ws);kfree (ep); }@@ -737,6 +750,7 @@ static int ep_read_events_proc(struct eventpoll *ep, struct list_head *head,* callback, but it's not actually ready, as far as * caller requested events goes. We can remove it here. */ +__pm_relax (epi->ws);list_del_init (&epi->rdllink); } }@@ -927,13 +941,23 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *kif (epi->next ==EP_UNACTIVE_PTR ) { epi->next = ep->ovflist; ep->ovflist = epi; +if (epi->ws) { +/* +* Activate ep->ws since epi->ws may get +* deactivated at any time. +*/ +__pm_stay_awake (ep->ws); + } + } goto out_unlock; }/* If this file is already in the ready list we exit soon */ -if (!ep_is_linked (&epi->rdllink)) +if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); + }/* * Wake up ( if active ) both the eventpoll wait list and the ->poll() @@ -1091,6 +1115,30 @@ static int reverse_path_check(void)return error; } +static int ep_create_wakeup_source (struct epitem *epi) +{ +const char *name; + +if (!epi->ep->ws) { + epi->ep->ws =wakeup_source_register ("eventpoll" ); +if (!epi->ep->ws) +return -ENOMEM ; + } + + name = epi->ffd.file->f_path.dentry->d_name.name; + epi->ws =wakeup_source_register (name); +if (!epi->ws) +return -ENOMEM ; + +return 0 ; +} + +static void ep_destroy_wakeup_source (struct epitem *epi) +{ +wakeup_source_unregister (epi->ws); + epi->ws =NULL ; +} +/* * Must be called with "mtx" held. */ @@ -1118,6 +1166,13 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, epi->event = *event; epi->nwait =0 ; epi->next =EP_UNACTIVE_PTR ; +if (epi->event.events &EPOLLWAKEUP ) { + error =ep_create_wakeup_source (epi); +if (error) + goto error_create_wakeup_source; + }else { + epi->ws =NULL ; + }/* Initialize the poll table using the queue callback */ epq.epi = epi;@@ -1164,6 +1219,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,/* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))@@ -1204,6 +1260,9 @@ error_unregister:list_del_init (&epi->rdllink);spin_unlock_irqrestore (&ep->lock, flags); +wakeup_source_unregister (epi->ws); + +error_create_wakeup_source:kmem_cache_free (epi_cache, epi);return error;@@ -1229,6 +1288,12 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even epi->event.events = event->events; pt._key = event->events; epi->event.data = event->data;/* protected by mtx */ +if (epi->event.events &EPOLLWAKEUP ) { +if (!epi->ws) +ep_create_wakeup_source (epi); + }else if (epi->ws) { +ep_destroy_wakeup_source (epi); + }/* * Get current event bits. We can safely use the file* here because @@ -1244,6 +1309,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_evenspin_lock_irq (&ep->lock);if (!ep_is_linked (&epi->rdllink)) {list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws);/* Notify waiting tasks that events are available */ if (waitqueue_active (&ep->wq))@@ -1282,6 +1348,18 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head, !list_empty(head) && eventcnt < esed->maxevents;) { epi = list_first_entry(head, struct epitem, rdllink); +/* +* Activate ep->ws before deactivating epi->ws to prevent +* triggering auto-suspend here (in case we reactive epi->ws +* below). +* +* This could be rearranged to delay the deactivation of epi->ws +* instead, but then epi->ws would temporarily be out of sync +* with ep_is_linked(). +*/ +if (epi->ws && epi->ws->active) +__pm_stay_awake (ep->ws); +__pm_relax (epi->ws);list_del_init (&epi->rdllink); pt._key = epi->event.events;@@ -1298,6 +1376,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,if (__put_user (revents, &uevent->events) ||__put_user (epi->event.data, &uevent->data)) {list_add (&epi->rdllink, head); +__pm_stay_awake (epi->ws);return eventcnt ? eventcnt : -EFAULT ; } eventcnt++;@@ -1317,6 +1396,7 @@ static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,* poll callback will queue them in ep->ovflist. */ list_add_tail (&epi->rdllink, &ep->rdllist); +__pm_stay_awake (epi->ws); } } }@@ -1629,6 +1709,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,if (!tfile->f_op || !tfile->f_op->poll) goto error_tgt_fput; +/* Check if EPOLLWAKEUP is allowed */ +if ((epds.events &EPOLLWAKEUP ) && !capable (CAP_EPOLLWAKEUP )) + goto error_tgt_fput; +/* * We have to check that the file structure underneath the file descriptor * the user passed to us _is_ an eventpoll file. And also we do not permit include/linux/capability.h +4
-1@@ -360,8 +360,11 @@ struct cpu_vfs_cap_data {#define CAP_WAKE_ALARM 35 +/* Allow preventing automatic system suspends while epoll events are pending */ -#define CAP_LAST_CAP CAP_WAKE_ALARM +#define CAP_EPOLLWAKEUP 36 + +#define CAP_LAST_CAP CAP_EPOLLWAKEUP#define cap_valid (x) ((x) >= 0 && (x) <= CAP_LAST_CAP)include/linux/eventpoll.h +12
-1@@ -26,6 +26,18 @@#define EPOLL_CTL_DEL 2#define EPOLL_CTL_MOD 3 +/* +* Request the handling of system wakeup events so as to prevent automatic +* system suspends from happening while those events are being processed. +* +* Assuming neither EPOLLET nor EPOLLONESHOT is set, automatic system suspends +* will not be re-allowed until epoll_wait is called again after consuming the +* wakeup event(s). +* +* Requires CAP_EPOLLWAKEUP +*/ +#define EPOLLWAKEUP (1 << 29) +/* Set the One Shot behaviour for the target file descriptor */ #define EPOLLONESHOT (1 << 30) --1.7.7.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Author of "The Linux Programming Interface", http://blog.man7.org/
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 10 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.It's late it the -rc series,
Well, exactly. :-)
Quoted 7 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).
Quoted 8 lines
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
Rafael
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
Quoted 17 lines
Quoted 1 line
It's late it the -rc series,
Well, exactly. :-)
Quoted 13 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.
We already have another api, "/sys/power/wake_lock", that allow user-space to block suspend. Do we want to apply this capability that api as well, or only to apis that do not have other ways to restrict access?
Quoted 7 lines
Quoted 2 lines
Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
CAP_BLOCK_SUSPEND is fine with me, but if it does not apply to the sysfs interface, then the comment should probably mention this.
Signature · 1 line
Arve Hjønnevåg
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 11 lines · Arve Hjønnevåg
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
It's late it the -rc series,
Well, exactly. :-)
If someone had CCed linux-api@ along the way (as per Documentation/SubmitChecklist), it might have helped ;-)
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).Quoted 6 lines
Quoted 6 lines
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.
Quoted 4 lines
We already have another api, "
/sys/power/wake_lock", that allow user-space to block suspend. Do we want to apply this capability that api as well, or only to apis that do not have other ways to restrict access?
Well, the question is: is there a governor on the use of /sys/power/wake_lock? It makes sense either they are both governed (preferably by the same mechanism, I would have thought), or neither is.
Quoted 9 lines
Quoted 6 lines
Quoted 1 line
Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
CAP_BLOCK_SUSPENDis fine with me, but if it does not apply to the sysfs interface, then the comment should probably mention this.
I've sent a patch, but omitted mention of API details in the comments. Maybe that can be changed afterward, when a decision has been reached about governing /sys/power/wake_lock.
Thanks,
Michael
Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Author of "The Linux Programming Interface", http://blog.man7.org/
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 12 lines · Michael Kerrisk
Quoted 11 lines · Arve Hjønnevåg
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
It's late it the -rc series,
Well, exactly. :-)
If someone had CCed linux-api@ along the way (as per
Documentation/SubmitChecklist), it might have helped ;-)
Well, it still _is_ late.
Quoted 31 lines
Quoted 14 lines
Quoted 7 lines
Quoted 7 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).Quoted 6 lines
Quoted 6 lines
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.Quoted 4 lines
We already have another api, "
/sys/power/wake_lock", that allow user-space to block suspend. Do we want to apply this capability that api as well, or only to apis that do not have other ways to restrict access?Well, the question is: is there a governor on the use of
/sys/power/wake_lock? It makes sense either they are both governed (preferably by the same mechanism, I would have thought), or neither is.Quoted 7 lines
Quoted 6 lines
Quoted 1 line
Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
Quoted 6 lines
Quoted 2 lines
CAP_BLOCK_SUSPENDis fine with me, but if it does not apply to the sysfs interface, then the comment should probably mention this.I've sent a patch, but omitted mention of API details in the comments. Maybe that can be changed afterward, when a decision has been reached about governing
/sys/power/wake_lock.
I'm going to push your patch for v3.5, but then I'm considering the following one for v3.6. I wouldn't like to make more changes in v3.5-rc at this point, if possible.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Require CAP_BLOCK_SUSPEND to use wake_lock/wake_unlock
Require processes wanting to use the wake_lock/wake_unlock sysfs files to have the CAP_BLOCK_SUSPEND capability, which also is required for the eventpoll EPOLLWAKEUP flag to be effective, so that all interfaces related to blocking autosleep depend on the same capability.
--- kernel/power/wakelock.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/power/wakelock.c +7 -0
@@ -9,6 +9,7 @@* manipulate wakelocks on Android. */ +#include <linux/capability.h> #include <linux/ctype.h> #include <linux/device.h> #include <linux/err.h>
@@ -188,6 +189,9 @@ int pm_wake_lock(const char *buf)size_t len;int ret =0 ; +if (!capable (CAP_BLOCK_SUSPEND )) +return -EPERM ; +while (*str && !isspace (*str)) str++;
@@ -231,6 +235,9 @@ int pm_wake_unlock(const char *buf)size_t len;int ret =0 ; +if (!capable (CAP_BLOCK_SUSPEND )) +return -EPERM ; + len =strlen (buf);if (!len)return -EINVAL ;
Greg KH
maintainerRe: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 71 lines · Rafael J. Wysocki
Quoted 12 lines · Michael Kerrisk
Quoted 11 lines · Arve Hjønnevåg
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
It's late it the -rc series,
Well, exactly. :-)
If someone had CCed linux-api@ along the way (as per
Documentation/SubmitChecklist), it might have helped ;-)Well, it still _is_ late.
Quoted 31 lines
Quoted 14 lines
Quoted 7 lines
Quoted 7 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).Quoted 6 lines
Quoted 6 lines
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.Quoted 4 lines
We already have another api, "
/sys/power/wake_lock", that allow user-space to block suspend. Do we want to apply this capability that api as well, or only to apis that do not have other ways to restrict access?Well, the question is: is there a governor on the use of
/sys/power/wake_lock? It makes sense either they are both governed (preferably by the same mechanism, I would have thought), or neither is.Quoted 7 lines
Quoted 6 lines
Quoted 1 line
Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
Quoted 6 lines
Quoted 2 lines
CAP_BLOCK_SUSPENDis fine with me, but if it does not apply to the sysfs interface, then the comment should probably mention this.I've sent a patch, but omitted mention of API details in the comments. Maybe that can be changed afterward, when a decision has been reached about governing
/sys/power/wake_lock.I'm going to push your patch for v3.5, but then I'm considering the following one for v3.6. I wouldn't like to make more changes in v3.5-rc at this point, if possible.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: RequireCAP_BLOCK_SUSPENDto use wake_lock/wake_unlock
Quoted 7 lines
Require processes wanting to use the wake_lock/wake_unlock sysfs files to have the
CAP_BLOCK_SUSPENDcapability, which also is required for the eventpoll EPOLLWAKEUP flag to be effective, so that all interfaces related to blocking autosleep depend on the same capability.Signed-off-by Rafael J. Wysocki <[email protected]>
Care to mark that for -stable as well?
thanks,
greg k-h
Re: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
Quoted 78 lines · Greg KH
Quoted 77 lines · Rafael J. Wysocki
Quoted 12 lines · Michael Kerrisk
Quoted 11 lines · Arve Hjønnevåg
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines · Michael Kerrisk
Arve, Rafael,
Quoted 5 lines · Arve Hjønnevåg
When an
epoll_event, that has the EPOLLWAKEUP flag set, is ready, awakeup_sourcewill be active to prevent suspend. This can be used to handle wakeup events from a driver that support poll, e.g. input, if that driver wakes up the waitqueue passed to epoll before allowing suspend.Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
It's late it the -rc series,
Well, exactly. :-)
If someone had CCed linux-api@ along the way (as per
Documentation/SubmitChecklist), it might have helped ;-)Well, it still _is_ late.
Quoted 31 lines
Quoted 14 lines
Quoted 7 lines
Quoted 7 lines
but it strikes me that
CAP_EPOLLWAKEUPis
a poor name for the capability that governs the use of EPOLLWAKEUP. While on the one hand some capabilities are overloaded
(https://lwn.net/Articles/486306/), on the other hand we should avoid adding individual capabilities for each new API feature (otherwise capabilities become administratively unwieldy).Quoted 6 lines
Quoted 6 lines
This capability is not really about "EPOLL". It's about the ability to block system suspend. Therefore, IMO, a better name would be something
like:CAP_BLOCK_SUSPEND. This name is better because there might be some other API feature that is later added that also has the effect of preventing system suspends, and we could reasonably govern that feature with the same capability.Quoted 4 lines
We already have another api, "
/sys/power/wake_lock", that allow user-space to block suspend. Do we want to apply this capability that api as well, or only to apis that do not have other ways to restrict access?Well, the question is: is there a governor on the use of
/sys/power/wake_lock? It makes sense either they are both governed (preferably by the same mechanism, I would have thought), or neither is.Quoted 7 lines
Quoted 6 lines
Quoted 1 line
Does that seem sensible to you? I can send a patch for the name change.
I'm not sure what Arve thinks about that, but I'd be fine with that.
Arve, what do you think?
Quoted 6 lines
Quoted 2 lines
CAP_BLOCK_SUSPENDis fine with me, but if it does not apply to the sysfs interface, then the comment should probably mention this.I've sent a patch, but omitted mention of API details in the comments. Maybe that can be changed afterward, when a decision has been reached about governing
/sys/power/wake_lock.I'm going to push your patch for v3.5, but then I'm considering the following one for v3.6. I wouldn't like to make more changes in v3.5-rc at this point, if possible.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: RequireCAP_BLOCK_SUSPENDto use wake_lock/wake_unlockRequire processes wanting to use the wake_lock/wake_unlock sysfs files to have the
CAP_BLOCK_SUSPENDcapability, which also is required for the eventpoll EPOLLWAKEUP flag to be effective, so that all interfaces related to blocking autosleep depend on the same capability.
Quoted 3 lines
Quoted 1 line
Signed-off-by Rafael J. Wysocki <[email protected]>Care to mark that for -stable as well?
I will.
Thanks,
Rafael
Michael Kerrisk (man-pages)
Acked-by Michael KerriskRe: [PATCH] epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
In reply to Rafael J. WysockiOn Tue, Jul 17, 2012 at 9:22 PM, Rafael J. Wysocki <[email protected]> wrote:
[...]
Quoted 1 line
I'm going to push your patch for v3.5,
Thanks.
Quoted 2 lines
but then I'm considering the following
one for v3.6. I wouldn't like to make more changes in v3.5-rc at this point,
Thanks,
Michael
Quoted 47 lines
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: RequireCAP_BLOCK_SUSPENDto use wake_lock/wake_unlockRequire processes wanting to use the wake_lock/wake_unlock sysfs files to have the
CAP_BLOCK_SUSPENDcapability, which also is required for the eventpoll EPOLLWAKEUP flag to be effective, so that all interfaces related to blocking autosleep depend on the same capability.Signed-off-by Rafael J. Wysocki <[email protected]>--- kernel/power/wakelock.c | 7 +++++++ 1 file changed, 7 insertions(+)Index:
linux/kernel/power/wakelock.c
===================================================================kernel/power/wakelock.c +7
-0@@ -9,6 +9,7 @@* manipulate wakelocks on Android. */ +#include <linux/capability.h> #include <linux/ctype.h> #include <linux/device.h> #include <linux/err.h> @@ -188,6 +189,9 @@ int pm_wake_lock(const char *buf)size_t len;int ret =0 ; +if (!capable (CAP_BLOCK_SUSPEND )) +return -EPERM ; +while (*str && !isspace (*str)) str++;@@ -231,6 +235,9 @@ int pm_wake_unlock(const char *buf)size_t len;int ret =0 ; +if (!capable (CAP_BLOCK_SUSPEND )) +return -EPERM ; + len =strlen (buf);if (!len)return -EINVAL ;
Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Author of "The Linux Programming Interface"; http://man7.org/tlpi/
RFC PATCH 6/8 PM / Sleep: Implement opportunistic sleep
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep.
That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- Documentation/ABI/testing/sysfs-power | 17 ++++ drivers/base/power/wakeup.c | 38 ++++++----- include/linux/suspend.h | 13 +++ kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 113 ++++++++++++++++++++++++++++++++ kernel/power/main.c | 117 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 290 insertions(+), 35 deletions(-)
kernel/power/Makefile +1 -0
@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern int pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline int pm_autosleep_lock (void ) {return 0 ; } +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +113 -0
113 lines not on this page · raw message
kernel/power/main.c +99 -18
169 lines not on this page · raw message
drivers/base/power/wakeup.c +22 -16
@@ -498,8 +498,10 @@ static void wakeup_source_deactivate(strtrace_wakeup_source_deactivate (ws->name, cec);split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + } }/**
@@ -660,29 +662,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Documentation/ABI/testing/sysfs-power +17 -0
@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 8 lines · On Sun, 22 Apr 2012 23:23:23 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: "Rafael J. Wysocki" <[email protected]>
To: Linux PM list <[email protected]>
Cc: LKML <[email protected]>, Magnus Damm <[email protected]>, [email protected], Matthew Garrett <[email protected]>, Greg KH <[email protected]>, Arve Hjønnevåg <[email protected]>, John Stultz <[email protected]>, Brian Swetland <[email protected]>, Neil Brown <[email protected]>, Alan Stern <[email protected]>, Dmitry Torokhov <[email protected]>, "Srivatsa S. Bhat" <[email protected]>
Subject: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Date: Sun, 22 Apr 2012 23:23:23 +0200
Sender: [email protected]
User-Agent: KMail/1.13.6 (Linux/3.4.0-rc3+; KDE/4.6.0;x86_64; ; )
Quoted 5 lines
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Hi Rafael,
just a few little issues below. Over all I think that if we have to have auto-sleep in the kernel, then this is a good way to do it.
Quoted 20 lines
kernel/power/autosleep.c new file +20
-0+static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out; + } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate ();
kernel/power/autosleep.c line 49
Quoted 10 lines
kernel/power/autosleep.c new file +10
-0+else +pm_suspend (autosleep_state); + +mutex_unlock (&autosleep_lock); + +if (!pm_get_wakeup_count (&final_count, false)) + goto out; + +if (final_count == initial_count) +schedule_timeout (HZ /2 );
This doesn't do what you seem to expect it to do.
You need to set current->state to something like TASK_UNINTERRUPTIBLE before calling schedule_timeout, otherwise it is effectily a no-op. schedule_timeout_uninterruptible(), for example, will do this for you.
However the value of this isn't clear to me, so a comment would probably be a good thing.
This continue presumably fires if we wake up without any wakeup sources being activated. In that case you want to delay for 500ms - presumably to avoid a tight suspend/resume loop if something goes wrong?
I have occasionally seen a stray/uninteresting interrupt wake from suspend immediately after entering suspend and the next attempt succeeds. Maybe this is a bug in some driver somewhere, but not a big one. I think I would rather in that case that we attempt to re-enter suspend immediately. Maybe after a few failed attempts it makes sense to back off.
The other question is: if we want to back-off, is 500ms really enough? What will be gained by, or could be achieved in, that time? An exponential back-off might be defensible, but I can't see the value of a 500ms fixed back-off.
However if you can, I'd love to see a comment in there explaining it.
Quoted 13 lines
kernel/power/autosleep.c new file +11
-0+ + out: +queue_up_suspend_work (); +} + + +int pm_autosleep_set_state (suspend_state_t state) +{ + +#ifndef CONFIG_HIBERNATION +if (state >=PM_SUSPEND_MAX )
kernel/power/autosleep.c line 92
Quoted 10 lines
kernel/power/autosleep.c new file +10
-0+return -EINVAL ; +#endif + +__pm_stay_awake (autosleep_ws); + +mutex_lock (&autosleep_lock); + + autosleep_state = state; + +__pm_relax (autosleep_ws);
I'm struggling to see the point of the autosleep_ws.
A suspend cannot actually happen while this code is running (can it?) because it will wait for the process to enter the freezer.
So the only effect of this is:
1/ cause the current auto-sleep cycle to abort and
2/ maybe add some accounting number is the autosleep_ws.
Is that right?
Which of these is needed?
I would imagine that any process writing to /sys/power/autosleep would be holding a wakelock, and if it didn't it should expect things to be racy...
Am I missing something?
kernel/power/autosleep.c line 95
Quoted 3 lines
kernel/power/autosleep.c new file +3
-0+ +if (state >PM_SUSPEND_ON ) +queue_up_suspend_work ();
The test here is superfluous as queue_up_suspend_work() itself tests that 'state' is > PM_SUSPEND_ON. However maybe it is more readable this way, so I won't object it you like it.
Quoted 7 lines
kernel/power/autosleep.c new file +4
-0+ +mutex_unlock (&autosleep_lock); +return 0 ; +}
kernel/power/autosleep.c static ssize_t wakeup_count_show(struct · line 364
Quoted 7 lines
kernel/power/autosleep.c new file +2
-1@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct {unsigned int val; -return pm_get_wakeup_count (&val) ?sprintf (buf,"%u\n" , val) : -EINTR ; +return pm_get_wakeup_count (&val, true) ? +sprintf (buf,"%u\n" , val) : -EINTR ; }
I think it would be really nice for user-space auto-suspend if the 'block' flag to be settable from the O_NONBLOCK setting. And for poll() to work on /sys/power/wakeup-count. However this would require a bit of surgery on sysfs. So that is a "maybe later", but having the 'block' flag in there is a step in the right direction.
Quoted 10 lines
kernel/power/main.c +4
-0static ssize_t wakeup_count_store(struct kobject *kobj,@@ -347,15 +368,69 @@ static ssize_t wakeup_count_store(struct const char *buf, size_t n) {unsigned int val; +int error; + + error =pm_autosleep_lock (); +if (error)
kernel/power/main.c static ssize_t wakeup_count_store(struct · line 384
Quoted 10 lines
kernel/power/main.c +6
-0+return error; + +if (pm_autosleep_state () >PM_SUSPEND_ON ) { + error = -EBUSY ; + goto out; + }if (sscanf (buf,"%u" , &val) ==1 ) {if (pm_save_wakeup_count (val))return n;
You need a 'pm_autosleep_unlock() in there - or possibly
error = n; goto out;
Quoted 15 lines
kernel/power/main.c +5
-1} -return -EINVAL ; + error = -EINVAL ; + + out: +pm_autosleep_unlock (); +return error; }core_initcall (pm_init);Index:
linux/drivers/base/power/wakeup.c
===================================================================
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 504
Quoted 8 lines
drivers/base/power/wakeup.c +3
-1@@ -498,8 +498,10 @@ static void wakeup_source_deactivate(strtrace_wakeup_source_deactivate (ws->name, cec);split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + }
This doesn't look right. suspend_work always requeues itself unless autosleep_state == PM_SUSPEND_ON, and whenver autosleep_state is set we already call queue_up_suspend_work(). So there is no need to call it here.
Quoted 5 lines
Index: linux/Documentation/ABI/testing/sysfs-power
===================================================================
Documentation/ABI/testing/sysfs-power Description: · line 180
Quoted 9 lines
Documentation/ABI/testing/sysfs-power +6
-0@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings
"To the .. file can be written..." or
"The .. file can have written ..." or
"One of the strings returned by (reads from) /sys/power/state can be written to the file ..."
??
Documentation/ABI/testing/sysfs-power Description: · line 185
Quoted 5 lines
Documentation/ABI/testing/sysfs-power +5
-0+ returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless
^^^^
"every"
Documentation/ABI/testing/sysfs-power Description: · line 191
Quoted 6 lines
Documentation/ABI/testing/sysfs-power +6
-0+ of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.
^^^^^^^^^ "returned".
Thanks,
NeilBrown
Rafael J. Wysocki
Acked-by Greg Kroah-HartmanRe: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 10 lines · NeilBrown
Quoted 8 lines · On Sun, 22 Apr 2012 23:23:23 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: "Rafael J. Wysocki" <[email protected]>
To: Linux PM list <[email protected]>
Cc: LKML <[email protected]>, Magnus Damm <[email protected]>, [email protected], Matthew Garrett <[email protected]>, Greg KH <[email protected]>, Arve Hjønnevåg <[email protected]>, John Stultz <[email protected]>, Brian Swetland <[email protected]>, Neil Brown <[email protected]>, Alan Stern <[email protected]>, Dmitry Torokhov <[email protected]>, "Srivatsa S. Bhat" <[email protected]>
Subject: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Date: Sun, 22 Apr 2012 23:23:23 +0200
Sender: [email protected]
User-Agent: KMail/1.13.6 (Linux/3.4.0-rc3+; KDE/4.6.0;x86_64; ; )
Quoted 7 lines
Quoted 5 lines
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Hi Rafael,
Hi,
Quoted 2 lines
just a few little issues below. Over all I think that if we have to have auto-sleep in the kernel, then this is a good way to do it.
Good, we seem to agree in principle, then. :-)
Quoted 27 lines
Quoted 27 lines
(quoted diff) +27
-0+static void try_to_suspend(struct work_struct *work) +{ + unsigned int initial_count, final_count; + + if (!pm_get_wakeup_count(&initial_count, true)) + goto out; + + mutex_lock(&autosleep_lock); + + if (!pm_save_wakeup_count(initial_count)) { + mutex_unlock(&autosleep_lock); + goto out; + } + + if (autosleep_state == PM_SUSPEND_ON) { + mutex_unlock(&autosleep_lock); + return; + } + if (autosleep_state >= PM_SUSPEND_MAX) + hibernate(); + else + pm_suspend(autosleep_state); + + mutex_unlock(&autosleep_lock); + + if (!pm_get_wakeup_count(&final_count, false)) + goto out;
Quoted 8 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ + if (final_count == initial_count) + schedule_timeout(HZ / 2);This doesn't do what you seem to expect it to do.
You need to setcurrent->stateto something likeTASK_UNINTERRUPTIBLEbefore callingschedule_timeout, otherwise it is effectily a no-op.schedule_timeout_uninterruptible(), for example, will do this for you.
Right. I obviously overlooked the missing state change.
Quoted 5 lines
However the value of this isn't clear to me, so a comment would probably be a good thing.
This continue presumably fires if we wake up without any wakeup sources being activated. In that case you want to delay for 500ms - presumably to avoid a tight suspend/resume loop if something goes wrong?
Yes.
Quoted 5 lines
I have occasionally seen a stray/uninteresting interrupt wake from suspend immediately after entering suspend and the next attempt succeeds. Maybe this is a bug in some driver somewhere, but not a big one. I think I would rather in that case that we attempt to re-enter suspend immediately. Maybe after a few failed attempts it makes sense to back off.
Perhaps. We can adjust this particular thing later, I think.
Quoted 5 lines
The other question is: if we want to back-off, is 500ms really enough? What will be gained by, or could be achieved in, that time? An exponential back-off might be defensible, but I can't see the value of a 500ms fixed back-off.
However if you can, I'd love to see a comment in there explaining it.
Sure.
Quoted 26 lines
Quoted 13 lines
(quoted diff) +11
-0+ + out: + queue_up_suspend_work(); +} + + +int pm_autosleep_set_state(suspend_state_t state) +{ + +#ifndef CONFIG_HIBERNATION + if (state >= PM_SUSPEND_MAX)Review
Quoted 10 lines
(quoted diff) +10
-0+ return -EINVAL; +#endif + + __pm_stay_awake(autosleep_ws); + + mutex_lock(&autosleep_lock); + + autosleep_state = state; + + __pm_relax(autosleep_ws);I'm struggling to see the point of the
autosleep_ws.
Quoted 7 lines
A suspend cannot actually happen while this code is running (can it?) because it will wait for the process to enter the freezer.
So the only effect of this is:
1/ cause the current auto-sleep cycle to abort and
2/ maybe add some accounting number is theautosleep_ws.
Is that right?
Which of these is needed?
This is to solve a problem when user space attempts to echo "off" to /sys/power/autosleep exactly when pm_suspend() is initiated as a part of autosleep under the autosleep lock. In that case, if autosleep_ws is not there, the process wanting to disable autosleep will have to wait for the pm_suspend() to complete (unless it holds a wakelock), which is suboptimal.
Quoted 4 lines
I would imagine that any process writing to
/sys/power/autosleepwould be holding a wakelock, and if it didn't it should expect things to be racy...Am I missing something?
The assumption above is kind of optimistic in my opinion. That process very well may be a system administrator's bash, for example. :-)
Quoted 7 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ + if (state > PM_SUSPEND_ON) + queue_up_suspend_work();The test here is superfluous as
queue_up_suspend_work()itself tests that 'state' is >PM_SUSPEND_ON. However maybe it is more readable this way, so I won't object it you like it.
Well, patch [7/8] adds the second statement under this conditional, so I'd prefer to keep it the current way.
Quoted 10 lines
Quoted 10 lines
(quoted diff) +4
-0+ + mutex_unlock(&autosleep_lock); + return 0; +}@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct { unsigned int val;
Quoted 10 lines
Review static ssize_t wakeup_count_show(struct · line 364
Quoted 4 lines
(quoted diff) +2
-1- return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; + return pm_get_wakeup_count(&val, true) ? + sprintf(buf, "%u\n", val) : -EINTR; }I think it would be really nice for user-space auto-suspend if the 'block' flag to be settable from the
O_NONBLOCKsetting. And forpoll()to work on/sys/power/wakeup-count. However this would require a bit of surgery on sysfs. So that is a "maybe later", but having the 'block' flag in there is a step in the right direction.
Yes, "maybe later" is what I think about that too. :-)
Quoted 17 lines
Quoted 17 lines
(quoted diff) +10
-0static ssize_t wakeup_count_store(struct kobject *kobj,@@ -347,15 +368,69 @@ static ssize_t wakeup_count_store(struct const char *buf, size_t n) { unsigned int val; + int error; + + error = pm_autosleep_lock(); + if (error) + return error; + + if (pm_autosleep_state() > PM_SUSPEND_ON) { + error = -EBUSY; + goto out; + }
Quoted 6 lines
Review static ssize_t wakeup_count_store(struct · line 384
Quoted 3 lines
(quoted diff) +0
-0if (sscanf(buf, "%u", &val) == 1) { if (pm_save_wakeup_count(val)) return n;You need a '
pm_autosleep_unlock()in there - or possibly
error = n; goto out;
Right, thanks for spotting this!
Quoted 17 lines
Quoted 17 lines
(quoted diff) +5
-1} - return -EINVAL; + error = -EINVAL; + + out: + pm_autosleep_unlock(); + return error; } core_initcall(pm_init);Index:
linux/drivers/base/power/wakeup.c
===================================================================drivers/base/power/wakeup.c +0
-0@@ -498,8 +498,10 @@ static void wakeup_source_deactivate(strtrace_wakeup_source_deactivate (ws->name, cec);
Quoted 10 lines
drivers/base/power/wakeup.c static void wakeup_source_deactivate(str · line 504
Quoted 6 lines
drivers/base/power/wakeup.c +3
-1split_counters (&cnt, &inpr); -if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) +if (!inpr &&waitqueue_active (&wakeup_count_wait_queue)) {wake_up (&wakeup_count_wait_queue); +queue_up_suspend_work (); + }This doesn't look right.
suspend_workalways requeues itself unlessautosleep_state==PM_SUSPEND_ON, and whenverautosleep_stateis set we already callqueue_up_suspend_work(). So there is no need to call it here.
OK, I agree. Good, I don't have to add more code to wakeup_source_deactivate(). :-)
Quoted 28 lines
Quoted 5 lines
Index: linux/Documentation/ABI/testing/sysfs-power
===================================================================Documentation/ABI/testing/sysfs-power Description: · line 180
Quoted 9 lines
Documentation/ABI/testing/sysfs-power +6
-0@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: February 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings"To the .. file can be written..." or
"The .. file can have written ..." or
"One of the strings returned by (reads from)/sys/power/statecan be written to the file ..."
??Documentation/ABI/testing/sysfs-power Description: · line 185
Quoted 5 lines
Documentation/ABI/testing/sysfs-power +5
-0+ returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After evey execution, regardless^^^^"every"
Quoted 7 lines
Documentation/ABI/testing/sysfs-power Description: · line 191
Quoted 6 lines
Documentation/ABI/testing/sysfs-power +6
-0+ of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be displayed.^^^^^^^^^ "returned".
Well spotted, thanks!
Below is an updated patch hopefully addressing your comments.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
It consists of a new sysfs attribute, /sys/power/autosleep, that can be written one of the strings returned by reads from /sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to /sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to /sys/power/autosleep.
That work item enables the detection of wakeup events using the functions already defined in drivers/base/power/wakeup.c (with one small modification) and calls either pm_suspend(), or hibernate() to put the system into a sleep state. If a wakeup event is reported while the transition is in progress, it will abort the transition and the "system suspend" work item will be queued up again.
--- Documentation/ABI/testing/sysfs-power | 17 ++++ drivers/base/power/wakeup.c | 34 +++++---- include/linux/suspend.h | 13 +++ kernel/power/Kconfig | 8 ++ kernel/power/Makefile | 1 kernel/power/autosleep.c | 117 +++++++++++++++++++++++++++++++++ kernel/power/main.c | 119 ++++++++++++++++++++++++++++------ kernel/power/power.h | 18 +++++ 8 files changed, 292 insertions(+), 35 deletions(-)
kernel/power/Makefile +1 -0
@@ -9,5 +9,6 @@ obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o +obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
kernel/power/Kconfig +8 -0
@@ -103,6 +103,14 @@ config PM_SLEEP_SMP select HOTPLUG select HOTPLUG_CPU +config PM_AUTOSLEEP + bool "Opportunistic sleep" + depends on PM_SLEEP + default n + ---help--- + Allow the kernel to trigger a system transition into a global sleep + state automatically whenever there are no active wakeup sources. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/power.h +18 -0
@@ -264,3 +264,21 @@ static inline void suspend_thaw_processe { }#endif + +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +extern int pm_autosleep_init (void ); +extern int pm_autosleep_lock (void ); +extern void pm_autosleep_unlock (void ); +extern suspend_state_t pm_autosleep_state (void ); +extern int pm_autosleep_set_state (suspend_state_t state); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline int pm_autosleep_init (void ) {return 0 ; } +static inline int pm_autosleep_lock (void ) {return 0 ; } +static inline void pm_autosleep_unlock (void ) {} +static inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; } + +#endif /* !CONFIG_PM_AUTOSLEEP */
include/linux/suspend.h +12 -1
@@ -356,7 +356,7 @@ extern int unregister_pm_notifier(structextern bool events_check_enabled;extern bool pm_wakeup_pending (void ); -extern bool pm_get_wakeup_count (unsigned int *count); +extern bool pm_get_wakeup_count (unsigned int *count,bool block);extern bool pm_save_wakeup_count (unsigned int count);static inline void lock_system_sleep (void )
@@ -407,6 +407,17 @@ static inline void unlock_system_sleep(v#endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_AUTOSLEEP + +/* kernel/power/autosleep.c */ +void queue_up_suspend_work (void ); + +#else /* !CONFIG_PM_AUTOSLEEP */ + +static inline void queue_up_suspend_work (void ) {} + +#endif /* !CONFIG_PM_AUTOSLEEP */ +#ifdef CONFIG_ARCH_SAVE_PAGE_KEYS /* * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
kernel/power/autosleep.c new file +117 -0
117 lines not on this page · raw message
kernel/power/main.c +100 -19
170 lines not on this page · raw message
drivers/base/power/wakeup.c +19 -15
@@ -660,29 +660,33 @@ bool pm_wakeup_pending(void)/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. +* @block: Whether or not to block. * -* Store the number of registered wakeup events at the address in @count. Block -* if the current number of wakeup events being processed is nonzero. +* Store the number of registered wakeup events at the address in @count. If +* @block is set, block until the current number of wakeup events being +* processed is zero. * -* Return 'false' if the wait for the number of wakeup events being processed to -* drop down to zero has been interrupted by a signal (and the current number -* of wakeup events being processed is still nonzero). Otherwise return 'true'. +* Return 'false' if the current number of wakeup events being processed is +* nonzero. Otherwise return 'true'. */ -bool pm_get_wakeup_count (unsigned int *count) +bool pm_get_wakeup_count (unsigned int *count,bool block) {unsigned int cnt, inpr; -DEFINE_WAIT (wait); -for (;;) { -prepare_to_wait (&wakeup_count_wait_queue, &wait, -TASK_INTERRUPTIBLE ); -split_counters (&cnt, &inpr); -if (inpr ==0 ||signal_pending (current)) -break ; +if (block) { +DEFINE_WAIT (wait); -schedule (); +for (;;) { +prepare_to_wait (&wakeup_count_wait_queue, &wait, +TASK_INTERRUPTIBLE ); +split_counters (&cnt, &inpr); +if (inpr ==0 ||signal_pending (current)) +break ; + +schedule (); + } +finish_wait (&wakeup_count_wait_queue, &wait); } -finish_wait (&wakeup_count_wait_queue, &wait);split_counters (&cnt, &inpr); *count = cnt;
Documentation/ABI/testing/sysfs-power +17 -0
@@ -172,3 +172,20 @@ Description: Reading from this file will display the current value, which is set to 1 MB by default. + +What: /sys/power/autosleep +Date: April 2012 +Contact: Rafael J. Wysocki <[email protected]> +Description: + The /sys/power/autosleep file can be written one of the strings + returned by reads from /sys/power/state. If that happens, a + work item attempting to trigger a transition of the system to + the sleep state represented by that string is queued up. This + attempt will only succeed if there are no active wakeup sources + in the system at that time. After every execution, regardless + of whether or not the attempt to put the system to sleep has + succeeded, the work item requeues itself until user space + writes "off" to /sys/power/autosleep. + + Reading from this file causes the last string successfully + written to it to be returned.
NeilBrown
Reviewed-byRe: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 128 lines · On Thu, 26 Apr 2012 23:52:42 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
Quoted 10 lines · NeilBrown
Quoted 8 lines · On Sun, 22 Apr 2012 23:23:23 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: "Rafael J. Wysocki" <[email protected]>
To: Linux PM list <[email protected]>
Cc: LKML <[email protected]>, Magnus Damm <[email protected]>, [email protected], Matthew Garrett <[email protected]>, Greg KH <[email protected]>, Arve Hjønnevåg <[email protected]>, John Stultz <[email protected]>, Brian Swetland <[email protected]>, Neil Brown <[email protected]>, Alan Stern <[email protected]>, Dmitry Torokhov <[email protected]>, "Srivatsa S. Bhat" <[email protected]>
Subject: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Date: Sun, 22 Apr 2012 23:23:23 +0200
Sender: [email protected]
User-Agent: KMail/1.13.6 (Linux/3.4.0-rc3+; KDE/4.6.0;x86_64; ; )Quoted 7 lines
Quoted 5 lines
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Hi Rafael,
Hi,
Quoted 2 lines
just a few little issues below. Over all I think that if we have to have auto-sleep in the kernel, then this is a good way to do it.
Good, we seem to agree in principle, then. :-)
Quoted 27 lines
Quoted 27 lines
kernel/power/autosleep.c new file +27
-0+static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out; + } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate (); +else +pm_suspend (autosleep_state); + +mutex_unlock (&autosleep_lock); + +if (!pm_get_wakeup_count (&final_count, false)) + goto out;Quoted 8 lines
kernel/power/autosleep.c
Quoted 3 lines
kernel/power/autosleep.c new file +3
-0+ +if (final_count == initial_count) +schedule_timeout (HZ /2 );This doesn't do what you seem to expect it to do.
You need to setcurrent->stateto something likeTASK_UNINTERRUPTIBLEbefore callingschedule_timeout, otherwise it is effectily a no-op.schedule_timeout_uninterruptible(), for example, will do this for you.Right. I obviously overlooked the missing state change.
Quoted 5 lines
However the value of this isn't clear to me, so a comment would probably be a good thing.
This continue presumably fires if we wake up without any wakeup sources being activated. In that case you want to delay for 500ms - presumably to avoid a tight suspend/resume loop if something goes wrong?Yes.
Quoted 5 lines
I have occasionally seen a stray/uninteresting interrupt wake from suspend immediately after entering suspend and the next attempt succeeds. Maybe this is a bug in some driver somewhere, but not a big one. I think I would rather in that case that we attempt to re-enter suspend immediately. Maybe after a few failed attempts it makes sense to back off.
Perhaps. We can adjust this particular thing later, I think.
Quoted 5 lines
The other question is: if we want to back-off, is 500ms really enough? What will be gained by, or could be achieved in, that time? An exponential back-off might be defensible, but I can't see the value of a 500ms fixed back-off.
However if you can, I'd love to see a comment in there explaining it.Sure.
Quoted 26 lines
Quoted 13 lines
kernel/power/autosleep.c new file +11
-0+ + out: +queue_up_suspend_work (); +} + + +int pm_autosleep_set_state (suspend_state_t state) +{ + +#ifndef CONFIG_HIBERNATION +if (state >=PM_SUSPEND_MAX )kernel/power/autosleep.c line 96
Quoted 10 lines
kernel/power/autosleep.c new file +10
-0+return -EINVAL ; +#endif + +__pm_stay_awake (autosleep_ws); + +mutex_lock (&autosleep_lock); + + autosleep_state = state; + +__pm_relax (autosleep_ws);I'm struggling to see the point of the
autosleep_ws.Quoted 7 lines
A suspend cannot actually happen while this code is running (can it?) because it will wait for the process to enter the freezer.
So the only effect of this is:
1/ cause the current auto-sleep cycle to abort and
2/ maybe add some accounting number is theautosleep_ws.
Is that right?
Which of these is needed?This is to solve a problem when user space attempts to echo "off" to
/sys/power/autosleepexactly whenpm_suspend()is initiated as a part of autosleep under the autosleep lock. In that case, ifautosleep_wsis not there, the process wanting to disable autosleep will have to wait for thepm_suspend()to complete (unless it holds a wakelock), which is suboptimal.
Quoted 7 lines
Quoted 4 lines
I would imagine that any process writing to
/sys/power/autosleepwould be holding a wakelock, and if it didn't it should expect things to be racy...Am I missing something?
The assumption above is kind of optimistic in my opinion. That process very well may be a system administrator's bash, for example. :-)
If it is, then presumably the auto-sleep could kick in between any pair of keystrokes that the sysadmin types. Or between the final 'enter' and when the write() system call begins. All that autosleep_ws seems to provide is certainty that when the write() system call completes, autosleep will be fully disabled.
I don't think that is really worth anything.
However, something did occur to me that I would like clarified. What happens if try_to_suspend() gets the autosleep_lock just before wakeup_count_store(), state_store() or pm_autosleep_set_state() try to get it?
For pm_autosleep_set_state() the try_to_suspend() attempt will abort because it is holding autosleep_ws, so it will drop the lock and pm_autosleep_set_state() will continue happily.
For the other two, what will happen (if there are no active wakesources and autosleep is enabled).
I'm guessing that try_to_suspend will try to freeze all the process, which sends a pseudo signal to all processes, so the mutex_lock_interruptible will fail and the suspend will complete.
Then will the aborted write() system call be re-attempted?
If that is right, then here is a very clear need to autosleep_ws: it prevents a deadlock.
So it appears there is a very real need for autosleep_ws that even I can agree with. It seems subtle though and could usefully be documented:
/* Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
* is active, otherwise a deadlock with try_to_suspend() is possible.
* Alternatively mutex_lock_interruptible() can be used. This will then fail
* if an auto_sleep cycle tries to freeze processes.
*/
static DEFINE_MUTEX(autosleep_lock);
So:
Thanks,
NeilBrown
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 159 lines · NeilBrown
Quoted 128 lines · On Thu, 26 Apr 2012 23:52:42 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
Quoted 10 lines · NeilBrown
Quoted 8 lines · On Sun, 22 Apr 2012 23:23:23 +0200 "Rafael J. Wysocki" <[email protected]> wrote:
From: "Rafael J. Wysocki" <[email protected]>
To: Linux PM list <[email protected]>
Cc: LKML <[email protected]>, Magnus Damm <[email protected]>, [email protected], Matthew Garrett <[email protected]>, Greg KH <[email protected]>, Arve Hjønnevåg <[email protected]>, John Stultz <[email protected]>, Brian Swetland <[email protected]>, Neil Brown <[email protected]>, Alan Stern <[email protected]>, Dmitry Torokhov <[email protected]>, "Srivatsa S. Bhat" <[email protected]>
Subject: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Date: Sun, 22 Apr 2012 23:23:23 +0200
Sender: [email protected]
User-Agent: KMail/1.13.6 (Linux/3.4.0-rc3+; KDE/4.6.0;x86_64; ; )Quoted 7 lines
Quoted 5 lines
From: Rafael J. Wysocki <[email protected]>
Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Hi Rafael,
Hi,
Quoted 2 lines
just a few little issues below. Over all I think that if we have to have auto-sleep in the kernel, then this is a good way to do it.
Good, we seem to agree in principle, then. :-)
Quoted 27 lines
Quoted 27 lines
kernel/power/autosleep.c new file +27
-0+static void try_to_suspend (struct work_struct *work) +{ +unsigned int initial_count, final_count; + +if (!pm_get_wakeup_count (&initial_count, true)) + goto out; + +mutex_lock (&autosleep_lock); + +if (!pm_save_wakeup_count (initial_count)) { +mutex_unlock (&autosleep_lock); + goto out; + } + +if (autosleep_state ==PM_SUSPEND_ON ) { +mutex_unlock (&autosleep_lock); +return ; + } +if (autosleep_state >=PM_SUSPEND_MAX ) +hibernate (); +else +pm_suspend (autosleep_state); + +mutex_unlock (&autosleep_lock); + +if (!pm_get_wakeup_count (&final_count, false)) + goto out;Quoted 8 lines
kernel/power/autosleep.c
Quoted 3 lines
kernel/power/autosleep.c new file +3
-0+ +if (final_count == initial_count) +schedule_timeout (HZ /2 );This doesn't do what you seem to expect it to do.
You need to setcurrent->stateto something likeTASK_UNINTERRUPTIBLEbefore callingschedule_timeout, otherwise it is effectily a no-op.schedule_timeout_uninterruptible(), for example, will do this for you.Right. I obviously overlooked the missing state change.
Quoted 5 lines
However the value of this isn't clear to me, so a comment would probably be a good thing.
This continue presumably fires if we wake up without any wakeup sources being activated. In that case you want to delay for 500ms - presumably to avoid a tight suspend/resume loop if something goes wrong?Yes.
Quoted 5 lines
I have occasionally seen a stray/uninteresting interrupt wake from suspend immediately after entering suspend and the next attempt succeeds. Maybe this is a bug in some driver somewhere, but not a big one. I think I would rather in that case that we attempt to re-enter suspend immediately. Maybe after a few failed attempts it makes sense to back off.
Perhaps. We can adjust this particular thing later, I think.
Quoted 5 lines
The other question is: if we want to back-off, is 500ms really enough? What will be gained by, or could be achieved in, that time? An exponential back-off might be defensible, but I can't see the value of a 500ms fixed back-off.
However if you can, I'd love to see a comment in there explaining it.Sure.
Quoted 26 lines
Quoted 13 lines
kernel/power/autosleep.c new file +11
-0+ + out: +queue_up_suspend_work (); +} + + +int pm_autosleep_set_state (suspend_state_t state) +{ + +#ifndef CONFIG_HIBERNATION +if (state >=PM_SUSPEND_MAX )kernel/power/autosleep.c line 96
Quoted 10 lines
kernel/power/autosleep.c new file +10
-0+return -EINVAL ; +#endif + +__pm_stay_awake (autosleep_ws); + +mutex_lock (&autosleep_lock); + + autosleep_state = state; + +__pm_relax (autosleep_ws);I'm struggling to see the point of the
autosleep_ws.Quoted 7 lines
A suspend cannot actually happen while this code is running (can it?) because it will wait for the process to enter the freezer.
So the only effect of this is:
1/ cause the current auto-sleep cycle to abort and
2/ maybe add some accounting number is theautosleep_ws.
Is that right?
Which of these is needed?This is to solve a problem when user space attempts to echo "off" to
/sys/power/autosleepexactly whenpm_suspend()is initiated as a part of autosleep under the autosleep lock. In that case, ifautosleep_wsis not there, the process wanting to disable autosleep will have to wait for thepm_suspend()to complete (unless it holds a wakelock), which is suboptimal.Quoted 7 lines
Quoted 4 lines
I would imagine that any process writing to
/sys/power/autosleepwould be holding a wakelock, and if it didn't it should expect things to be racy...Am I missing something?
The assumption above is kind of optimistic in my opinion. That process very well may be a system administrator's bash, for example. :-)
If it is, then presumably the auto-sleep could kick in between any pair of keystrokes that the sysadmin types. Or between the final 'enter' and when the
write()system call begins. All thatautosleep_wsseems to provide is certainty that when thewrite()system call completes, autosleep will be fully disabled.
I don't think that is really worth anything.However, something did occur to me that I would like clarified. What happens if
try_to_suspend()gets theautosleep_lockjust beforewakeup_count_store(),state_store()orpm_autosleep_set_state()try to get it?
Forpm_autosleep_set_state()thetry_to_suspend()attempt will abort because it is holdingautosleep_ws, so it will drop the lock andpm_autosleep_set_state()will continue happily.
For the other two, what will happen (if there are no active wakesources and autosleep is enabled).
I'm guessing thattry_to_suspendwill try to freeze all the process, which sends a pseudo signal to all processes, so themutex_lock_interruptiblewill fail and the suspend will complete.
Then will the abortedwrite()system call be re-attempted?
Quoted 2 lines
If that is right, then here is a very clear need to
autosleep_ws: it prevents a deadlock.
Yes, I think that this is the case.
Quoted 8 lines
So it appears there is a very real need for
autosleep_wsthat even I can agree with. It seems subtle though and could usefully be documented:/* Note: it is only safe to
mutex_lock(&autosleep_lock)if awakeup_source
* is active, otherwise a deadlock withtry_to_suspend()is possible.
* Alternativelymutex_lock_interruptible()can be used. This will then fail
* if anauto_sleepcycle tries to freeze processes.
*/
I'll add the comment above if you don't mind. :-)
Quoted 4 lines
static
DEFINE_MUTEX(autosleep_lock);So:
Reviewed-by NeilBrown <[email protected]>
Thanks!
Rafael
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
In reply to Rafael J. WysockiOn Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...
Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.
This does not work. Writing something other than "off" disabled auto suspend for me.
...
kernel/power/main.c static ssize_t wakeup_count_store(struct · line 426
Quoted 10 lines
kernel/power/main.c +10
-0+static ssize_t autosleep_store (struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;
Did you mean:
if (state == PM_SUSPEND_ON && strcmp(buf, "off") && strcmp(buf, "off\n")) return -EINVAL;
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 19 lines · Arve Hjønnevåg
On Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.
Quoted 2 lines
This does not work. Writing something other than "off" disabled auto suspend for me.
My bad, sorry about that.
Quoted 13 lines
...
Review
Quoted 10 lines
(quoted diff) +10
-0+static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;Did you mean:
Quoted 3 lines
if (state == PM_SUSPEND_ON && strcmp(buf, "off") && strcmp(buf, "off\n")) return -EINVAL;
Yes, I did.
I'll add the following as an incremental patch on top of the series.
Thanks,
Rafael
--- kernel/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/power/main.c +1 -1
@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct koint error;if (state ==PM_SUSPEND_ON - && !(strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))) + &&strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))return -EINVAL ; error =pm_autosleep_set_state (state);
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 66 lines · Rafael J. Wysocki
Quoted 19 lines · Arve Hjønnevåg
On Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.Quoted 2 lines
This does not work. Writing something other than "off" disabled auto suspend for me.
My bad, sorry about that.
Quoted 16 lines
...
Review
Quoted 10 lines
(quoted diff) +10
-0+static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;Did you mean:
if (state ==PM_SUSPEND_ON
&&strcmp(buf, "off")&&strcmp(buf, "off\n")) return-EINVAL;Yes, I did.
I'll add the following as an incremental patch on top of the series.
Thanks,
Rafael---
kernel/power/main.c | 2 +-1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c +0
-0@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct ko int error;if (state ==
PM_SUSPEND_ON
- && !(strncmp(buf, "off", 3) &&strncmp(buf, "off\n", 4)))
+ &&strncmp(buf, "off", 3)&&strncmp(buf, "off\n", 4)) return-EINVAL;
Quoted 1 line
error =
pm_autosleep_set_state(state);
You still use strncmp here, so anything that starts with "off" is allowed (and the second strncmp is redundant).
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 67 lines · Arve Hjønnevåg
Quoted 66 lines · Rafael J. Wysocki
Quoted 19 lines · Arve Hjønnevåg
On Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.Quoted 2 lines
This does not work. Writing something other than "off" disabled auto suspend for me.
My bad, sorry about that.
Quoted 13 lines
...
Review
Quoted 10 lines
(quoted diff) +10
-0+static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;Did you mean:
Quoted 3 lines
if (state == PM_SUSPEND_ON && strcmp(buf, "off") && strcmp(buf, "off\n")) return -EINVAL;Yes, I did.
I'll add the following as an incremental patch on top of the series.
Thanks,
Rafael--- kernel/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c +1
-1@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct koint error;if (state ==PM_SUSPEND_ON - && !(strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))) + &&strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))return -EINVAL ;
Quoted 4 lines
kernel/power/main.c static ssize_t autosleep_store(struct ko · line 428
Quoted 1 line
kernel/power/main.c +0
-0error =pm_autosleep_set_state (state);You still use strncmp here, so anything that starts with "off" is allowed (and the second strncmp is redundant).
Good point. So I'm going to add the patch below after all. OK to add your sign-off to it?
Rafael
--- kernel/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/power/main.c +1 -1
@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct koint error;if (state ==PM_SUSPEND_ON - && !(strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))) + &&strcmp (buf,"off" ) &&strcmp (buf,"off\n" ))return -EINVAL ; error =pm_autosleep_set_state (state);
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 68 lines · Rafael J. Wysocki
Quoted 67 lines · Arve Hjønnevåg
Quoted 66 lines · Rafael J. Wysocki
Quoted 19 lines · Arve Hjønnevåg
On Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.Quoted 2 lines
This does not work. Writing something other than "off" disabled auto suspend for me.
My bad, sorry about that.
Quoted 16 lines
...
Review
Quoted 10 lines
(quoted diff) +10
-0+static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;Did you mean:
if (state ==PM_SUSPEND_ON
&&strcmp(buf, "off")&&strcmp(buf, "off\n")) return-EINVAL;Yes, I did.
I'll add the following as an incremental patch on top of the series.
Thanks,
Rafael---
kernel/power/main.c | 2 +-1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c +0
-0@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct ko int error;if (state ==
PM_SUSPEND_ON
- && !(strncmp(buf, "off", 3) &&strncmp(buf, "off\n", 4)))
+ &&strncmp(buf, "off", 3)&&strncmp(buf, "off\n", 4)) return-EINVAL;
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
error =
pm_autosleep_set_state(state);You still use strncmp here, so anything that starts with "off" is allowed (and the second strncmp is redundant).
Good point. So I'm going to add the patch below after all. OK to add your sign-off to it?
Yes.
Signature · 1 line
Arve Hjønnevåg
Re: [RFC][PATCH 6/8] PM / Sleep: Implement opportunistic sleep
Quoted 74 lines · Arve Hjønnevåg
Quoted 73 lines · Rafael J. Wysocki
Quoted 71 lines · Arve Hjønnevåg
Quoted 59 lines · Rafael J. Wysocki
Quoted 19 lines · Arve Hjønnevåg
On Thu, Apr 26, 2012 at 2:52 PM, Rafael J. Wysocki <[email protected]> wrote:
...Quoted 7 lines
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Sleep: Implement opportunistic sleep, v2Introduce a mechanism by which the kernel can trigger global transitions to a sleep state chosen by user space if there are no active wakeup sources.
Quoted 9 lines
It consists of a new sysfs attribute,
/sys/power/autosleep, that can be written one of the strings returned by reads from/sys/power/state, an ordered workqueue and a work item carrying out the "suspend" operations. If a string representing the system's sleep state is written to/sys/power/autosleep, the work item triggering transitions to that state is queued up and it requeues itself after every execution until user space writes "off" to/sys/power/autosleep.Quoted 2 lines
This does not work. Writing something other than "off" disabled auto suspend for me.
My bad, sorry about that.
Quoted 13 lines
...
Review
Quoted 10 lines
(quoted diff) +10
-0+static ssize_t autosleep_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + suspend_state_t state = decode_state(buf, n); + int error; + + if (state == PM_SUSPEND_ON + && !(strncmp(buf, "off", 3) && strncmp(buf, "off\n", 4))) + return -EINVAL;Did you mean:
Quoted 3 lines
if (state == PM_SUSPEND_ON && strcmp(buf, "off") && strcmp(buf, "off\n")) return -EINVAL;Yes, I did.
I'll add the following as an incremental patch on top of the series.
Thanks,
Rafael--- kernel/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)Index:
linux/kernel/power/main.c
===================================================================kernel/power/main.c static ssize_t autosleep_store(struct ko · line 428
Quoted 8 lines
kernel/power/main.c +1
-1@@ -422,7 +422,7 @@ static ssize_t autosleep_store(struct koint error;if (state ==PM_SUSPEND_ON - && !(strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))) + &&strncmp (buf,"off" ,3 ) &&strncmp (buf,"off\n" ,4 ))return -EINVAL ; error =pm_autosleep_set_state (state);You still use strncmp here, so anything that starts with "off" is allowed (and the second strncmp is redundant).
Quoted 5 lines
Quoted 3 lines
Good point. So I'm going to add the patch below after all. OK to add your sign-off to it?
Yes.
OK, done.
RFC PATCH 7/8 PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
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.
--- Documentation/ABI/testing/sysfs-devices-power | 11 ++++ drivers/base/power/sysfs.c | 24 ++++++++++ drivers/base/power/wakeup.c | 61 ++++++++++++++++++++++++-- include/linux/pm_wakeup.h | 4 + include/linux/suspend.h | 1 kernel/power/autosleep.c | 6 ++ 6 files changed, 102 insertions(+), 5 deletions(-)
include/linux/pm_wakeup.h +4 -0
@@ -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
drivers/base/power/wakeup.c +57 -4
116 lines not on this page · raw message
include/linux/suspend.h +1 -0
@@ -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 ) {
drivers/base/power/sysfs.c +24 -0
@@ -417,6 +417,27 @@ static ssize_t wakeup_last_time_show(str } 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, };
Documentation/ABI/testing/sysfs-devices-power +11 -0
@@ -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 <[email protected]> +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 <[email protected]>
kernel/power/autosleep.c +5 -1
@@ -91,8 +91,12 @@ int pm_autosleep_set_state(suspend_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 ;
RFC PATCH 8/8 PM / Sleep: Add user space interface for manipulating wakeup sources
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Android allows user space to manipulate wakelocks using two sysfs file located in /sys/power/, wake_lock and wake_unlock. Writing a wakelock name and optionally a timeout to the wake_lock file causes the wakelock whose name was written to be acquired (it is created before is necessary), optionally with the given timeout. Writing the name of a wakelock to wake_unlock causes that wakelock to be released.
Implement an analogous interface for user space using wakeup sources. Add the /sys/power/wake_lock and /sys/power/wake_unlock files allowing user space to create, activate and deactivate wakeup sources, such that writing a name and optionally a timeout to wake_lock causes the wakeup source of that name to be activated, optionally with the given timeout. If that wakeup source doesn't exist, it will be created and then activated. Writing a name to wake_unlock causes the wakeup source of that name, if there is one, to be deactivated. Wakeup sources created with the help of wake_lock that haven't been used for more than 5 minutes are garbage collected and destroyed. Moreover, there can be only WL_NUMBER_LIMIT wakeup sources created with the help of wake_lock present at a time.
The data type used to track wakeup sources created by user space is called "struct wakelock" to indicate the origins of this feature.
--- Documentation/ABI/testing/sysfs-power | 42 ++++++ drivers/base/power/wakeup.c | 1 kernel/power/Kconfig | 8 + kernel/power/Makefile | 1 kernel/power/main.c | 41 ++++++ kernel/power/power.h | 9 + kernel/power/wakelock.c | 218 ++++++++++++++++++++++++++++++++++ 7 files changed, 320 insertions(+)
kernel/power/main.c +41 -0
@@ -431,6 +431,43 @@ static ssize_t autosleep_store(struct kopower_attr (autosleep);#endif /* CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS +static ssize_t wake_lock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, true); +} + +static ssize_t wake_lock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_lock (buf); +return error ? error : n; +} + +power_attr (wake_lock); + +static ssize_t wake_unlock_show (struct kobject *kobj, +struct kobj_attribute *attr, +char *buf) +{ +return pm_show_wakelocks (buf, false); +} + +static ssize_t wake_unlock_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int error =pm_wake_unlock (buf); +return error ? error : n; +} + +power_attr (wake_unlock); + +#endif /* CONFIG_PM_WAKELOCKS */ #endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_TRACE
@@ -487,6 +524,10 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr,#endif +#ifdef CONFIG_PM_WAKELOCKS + &wake_lock_attr.attr, + &wake_unlock_attr.attr, +#endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif
kernel/power/power.h +9 -0
@@ -282,3 +282,12 @@ static inline void pm_autosleep_unlock(vstatic inline suspend_state_t pm_autosleep_state (void ) {return PM_SUSPEND_ON ; }#endif /* !CONFIG_PM_AUTOSLEEP */ + +#ifdef CONFIG_PM_WAKELOCKS + +/* kernel/power/wakelock.c */ +extern ssize_t pm_show_wakelocks (char *buf,bool show_active); +extern int pm_wake_lock (const char *buf); +extern int pm_wake_unlock (const char *buf); + +#endif /* !CONFIG_PM_WAKELOCKS */
kernel/power/Kconfig +8 -0
@@ -111,6 +111,14 @@ config PM_AUTOSLEEP Allow the kernel to trigger a system transition into a global sleep state automatically whenever there are no active wakeup sources. +config PM_WAKELOCKS + bool "User space wakeup sources interface" + depends on PM_SLEEP + default n + ---help--- + Allow user space to create, activate and deactivate wakeup source + objects with the help of a sysfs-based interface. + config PM_RUNTIME bool "Run-time PM core functionality" depends on !IA64_HP_SIM
kernel/power/wakelock.c new file +218 -0
218 lines not on this page · raw message
kernel/power/Makefile +1 -0
@@ -10,5 +10,6 @@ obj-$(CONFIG_PM_TEST_SUSPEND) += suspend obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o +obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
drivers/base/power/wakeup.c +1 -0
@@ -133,6 +133,7 @@ void wakeup_source_add(struct wakeup_souspin_lock_init (&ws->lock);setup_timer (&ws->timer, pm_wakeup_timer_fn, (unsigned long )ws); ws->active = false; + ws->last_time =ktime_get ();spin_lock_irq (&events_lock);list_add_rcu (&ws->entry, &wakeup_sources);
Documentation/ABI/testing/sysfs-power +42 -0
45 lines not on this page · raw message