diff options
Diffstat (limited to 'drivers/base/power/wakeup.c')
-rw-r--r-- | drivers/base/power/wakeup.c | 109 |
1 files changed, 76 insertions, 33 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 8ec406d8f548..4573c83df6dd 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -24,12 +24,26 @@ | |||
24 | */ | 24 | */ |
25 | bool events_check_enabled; | 25 | bool events_check_enabled; |
26 | 26 | ||
27 | /* The counter of registered wakeup events. */ | 27 | /* |
28 | static atomic_t event_count = ATOMIC_INIT(0); | 28 | * Combined counters of registered wakeup events and wakeup events in progress. |
29 | /* A preserved old value of event_count. */ | 29 | * They need to be modified together atomically, so it's better to use one |
30 | * atomic variable to hold them both. | ||
31 | */ | ||
32 | static atomic_t combined_event_count = ATOMIC_INIT(0); | ||
33 | |||
34 | #define IN_PROGRESS_BITS (sizeof(int) * 4) | ||
35 | #define MAX_IN_PROGRESS ((1 << IN_PROGRESS_BITS) - 1) | ||
36 | |||
37 | static void split_counters(unsigned int *cnt, unsigned int *inpr) | ||
38 | { | ||
39 | unsigned int comb = atomic_read(&combined_event_count); | ||
40 | |||
41 | *cnt = (comb >> IN_PROGRESS_BITS); | ||
42 | *inpr = comb & MAX_IN_PROGRESS; | ||
43 | } | ||
44 | |||
45 | /* A preserved old value of the events counter. */ | ||
30 | static unsigned int saved_count; | 46 | static unsigned int saved_count; |
31 | /* The counter of wakeup events being processed. */ | ||
32 | static atomic_t events_in_progress = ATOMIC_INIT(0); | ||
33 | 47 | ||
34 | static DEFINE_SPINLOCK(events_lock); | 48 | static DEFINE_SPINLOCK(events_lock); |
35 | 49 | ||
@@ -228,6 +242,35 @@ int device_wakeup_disable(struct device *dev) | |||
228 | EXPORT_SYMBOL_GPL(device_wakeup_disable); | 242 | EXPORT_SYMBOL_GPL(device_wakeup_disable); |
229 | 243 | ||
230 | /** | 244 | /** |
245 | * device_set_wakeup_capable - Set/reset device wakeup capability flag. | ||
246 | * @dev: Device to handle. | ||
247 | * @capable: Whether or not @dev is capable of waking up the system from sleep. | ||
248 | * | ||
249 | * If @capable is set, set the @dev's power.can_wakeup flag and add its | ||
250 | * wakeup-related attributes to sysfs. Otherwise, unset the @dev's | ||
251 | * power.can_wakeup flag and remove its wakeup-related attributes from sysfs. | ||
252 | * | ||
253 | * This function may sleep and it can't be called from any context where | ||
254 | * sleeping is not allowed. | ||
255 | */ | ||
256 | void device_set_wakeup_capable(struct device *dev, bool capable) | ||
257 | { | ||
258 | if (!!dev->power.can_wakeup == !!capable) | ||
259 | return; | ||
260 | |||
261 | if (device_is_registered(dev)) { | ||
262 | if (capable) { | ||
263 | if (wakeup_sysfs_add(dev)) | ||
264 | return; | ||
265 | } else { | ||
266 | wakeup_sysfs_remove(dev); | ||
267 | } | ||
268 | } | ||
269 | dev->power.can_wakeup = capable; | ||
270 | } | ||
271 | EXPORT_SYMBOL_GPL(device_set_wakeup_capable); | ||
272 | |||
273 | /** | ||
231 | * device_init_wakeup - Device wakeup initialization. | 274 | * device_init_wakeup - Device wakeup initialization. |
232 | * @dev: Device to handle. | 275 | * @dev: Device to handle. |
233 | * @enable: Whether or not to enable @dev as a wakeup device. | 276 | * @enable: Whether or not to enable @dev as a wakeup device. |
@@ -307,7 +350,8 @@ static void wakeup_source_activate(struct wakeup_source *ws) | |||
307 | ws->timer_expires = jiffies; | 350 | ws->timer_expires = jiffies; |
308 | ws->last_time = ktime_get(); | 351 | ws->last_time = ktime_get(); |
309 | 352 | ||
310 | atomic_inc(&events_in_progress); | 353 | /* Increment the counter of events in progress. */ |
354 | atomic_inc(&combined_event_count); | ||
311 | } | 355 | } |
312 | 356 | ||
313 | /** | 357 | /** |
@@ -394,14 +438,10 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) | |||
394 | del_timer(&ws->timer); | 438 | del_timer(&ws->timer); |
395 | 439 | ||
396 | /* | 440 | /* |
397 | * event_count has to be incremented before events_in_progress is | 441 | * Increment the counter of registered wakeup events and decrement the |
398 | * modified, so that the callers of pm_check_wakeup_events() and | 442 | * couter of wakeup events in progress simultaneously. |
399 | * pm_save_wakeup_count() don't see the old value of event_count and | ||
400 | * events_in_progress equal to zero at the same time. | ||
401 | */ | 443 | */ |
402 | atomic_inc(&event_count); | 444 | atomic_add(MAX_IN_PROGRESS, &combined_event_count); |
403 | smp_mb__before_atomic_dec(); | ||
404 | atomic_dec(&events_in_progress); | ||
405 | } | 445 | } |
406 | 446 | ||
407 | /** | 447 | /** |
@@ -556,8 +596,10 @@ bool pm_wakeup_pending(void) | |||
556 | 596 | ||
557 | spin_lock_irqsave(&events_lock, flags); | 597 | spin_lock_irqsave(&events_lock, flags); |
558 | if (events_check_enabled) { | 598 | if (events_check_enabled) { |
559 | ret = ((unsigned int)atomic_read(&event_count) != saved_count) | 599 | unsigned int cnt, inpr; |
560 | || atomic_read(&events_in_progress); | 600 | |
601 | split_counters(&cnt, &inpr); | ||
602 | ret = (cnt != saved_count || inpr > 0); | ||
561 | events_check_enabled = !ret; | 603 | events_check_enabled = !ret; |
562 | } | 604 | } |
563 | spin_unlock_irqrestore(&events_lock, flags); | 605 | spin_unlock_irqrestore(&events_lock, flags); |
@@ -573,25 +615,25 @@ bool pm_wakeup_pending(void) | |||
573 | * Store the number of registered wakeup events at the address in @count. Block | 615 | * Store the number of registered wakeup events at the address in @count. Block |
574 | * if the current number of wakeup events being processed is nonzero. | 616 | * if the current number of wakeup events being processed is nonzero. |
575 | * | 617 | * |
576 | * Return false if the wait for the number of wakeup events being processed to | 618 | * Return 'false' if the wait for the number of wakeup events being processed to |
577 | * drop down to zero has been interrupted by a signal (and the current number | 619 | * drop down to zero has been interrupted by a signal (and the current number |
578 | * of wakeup events being processed is still nonzero). Otherwise return true. | 620 | * of wakeup events being processed is still nonzero). Otherwise return 'true'. |
579 | */ | 621 | */ |
580 | bool pm_get_wakeup_count(unsigned int *count) | 622 | bool pm_get_wakeup_count(unsigned int *count) |
581 | { | 623 | { |
582 | bool ret; | 624 | unsigned int cnt, inpr; |
583 | |||
584 | if (capable(CAP_SYS_ADMIN)) | ||
585 | events_check_enabled = false; | ||
586 | 625 | ||
587 | while (atomic_read(&events_in_progress) && !signal_pending(current)) { | 626 | for (;;) { |
627 | split_counters(&cnt, &inpr); | ||
628 | if (inpr == 0 || signal_pending(current)) | ||
629 | break; | ||
588 | pm_wakeup_update_hit_counts(); | 630 | pm_wakeup_update_hit_counts(); |
589 | schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); | 631 | schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); |
590 | } | 632 | } |
591 | 633 | ||
592 | ret = !atomic_read(&events_in_progress); | 634 | split_counters(&cnt, &inpr); |
593 | *count = atomic_read(&event_count); | 635 | *count = cnt; |
594 | return ret; | 636 | return !inpr; |
595 | } | 637 | } |
596 | 638 | ||
597 | /** | 639 | /** |
@@ -600,24 +642,25 @@ bool pm_get_wakeup_count(unsigned int *count) | |||
600 | * | 642 | * |
601 | * If @count is equal to the current number of registered wakeup events and the | 643 | * If @count is equal to the current number of registered wakeup events and the |
602 | * current number of wakeup events being processed is zero, store @count as the | 644 | * current number of wakeup events being processed is zero, store @count as the |
603 | * old number of registered wakeup events to be used by pm_check_wakeup_events() | 645 | * old number of registered wakeup events for pm_check_wakeup_events(), enable |
604 | * and return true. Otherwise return false. | 646 | * wakeup events detection and return 'true'. Otherwise disable wakeup events |
647 | * detection and return 'false'. | ||
605 | */ | 648 | */ |
606 | bool pm_save_wakeup_count(unsigned int count) | 649 | bool pm_save_wakeup_count(unsigned int count) |
607 | { | 650 | { |
608 | bool ret = false; | 651 | unsigned int cnt, inpr; |
609 | 652 | ||
653 | events_check_enabled = false; | ||
610 | spin_lock_irq(&events_lock); | 654 | spin_lock_irq(&events_lock); |
611 | if (count == (unsigned int)atomic_read(&event_count) | 655 | split_counters(&cnt, &inpr); |
612 | && !atomic_read(&events_in_progress)) { | 656 | if (cnt == count && inpr == 0) { |
613 | saved_count = count; | 657 | saved_count = count; |
614 | events_check_enabled = true; | 658 | events_check_enabled = true; |
615 | ret = true; | ||
616 | } | 659 | } |
617 | spin_unlock_irq(&events_lock); | 660 | spin_unlock_irq(&events_lock); |
618 | if (!ret) | 661 | if (!events_check_enabled) |
619 | pm_wakeup_update_hit_counts(); | 662 | pm_wakeup_update_hit_counts(); |
620 | return ret; | 663 | return events_check_enabled; |
621 | } | 664 | } |
622 | 665 | ||
623 | static struct dentry *wakeup_sources_stats_dentry; | 666 | static struct dentry *wakeup_sources_stats_dentry; |