Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources

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.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

+102 -5
+11
Documentation/ABI/testing/sysfs-devices-power
··· 158 158 not enabled to wake up the system from sleep states, this 159 159 attribute is not present. 160 160 161 + What: /sys/devices/.../power/wakeup_prevent_sleep_time_ms 162 + Date: February 2012 163 + Contact: Rafael J. Wysocki <rjw@sisk.pl> 164 + Description: 165 + The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute 166 + contains the total time the device has been preventing 167 + opportunistic transitions to sleep states from occuring. 168 + This attribute is read-only. If the device is not enabled to 169 + wake up the system from sleep states, this attribute is not 170 + present. 171 + 161 172 What: /sys/devices/.../power/autosuspend_delay_ms 162 173 Date: September 2010 163 174 Contact: Alan Stern <stern@rowland.harvard.edu>
+24
drivers/base/power/sysfs.c
··· 417 417 } 418 418 419 419 static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); 420 + 421 + #ifdef CONFIG_PM_AUTOSLEEP 422 + static ssize_t wakeup_prevent_sleep_time_show(struct device *dev, 423 + struct device_attribute *attr, 424 + char *buf) 425 + { 426 + s64 msec = 0; 427 + bool enabled = false; 428 + 429 + spin_lock_irq(&dev->power.lock); 430 + if (dev->power.wakeup) { 431 + msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time); 432 + enabled = true; 433 + } 434 + spin_unlock_irq(&dev->power.lock); 435 + return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); 436 + } 437 + 438 + static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444, 439 + wakeup_prevent_sleep_time_show, NULL); 440 + #endif /* CONFIG_PM_AUTOSLEEP */ 420 441 #endif /* CONFIG_PM_SLEEP */ 421 442 422 443 #ifdef CONFIG_PM_ADVANCED_DEBUG ··· 532 511 &dev_attr_wakeup_total_time_ms.attr, 533 512 &dev_attr_wakeup_max_time_ms.attr, 534 513 &dev_attr_wakeup_last_time_ms.attr, 514 + #ifdef CONFIG_PM_AUTOSLEEP 515 + &dev_attr_wakeup_prevent_sleep_time_ms.attr, 516 + #endif 535 517 #endif 536 518 NULL, 537 519 };
+57 -4
drivers/base/power/wakeup.c
··· 380 380 ws->active = true; 381 381 ws->active_count++; 382 382 ws->last_time = ktime_get(); 383 + if (ws->autosleep_enabled) 384 + ws->start_prevent_time = ws->last_time; 383 385 384 386 /* Increment the counter of events in progress. */ 385 387 cec = atomic_inc_return(&combined_event_count); ··· 451 449 } 452 450 EXPORT_SYMBOL_GPL(pm_stay_awake); 453 451 452 + #ifdef CONFIG_PM_AUTOSLEEP 453 + static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now) 454 + { 455 + ktime_t delta = ktime_sub(now, ws->start_prevent_time); 456 + ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta); 457 + } 458 + #else 459 + static inline void update_prevent_sleep_time(struct wakeup_source *ws, 460 + ktime_t now) {} 461 + #endif 462 + 454 463 /** 455 464 * wakup_source_deactivate - Mark given wakeup source as inactive. 456 465 * @ws: Wakeup source to handle. ··· 502 489 ws->last_time = now; 503 490 del_timer(&ws->timer); 504 491 ws->timer_expires = 0; 492 + 493 + if (ws->autosleep_enabled) 494 + update_prevent_sleep_time(ws, now); 505 495 506 496 /* 507 497 * Increment the counter of registered wakeup events and decrement the ··· 734 718 return events_check_enabled; 735 719 } 736 720 721 + #ifdef CONFIG_PM_AUTOSLEEP 722 + /** 723 + * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. 724 + * @enabled: Whether to set or to clear the autosleep_enabled flags. 725 + */ 726 + void pm_wakep_autosleep_enabled(bool set) 727 + { 728 + struct wakeup_source *ws; 729 + ktime_t now = ktime_get(); 730 + 731 + rcu_read_lock(); 732 + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { 733 + spin_lock_irq(&ws->lock); 734 + if (ws->autosleep_enabled != set) { 735 + ws->autosleep_enabled = set; 736 + if (ws->active) { 737 + if (set) 738 + ws->start_prevent_time = now; 739 + else 740 + update_prevent_sleep_time(ws, now); 741 + } 742 + } 743 + spin_unlock_irq(&ws->lock); 744 + } 745 + rcu_read_unlock(); 746 + } 747 + #endif /* CONFIG_PM_AUTOSLEEP */ 748 + 737 749 static struct dentry *wakeup_sources_stats_dentry; 738 750 739 751 /** ··· 777 733 ktime_t max_time; 778 734 unsigned long active_count; 779 735 ktime_t active_time; 736 + ktime_t prevent_sleep_time; 780 737 int ret; 781 738 782 739 spin_lock_irqsave(&ws->lock, flags); 783 740 784 741 total_time = ws->total_time; 785 742 max_time = ws->max_time; 743 + prevent_sleep_time = ws->prevent_sleep_time; 786 744 active_count = ws->active_count; 787 745 if (ws->active) { 788 - active_time = ktime_sub(ktime_get(), ws->last_time); 746 + ktime_t now = ktime_get(); 747 + 748 + active_time = ktime_sub(now, ws->last_time); 789 749 total_time = ktime_add(total_time, active_time); 790 750 if (active_time.tv64 > max_time.tv64) 791 751 max_time = active_time; 752 + 753 + if (ws->autosleep_enabled) 754 + prevent_sleep_time = ktime_add(prevent_sleep_time, 755 + ktime_sub(now, ws->start_prevent_time)); 792 756 } else { 793 757 active_time = ktime_set(0, 0); 794 758 } 795 759 796 760 ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t" 797 - "%lld\t\t%lld\t\t%lld\t\t%lld\n", 761 + "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", 798 762 ws->name, active_count, ws->event_count, 799 763 ws->wakeup_count, ws->expire_count, 800 764 ktime_to_ms(active_time), ktime_to_ms(total_time), 801 - ktime_to_ms(max_time), ktime_to_ms(ws->last_time)); 765 + ktime_to_ms(max_time), ktime_to_ms(ws->last_time), 766 + ktime_to_ms(prevent_sleep_time)); 802 767 803 768 spin_unlock_irqrestore(&ws->lock, flags); 804 769 ··· 824 771 825 772 seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" 826 773 "expire_count\tactive_since\ttotal_time\tmax_time\t" 827 - "last_change\n"); 774 + "last_change\tprevent_suspend_time\n"); 828 775 829 776 rcu_read_lock(); 830 777 list_for_each_entry_rcu(ws, &wakeup_sources, entry)
+4
include/linux/pm_wakeup.h
··· 34 34 * @total_time: Total time this wakeup source has been active. 35 35 * @max_time: Maximum time this wakeup source has been continuously active. 36 36 * @last_time: Monotonic clock when the wakeup source's was touched last time. 37 + * @prevent_sleep_time: Total time this source has been preventing autosleep. 37 38 * @event_count: Number of signaled wakeup events. 38 39 * @active_count: Number of times the wakeup sorce was activated. 39 40 * @relax_count: Number of times the wakeup sorce was deactivated. ··· 52 51 ktime_t total_time; 53 52 ktime_t max_time; 54 53 ktime_t last_time; 54 + ktime_t start_prevent_time; 55 + ktime_t prevent_sleep_time; 55 56 unsigned long event_count; 56 57 unsigned long active_count; 57 58 unsigned long relax_count; 58 59 unsigned long expire_count; 59 60 unsigned long wakeup_count; 60 61 bool active:1; 62 + bool autosleep_enabled:1; 61 63 }; 62 64 63 65 #ifdef CONFIG_PM_SLEEP
+1
include/linux/suspend.h
··· 358 358 extern bool pm_wakeup_pending(void); 359 359 extern bool pm_get_wakeup_count(unsigned int *count, bool block); 360 360 extern bool pm_save_wakeup_count(unsigned int count); 361 + extern void pm_wakep_autosleep_enabled(bool set); 361 362 362 363 static inline void lock_system_sleep(void) 363 364 {
+5 -1
kernel/power/autosleep.c
··· 101 101 102 102 __pm_relax(autosleep_ws); 103 103 104 - if (state > PM_SUSPEND_ON) 104 + if (state > PM_SUSPEND_ON) { 105 + pm_wakep_autosleep_enabled(true); 105 106 queue_up_suspend_work(); 107 + } else { 108 + pm_wakep_autosleep_enabled(false); 109 + } 106 110 107 111 mutex_unlock(&autosleep_lock); 108 112 return 0;