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

PM / wakeup: Integrate mechanism to abort transitions in progress

The system wakeup framework is not very consistent with respect to
the way it handles suspend-to-idle and generally wakeup events
occurring during transitions to system low-power states.

First off, system transitions in progress are aborted by the event
reporting helpers like pm_wakeup_event() only if the wakeup_count
sysfs attribute is in use (as documented), but there are cases in
which system-wide transitions should be aborted even if that is
not the case. For example, a wakeup signal from a designated
wakeup device during system-wide PM transition, it should cause
the transition to be aborted right away.

Moreover, there is a freeze_wake() call in wakeup_source_activate(),
but that really is only effective after suspend_freeze_state has
been set to FREEZE_STATE_ENTER by freeze_enter(). However, it
is very unlikely that wakeup_source_activate() will ever be called
at that time, as it could only be triggered by a IRQF_NO_SUSPEND
interrupt handler, so wakeups from suspend-to-idle don't really
occur in wakeup_source_activate().

At the same time there is a way to abort a system suspend in
progress (or wake up the system from suspend-to-idle), which is by
calling pm_system_wakeup(), but in turn that doesn't cause any
wakeup source objects to be activated, so it will not be covered
by wakeup source statistics and will not prevent the system from
suspending again immediately (in case autosleep is used, for
example). Consequently, if anyone wants to abort system transitions
in progress and allow the wakeup_count mechanism to work, they need
to use both pm_system_wakeup() and pm_wakeup_event(), say, at the
same time which is awkward.

For the above reasons, make it possible to trigger
pm_system_wakeup() from within wakeup_source_activate() and
provide a new pm_wakeup_hard_event() helper to do so within the
wakeup framework.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

+39 -22
+18 -18
drivers/base/power/wakeup.c
··· 512 512 /** 513 513 * wakup_source_activate - Mark given wakeup source as active. 514 514 * @ws: Wakeup source to handle. 515 + * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 515 516 * 516 517 * Update the @ws' statistics and, if @ws has just been activated, notify the PM 517 518 * core of the event by incrementing the counter of of wakeup events being 518 519 * processed. 519 520 */ 520 - static void wakeup_source_activate(struct wakeup_source *ws) 521 + static void wakeup_source_activate(struct wakeup_source *ws, bool hard) 521 522 { 522 523 unsigned int cec; 523 524 ··· 526 525 "unregistered wakeup source\n")) 527 526 return; 528 527 529 - /* 530 - * active wakeup source should bring the system 531 - * out of PM_SUSPEND_FREEZE state 532 - */ 533 - freeze_wake(); 528 + if (hard) 529 + pm_system_wakeup(); 534 530 535 531 ws->active = true; 536 532 ws->active_count++; ··· 544 546 /** 545 547 * wakeup_source_report_event - Report wakeup event using the given source. 546 548 * @ws: Wakeup source to report the event for. 549 + * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 547 550 */ 548 - static void wakeup_source_report_event(struct wakeup_source *ws) 551 + static void wakeup_source_report_event(struct wakeup_source *ws, bool hard) 549 552 { 550 553 ws->event_count++; 551 554 /* This is racy, but the counter is approximate anyway. */ ··· 554 555 ws->wakeup_count++; 555 556 556 557 if (!ws->active) 557 - wakeup_source_activate(ws); 558 + wakeup_source_activate(ws, hard); 558 559 } 559 560 560 561 /** ··· 572 573 573 574 spin_lock_irqsave(&ws->lock, flags); 574 575 575 - wakeup_source_report_event(ws); 576 + wakeup_source_report_event(ws, false); 576 577 del_timer(&ws->timer); 577 578 ws->timer_expires = 0; 578 579 ··· 738 739 } 739 740 740 741 /** 741 - * __pm_wakeup_event - Notify the PM core of a wakeup event. 742 + * pm_wakeup_ws_event - Notify the PM core of a wakeup event. 742 743 * @ws: Wakeup source object associated with the event source. 743 744 * @msec: Anticipated event processing time (in milliseconds). 745 + * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 744 746 * 745 747 * Notify the PM core of a wakeup event whose source is @ws that will take 746 748 * approximately @msec milliseconds to be processed by the kernel. If @ws is ··· 750 750 * 751 751 * It is safe to call this function from interrupt context. 752 752 */ 753 - void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) 753 + void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard) 754 754 { 755 755 unsigned long flags; 756 756 unsigned long expires; ··· 760 760 761 761 spin_lock_irqsave(&ws->lock, flags); 762 762 763 - wakeup_source_report_event(ws); 763 + wakeup_source_report_event(ws, hard); 764 764 765 765 if (!msec) { 766 766 wakeup_source_deactivate(ws); ··· 779 779 unlock: 780 780 spin_unlock_irqrestore(&ws->lock, flags); 781 781 } 782 - EXPORT_SYMBOL_GPL(__pm_wakeup_event); 783 - 782 + EXPORT_SYMBOL_GPL(pm_wakeup_ws_event); 784 783 785 784 /** 786 785 * pm_wakeup_event - Notify the PM core of a wakeup event. 787 786 * @dev: Device the wakeup event is related to. 788 787 * @msec: Anticipated event processing time (in milliseconds). 788 + * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 789 789 * 790 - * Call __pm_wakeup_event() for the @dev's wakeup source object. 790 + * Call pm_wakeup_ws_event() for the @dev's wakeup source object. 791 791 */ 792 - void pm_wakeup_event(struct device *dev, unsigned int msec) 792 + void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard) 793 793 { 794 794 unsigned long flags; 795 795 ··· 797 797 return; 798 798 799 799 spin_lock_irqsave(&dev->power.lock, flags); 800 - __pm_wakeup_event(dev->power.wakeup, msec); 800 + pm_wakeup_ws_event(dev->power.wakeup, msec, hard); 801 801 spin_unlock_irqrestore(&dev->power.lock, flags); 802 802 } 803 - EXPORT_SYMBOL_GPL(pm_wakeup_event); 803 + EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); 804 804 805 805 void pm_print_active_wakeup_sources(void) 806 806 {
+21 -4
include/linux/pm_wakeup.h
··· 106 106 extern void pm_stay_awake(struct device *dev); 107 107 extern void __pm_relax(struct wakeup_source *ws); 108 108 extern void pm_relax(struct device *dev); 109 - extern void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec); 110 - extern void pm_wakeup_event(struct device *dev, unsigned int msec); 109 + extern void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard); 110 + extern void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard); 111 111 112 112 #else /* !CONFIG_PM_SLEEP */ 113 113 ··· 182 182 183 183 static inline void pm_relax(struct device *dev) {} 184 184 185 - static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) {} 185 + static inline void pm_wakeup_ws_event(struct wakeup_source *ws, 186 + unsigned int msec, bool hard) {} 186 187 187 - static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} 188 + static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec, 189 + bool hard) {} 188 190 189 191 #endif /* !CONFIG_PM_SLEEP */ 190 192 ··· 201 199 { 202 200 wakeup_source_remove(ws); 203 201 wakeup_source_drop(ws); 202 + } 203 + 204 + static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) 205 + { 206 + return pm_wakeup_ws_event(ws, msec, false); 207 + } 208 + 209 + static inline void pm_wakeup_event(struct device *dev, unsigned int msec) 210 + { 211 + return pm_wakeup_dev_event(dev, msec, false); 212 + } 213 + 214 + static inline void pm_wakeup_hard_event(struct device *dev) 215 + { 216 + return pm_wakeup_dev_event(dev, 0, true); 204 217 } 205 218 206 219 #endif /* _LINUX_PM_WAKEUP_H */