diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-02-17 17:39:33 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-03-04 17:08:14 -0500 |
commit | da863cddd831b0f4bf2d067f8b75254f1be94590 (patch) | |
tree | f217486238bec611a0c08b88ae64249ba60a544e /drivers/base/power/wakeup.c | |
parent | d94aff87826ee6aa43032f4c0263482913f4e2c8 (diff) |
PM / Sleep: Fix race conditions related to wakeup source timer function
If __pm_wakeup_event() has been used (with a nonzero timeout) to
report a wakeup event and then __pm_relax() immediately followed by
__pm_stay_awake() is called or __pm_wakeup_event() is called once
again for the same wakeup source object before its timer expires, the
timer function pm_wakeup_timer_fn() may still be run as a result of
the previous __pm_wakeup_event() call. In either of those cases it
may mistakenly deactivate the wakeup source that has just been
activated.
To prevent that from happening, make wakeup_source_deactivate()
clear the wakeup source's timer_expires field and make
pm_wakeup_timer_fn() check if timer_expires is different from zero
and if it's not in future before calling wakeup_source_deactivate()
(if timer_expires is 0, it means that the timer has just been
deleted and if timer_expires is in future, it means that the timer
has just been rescheduled to a different time).
Reported-by: Arve Hjønnevåg <arve@android.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base/power/wakeup.c')
-rw-r--r-- | drivers/base/power/wakeup.c | 16 |
1 files changed, 14 insertions, 2 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index d279f462d624..b38bb9afb719 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -433,6 +433,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) | |||
433 | ws->max_time = duration; | 433 | ws->max_time = duration; |
434 | 434 | ||
435 | del_timer(&ws->timer); | 435 | del_timer(&ws->timer); |
436 | ws->timer_expires = 0; | ||
436 | 437 | ||
437 | /* | 438 | /* |
438 | * Increment the counter of registered wakeup events and decrement the | 439 | * Increment the counter of registered wakeup events and decrement the |
@@ -487,11 +488,22 @@ EXPORT_SYMBOL_GPL(pm_relax); | |||
487 | * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. | 488 | * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. |
488 | * @data: Address of the wakeup source object associated with the event source. | 489 | * @data: Address of the wakeup source object associated with the event source. |
489 | * | 490 | * |
490 | * Call __pm_relax() for the wakeup source whose address is stored in @data. | 491 | * Call wakeup_source_deactivate() for the wakeup source whose address is stored |
492 | * in @data if it is currently active and its timer has not been canceled and | ||
493 | * the expiration time of the timer is not in future. | ||
491 | */ | 494 | */ |
492 | static void pm_wakeup_timer_fn(unsigned long data) | 495 | static void pm_wakeup_timer_fn(unsigned long data) |
493 | { | 496 | { |
494 | __pm_relax((struct wakeup_source *)data); | 497 | struct wakeup_source *ws = (struct wakeup_source *)data; |
498 | unsigned long flags; | ||
499 | |||
500 | spin_lock_irqsave(&ws->lock, flags); | ||
501 | |||
502 | if (ws->active && ws->timer_expires | ||
503 | && time_after_eq(jiffies, ws->timer_expires)) | ||
504 | wakeup_source_deactivate(ws); | ||
505 | |||
506 | spin_unlock_irqrestore(&ws->lock, flags); | ||
495 | } | 507 | } |
496 | 508 | ||
497 | /** | 509 | /** |