diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-devices-power | 24 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 30 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 64 | ||||
-rw-r--r-- | include/linux/pm_wakeup.h | 11 |
4 files changed, 77 insertions, 52 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 840f7d64d483..b0a5d9a6135e 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power | |||
@@ -96,16 +96,26 @@ Description: | |||
96 | is read-only. If the device is not enabled to wake up the | 96 | is read-only. If the device is not enabled to wake up the |
97 | system from sleep states, this attribute is not present. | 97 | system from sleep states, this attribute is not present. |
98 | 98 | ||
99 | What: /sys/devices/.../power/wakeup_hit_count | 99 | What: /sys/devices/.../power/wakeup_abort_count |
100 | Date: September 2010 | 100 | Date: February 2012 |
101 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | 101 | Contact: Rafael J. Wysocki <rjw@sisk.pl> |
102 | Description: | 102 | Description: |
103 | The /sys/devices/.../wakeup_hit_count attribute contains the | 103 | The /sys/devices/.../wakeup_abort_count attribute contains the |
104 | number of times the processing of a wakeup event associated with | 104 | number of times the processing of a wakeup event associated with |
105 | the device might prevent the system from entering a sleep state. | 105 | the device might have aborted system transition into a sleep |
106 | This attribute is read-only. If the device is not enabled to | 106 | state in progress. This attribute is read-only. If the device |
107 | wake up the system from sleep states, this attribute is not | 107 | is not enabled to wake up the system from sleep states, this |
108 | present. | 108 | attribute is not present. |
109 | |||
110 | What: /sys/devices/.../power/wakeup_expire_count | ||
111 | Date: February 2012 | ||
112 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
113 | Description: | ||
114 | The /sys/devices/.../wakeup_expire_count attribute contains the | ||
115 | number of times a wakeup event associated with the device has | ||
116 | been reported with a timeout that expired. This attribute is | ||
117 | read-only. If the device is not enabled to wake up the system | ||
118 | from sleep states, this attribute is not present. | ||
109 | 119 | ||
110 | What: /sys/devices/.../power/wakeup_active | 120 | What: /sys/devices/.../power/wakeup_active |
111 | Date: September 2010 | 121 | Date: September 2010 |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 95c12f6cb5b9..13e40b9021b9 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -314,22 +314,41 @@ static ssize_t wakeup_active_count_show(struct device *dev, | |||
314 | 314 | ||
315 | static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); | 315 | static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); |
316 | 316 | ||
317 | static ssize_t wakeup_hit_count_show(struct device *dev, | 317 | static ssize_t wakeup_abort_count_show(struct device *dev, |
318 | struct device_attribute *attr, char *buf) | 318 | struct device_attribute *attr, |
319 | char *buf) | ||
320 | { | ||
321 | unsigned long count = 0; | ||
322 | bool enabled = false; | ||
323 | |||
324 | spin_lock_irq(&dev->power.lock); | ||
325 | if (dev->power.wakeup) { | ||
326 | count = dev->power.wakeup->wakeup_count; | ||
327 | enabled = true; | ||
328 | } | ||
329 | spin_unlock_irq(&dev->power.lock); | ||
330 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); | ||
331 | } | ||
332 | |||
333 | static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); | ||
334 | |||
335 | static ssize_t wakeup_expire_count_show(struct device *dev, | ||
336 | struct device_attribute *attr, | ||
337 | char *buf) | ||
319 | { | 338 | { |
320 | unsigned long count = 0; | 339 | unsigned long count = 0; |
321 | bool enabled = false; | 340 | bool enabled = false; |
322 | 341 | ||
323 | spin_lock_irq(&dev->power.lock); | 342 | spin_lock_irq(&dev->power.lock); |
324 | if (dev->power.wakeup) { | 343 | if (dev->power.wakeup) { |
325 | count = dev->power.wakeup->hit_count; | 344 | count = dev->power.wakeup->expire_count; |
326 | enabled = true; | 345 | enabled = true; |
327 | } | 346 | } |
328 | spin_unlock_irq(&dev->power.lock); | 347 | spin_unlock_irq(&dev->power.lock); |
329 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); | 348 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); |
330 | } | 349 | } |
331 | 350 | ||
332 | static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); | 351 | static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL); |
333 | 352 | ||
334 | static ssize_t wakeup_active_show(struct device *dev, | 353 | static ssize_t wakeup_active_show(struct device *dev, |
335 | struct device_attribute *attr, char *buf) | 354 | struct device_attribute *attr, char *buf) |
@@ -486,7 +505,8 @@ static struct attribute *wakeup_attrs[] = { | |||
486 | &dev_attr_wakeup.attr, | 505 | &dev_attr_wakeup.attr, |
487 | &dev_attr_wakeup_count.attr, | 506 | &dev_attr_wakeup_count.attr, |
488 | &dev_attr_wakeup_active_count.attr, | 507 | &dev_attr_wakeup_active_count.attr, |
489 | &dev_attr_wakeup_hit_count.attr, | 508 | &dev_attr_wakeup_abort_count.attr, |
509 | &dev_attr_wakeup_expire_count.attr, | ||
490 | &dev_attr_wakeup_active.attr, | 510 | &dev_attr_wakeup_active.attr, |
491 | &dev_attr_wakeup_total_time_ms.attr, | 511 | &dev_attr_wakeup_total_time_ms.attr, |
492 | &dev_attr_wakeup_max_time_ms.attr, | 512 | &dev_attr_wakeup_max_time_ms.attr, |
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 92f220d89d35..7a6eada4534d 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * If set, the suspend/hibernate code will abort transitions to a sleep state | 21 | * If set, the suspend/hibernate code will abort transitions to a sleep state |
22 | * if wakeup events are registered during or immediately before the transition. | 22 | * if wakeup events are registered during or immediately before the transition. |
23 | */ | 23 | */ |
24 | bool events_check_enabled; | 24 | bool events_check_enabled __read_mostly; |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * Combined counters of registered wakeup events and wakeup events in progress. | 27 | * Combined counters of registered wakeup events and wakeup events in progress. |
@@ -383,6 +383,21 @@ static void wakeup_source_activate(struct wakeup_source *ws) | |||
383 | } | 383 | } |
384 | 384 | ||
385 | /** | 385 | /** |
386 | * wakeup_source_report_event - Report wakeup event using the given source. | ||
387 | * @ws: Wakeup source to report the event for. | ||
388 | */ | ||
389 | static void wakeup_source_report_event(struct wakeup_source *ws) | ||
390 | { | ||
391 | ws->event_count++; | ||
392 | /* This is racy, but the counter is approximate anyway. */ | ||
393 | if (events_check_enabled) | ||
394 | ws->wakeup_count++; | ||
395 | |||
396 | if (!ws->active) | ||
397 | wakeup_source_activate(ws); | ||
398 | } | ||
399 | |||
400 | /** | ||
386 | * __pm_stay_awake - Notify the PM core of a wakeup event. | 401 | * __pm_stay_awake - Notify the PM core of a wakeup event. |
387 | * @ws: Wakeup source object associated with the source of the event. | 402 | * @ws: Wakeup source object associated with the source of the event. |
388 | * | 403 | * |
@@ -397,10 +412,7 @@ void __pm_stay_awake(struct wakeup_source *ws) | |||
397 | 412 | ||
398 | spin_lock_irqsave(&ws->lock, flags); | 413 | spin_lock_irqsave(&ws->lock, flags); |
399 | 414 | ||
400 | ws->event_count++; | 415 | wakeup_source_report_event(ws); |
401 | if (!ws->active) | ||
402 | wakeup_source_activate(ws); | ||
403 | |||
404 | del_timer(&ws->timer); | 416 | del_timer(&ws->timer); |
405 | ws->timer_expires = 0; | 417 | ws->timer_expires = 0; |
406 | 418 | ||
@@ -469,6 +481,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) | |||
469 | if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) | 481 | if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) |
470 | ws->max_time = duration; | 482 | ws->max_time = duration; |
471 | 483 | ||
484 | ws->last_time = now; | ||
472 | del_timer(&ws->timer); | 485 | del_timer(&ws->timer); |
473 | ws->timer_expires = 0; | 486 | ws->timer_expires = 0; |
474 | 487 | ||
@@ -541,8 +554,10 @@ static void pm_wakeup_timer_fn(unsigned long data) | |||
541 | spin_lock_irqsave(&ws->lock, flags); | 554 | spin_lock_irqsave(&ws->lock, flags); |
542 | 555 | ||
543 | if (ws->active && ws->timer_expires | 556 | if (ws->active && ws->timer_expires |
544 | && time_after_eq(jiffies, ws->timer_expires)) | 557 | && time_after_eq(jiffies, ws->timer_expires)) { |
545 | wakeup_source_deactivate(ws); | 558 | wakeup_source_deactivate(ws); |
559 | ws->expire_count++; | ||
560 | } | ||
546 | 561 | ||
547 | spin_unlock_irqrestore(&ws->lock, flags); | 562 | spin_unlock_irqrestore(&ws->lock, flags); |
548 | } | 563 | } |
@@ -569,9 +584,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) | |||
569 | 584 | ||
570 | spin_lock_irqsave(&ws->lock, flags); | 585 | spin_lock_irqsave(&ws->lock, flags); |
571 | 586 | ||
572 | ws->event_count++; | 587 | wakeup_source_report_event(ws); |
573 | if (!ws->active) | ||
574 | wakeup_source_activate(ws); | ||
575 | 588 | ||
576 | if (!msec) { | 589 | if (!msec) { |
577 | wakeup_source_deactivate(ws); | 590 | wakeup_source_deactivate(ws); |
@@ -614,24 +627,6 @@ void pm_wakeup_event(struct device *dev, unsigned int msec) | |||
614 | EXPORT_SYMBOL_GPL(pm_wakeup_event); | 627 | EXPORT_SYMBOL_GPL(pm_wakeup_event); |
615 | 628 | ||
616 | /** | 629 | /** |
617 | * pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources. | ||
618 | */ | ||
619 | static void pm_wakeup_update_hit_counts(void) | ||
620 | { | ||
621 | unsigned long flags; | ||
622 | struct wakeup_source *ws; | ||
623 | |||
624 | rcu_read_lock(); | ||
625 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) { | ||
626 | spin_lock_irqsave(&ws->lock, flags); | ||
627 | if (ws->active) | ||
628 | ws->hit_count++; | ||
629 | spin_unlock_irqrestore(&ws->lock, flags); | ||
630 | } | ||
631 | rcu_read_unlock(); | ||
632 | } | ||
633 | |||
634 | /** | ||
635 | * pm_wakeup_pending - Check if power transition in progress should be aborted. | 630 | * pm_wakeup_pending - Check if power transition in progress should be aborted. |
636 | * | 631 | * |
637 | * Compare the current number of registered wakeup events with its preserved | 632 | * Compare the current number of registered wakeup events with its preserved |
@@ -653,8 +648,6 @@ bool pm_wakeup_pending(void) | |||
653 | events_check_enabled = !ret; | 648 | events_check_enabled = !ret; |
654 | } | 649 | } |
655 | spin_unlock_irqrestore(&events_lock, flags); | 650 | spin_unlock_irqrestore(&events_lock, flags); |
656 | if (ret) | ||
657 | pm_wakeup_update_hit_counts(); | ||
658 | return ret; | 651 | return ret; |
659 | } | 652 | } |
660 | 653 | ||
@@ -680,7 +673,6 @@ bool pm_get_wakeup_count(unsigned int *count) | |||
680 | split_counters(&cnt, &inpr); | 673 | split_counters(&cnt, &inpr); |
681 | if (inpr == 0 || signal_pending(current)) | 674 | if (inpr == 0 || signal_pending(current)) |
682 | break; | 675 | break; |
683 | pm_wakeup_update_hit_counts(); | ||
684 | 676 | ||
685 | schedule(); | 677 | schedule(); |
686 | } | 678 | } |
@@ -713,8 +705,6 @@ bool pm_save_wakeup_count(unsigned int count) | |||
713 | events_check_enabled = true; | 705 | events_check_enabled = true; |
714 | } | 706 | } |
715 | spin_unlock_irq(&events_lock); | 707 | spin_unlock_irq(&events_lock); |
716 | if (!events_check_enabled) | ||
717 | pm_wakeup_update_hit_counts(); | ||
718 | return events_check_enabled; | 708 | return events_check_enabled; |
719 | } | 709 | } |
720 | 710 | ||
@@ -749,9 +739,10 @@ static int print_wakeup_source_stats(struct seq_file *m, | |||
749 | active_time = ktime_set(0, 0); | 739 | active_time = ktime_set(0, 0); |
750 | } | 740 | } |
751 | 741 | ||
752 | ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t" | 742 | ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" |
753 | "%lld\t\t%lld\t\t%lld\t\t%lld\n", | 743 | "%lld\t\t%lld\t\t%lld\t\t%lld\n", |
754 | ws->name, active_count, ws->event_count, ws->hit_count, | 744 | ws->name, active_count, ws->event_count, |
745 | ws->wakeup_count, ws->expire_count, | ||
755 | ktime_to_ms(active_time), ktime_to_ms(total_time), | 746 | ktime_to_ms(active_time), ktime_to_ms(total_time), |
756 | ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); | 747 | ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); |
757 | 748 | ||
@@ -768,8 +759,9 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) | |||
768 | { | 759 | { |
769 | struct wakeup_source *ws; | 760 | struct wakeup_source *ws; |
770 | 761 | ||
771 | seq_puts(m, "name\t\tactive_count\tevent_count\thit_count\t" | 762 | seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" |
772 | "active_since\ttotal_time\tmax_time\tlast_change\n"); | 763 | "expire_count\tactive_since\ttotal_time\tmax_time\t" |
764 | "last_change\n"); | ||
773 | 765 | ||
774 | rcu_read_lock(); | 766 | rcu_read_lock(); |
775 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) | 767 | list_for_each_entry_rcu(ws, &wakeup_sources, entry) |
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index d9f05113e5fb..5285317a612a 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h | |||
@@ -33,12 +33,14 @@ | |||
33 | * | 33 | * |
34 | * @total_time: Total time this wakeup source has been active. | 34 | * @total_time: Total time this wakeup source has been active. |
35 | * @max_time: Maximum time this wakeup source has been continuously active. | 35 | * @max_time: Maximum time this wakeup source has been continuously active. |
36 | * @last_time: Monotonic clock when the wakeup source's was activated last time. | 36 | * @last_time: Monotonic clock when the wakeup source's was touched last time. |
37 | * @event_count: Number of signaled wakeup events. | 37 | * @event_count: Number of signaled wakeup events. |
38 | * @active_count: Number of times the wakeup sorce was activated. | 38 | * @active_count: Number of times the wakeup sorce was activated. |
39 | * @relax_count: Number of times the wakeup sorce was deactivated. | 39 | * @relax_count: Number of times the wakeup sorce was deactivated. |
40 | * @hit_count: Number of times the wakeup sorce might abort system suspend. | 40 | * @expire_count: Number of times the wakeup source's timeout has expired. |
41 | * @wakeup_count: Number of times the wakeup source might abort suspend. | ||
41 | * @active: Status of the wakeup source. | 42 | * @active: Status of the wakeup source. |
43 | * @has_timeout: The wakeup source has been activated with a timeout. | ||
42 | */ | 44 | */ |
43 | struct wakeup_source { | 45 | struct wakeup_source { |
44 | const char *name; | 46 | const char *name; |
@@ -52,8 +54,9 @@ struct wakeup_source { | |||
52 | unsigned long event_count; | 54 | unsigned long event_count; |
53 | unsigned long active_count; | 55 | unsigned long active_count; |
54 | unsigned long relax_count; | 56 | unsigned long relax_count; |
55 | unsigned long hit_count; | 57 | unsigned long expire_count; |
56 | unsigned int active:1; | 58 | unsigned long wakeup_count; |
59 | bool active:1; | ||
57 | }; | 60 | }; |
58 | 61 | ||
59 | #ifdef CONFIG_PM_SLEEP | 62 | #ifdef CONFIG_PM_SLEEP |