diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-04-29 16:52:52 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-05-01 15:25:11 -0400 |
commit | 30e3ce6dcbe3fc29c343b17e768b07d4a795de21 (patch) | |
tree | 42af32f901aab0c9cbca0dc22f755f9fe3936d67 | |
parent | 60af1066913162c5dd13fad3b872a67b1eb7da0f (diff) |
PM / Sleep: Change wakeup source statistics to follow Android
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.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 |