aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-04-26 17:22:09 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-05-05 16:54:28 -0400
commit8a537ece3d946227e4afa81eae0e43fa47439c7d (patch)
tree86d7284bb036dba324b0bd539e7f4ba354581251
parenta351e9b9fc24e982ec2f0e76379a49826036da12 (diff)
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>
-rw-r--r--drivers/base/power/wakeup.c36
-rw-r--r--include/linux/pm_wakeup.h25
2 files changed, 39 insertions, 22 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 136854970489..84c41c98d6fc 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -512,12 +512,13 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws)
512/** 512/**
513 * wakup_source_activate - Mark given wakeup source as active. 513 * wakup_source_activate - Mark given wakeup source as active.
514 * @ws: Wakeup source to handle. 514 * @ws: Wakeup source to handle.
515 * @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
515 * 516 *
516 * Update the @ws' statistics and, if @ws has just been activated, notify the PM 517 * Update the @ws' statistics and, if @ws has just been activated, notify the PM
517 * core of the event by incrementing the counter of of wakeup events being 518 * core of the event by incrementing the counter of of wakeup events being
518 * processed. 519 * processed.
519 */ 520 */
520static void wakeup_source_activate(struct wakeup_source *ws) 521static void wakeup_source_activate(struct wakeup_source *ws, bool hard)
521{ 522{
522 unsigned int cec; 523 unsigned int cec;
523 524
@@ -525,11 +526,8 @@ static void wakeup_source_activate(struct wakeup_source *ws)
525 "unregistered wakeup source\n")) 526 "unregistered wakeup source\n"))
526 return; 527 return;
527 528
528 /* 529 if (hard)
529 * active wakeup source should bring the system 530 pm_system_wakeup();
530 * out of PM_SUSPEND_FREEZE state
531 */
532 freeze_wake();
533 531
534 ws->active = true; 532 ws->active = true;
535 ws->active_count++; 533 ws->active_count++;
@@ -546,8 +544,9 @@ static void wakeup_source_activate(struct wakeup_source *ws)
546/** 544/**
547 * wakeup_source_report_event - Report wakeup event using the given source. 545 * wakeup_source_report_event - Report wakeup event using the given source.
548 * @ws: Wakeup source to report the event for. 546 * @ws: Wakeup source to report the event for.
547 * @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
549 */ 548 */
550static void wakeup_source_report_event(struct wakeup_source *ws) 549static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
551{ 550{
552 ws->event_count++; 551 ws->event_count++;
553 /* This is racy, but the counter is approximate anyway. */ 552 /* This is racy, but the counter is approximate anyway. */
@@ -555,7 +554,7 @@ static void wakeup_source_report_event(struct wakeup_source *ws)
555 ws->wakeup_count++; 554 ws->wakeup_count++;
556 555
557 if (!ws->active) 556 if (!ws->active)
558 wakeup_source_activate(ws); 557 wakeup_source_activate(ws, hard);
559} 558}
560 559
561/** 560/**
@@ -573,7 +572,7 @@ void __pm_stay_awake(struct wakeup_source *ws)
573 572
574 spin_lock_irqsave(&ws->lock, flags); 573 spin_lock_irqsave(&ws->lock, flags);
575 574
576 wakeup_source_report_event(ws); 575 wakeup_source_report_event(ws, false);
577 del_timer(&ws->timer); 576 del_timer(&ws->timer);
578 ws->timer_expires = 0; 577 ws->timer_expires = 0;
579 578
@@ -739,9 +738,10 @@ static void pm_wakeup_timer_fn(unsigned long data)
739} 738}
740 739
741/** 740/**
742 * __pm_wakeup_event - Notify the PM core of a wakeup event. 741 * pm_wakeup_ws_event - Notify the PM core of a wakeup event.
743 * @ws: Wakeup source object associated with the event source. 742 * @ws: Wakeup source object associated with the event source.
744 * @msec: Anticipated event processing time (in milliseconds). 743 * @msec: Anticipated event processing time (in milliseconds).
744 * @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
745 * 745 *
746 * Notify the PM core of a wakeup event whose source is @ws that will take 746 * Notify the PM core of a wakeup event whose source is @ws that will take
747 * approximately @msec milliseconds to be processed by the kernel. If @ws is 747 * approximately @msec milliseconds to be processed by the kernel. If @ws is
@@ -750,7 +750,7 @@ static void pm_wakeup_timer_fn(unsigned long data)
750 * 750 *
751 * It is safe to call this function from interrupt context. 751 * It is safe to call this function from interrupt context.
752 */ 752 */
753void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) 753void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard)
754{ 754{
755 unsigned long flags; 755 unsigned long flags;
756 unsigned long expires; 756 unsigned long expires;
@@ -760,7 +760,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
760 760
761 spin_lock_irqsave(&ws->lock, flags); 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 if (!msec) { 765 if (!msec) {
766 wakeup_source_deactivate(ws); 766 wakeup_source_deactivate(ws);
@@ -779,17 +779,17 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
779 unlock: 779 unlock:
780 spin_unlock_irqrestore(&ws->lock, flags); 780 spin_unlock_irqrestore(&ws->lock, flags);
781} 781}
782EXPORT_SYMBOL_GPL(__pm_wakeup_event); 782EXPORT_SYMBOL_GPL(pm_wakeup_ws_event);
783
784 783
785/** 784/**
786 * pm_wakeup_event - Notify the PM core of a wakeup event. 785 * pm_wakeup_event - Notify the PM core of a wakeup event.
787 * @dev: Device the wakeup event is related to. 786 * @dev: Device the wakeup event is related to.
788 * @msec: Anticipated event processing time (in milliseconds). 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 */
792void pm_wakeup_event(struct device *dev, unsigned int msec) 792void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard)
793{ 793{
794 unsigned long flags; 794 unsigned long flags;
795 795
@@ -797,10 +797,10 @@ void pm_wakeup_event(struct device *dev, unsigned int msec)
797 return; 797 return;
798 798
799 spin_lock_irqsave(&dev->power.lock, flags); 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 spin_unlock_irqrestore(&dev->power.lock, flags); 801 spin_unlock_irqrestore(&dev->power.lock, flags);
802} 802}
803EXPORT_SYMBOL_GPL(pm_wakeup_event); 803EXPORT_SYMBOL_GPL(pm_wakeup_dev_event);
804 804
805void pm_print_active_wakeup_sources(void) 805void pm_print_active_wakeup_sources(void)
806{ 806{
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a3447932df1f..4c2cba7ec1d4 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -106,8 +106,8 @@ extern void __pm_stay_awake(struct wakeup_source *ws);
106extern void pm_stay_awake(struct device *dev); 106extern void pm_stay_awake(struct device *dev);
107extern void __pm_relax(struct wakeup_source *ws); 107extern void __pm_relax(struct wakeup_source *ws);
108extern void pm_relax(struct device *dev); 108extern void pm_relax(struct device *dev);
109extern void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec); 109extern void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard);
110extern void pm_wakeup_event(struct device *dev, unsigned int msec); 110extern void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard);
111 111
112#else /* !CONFIG_PM_SLEEP */ 112#else /* !CONFIG_PM_SLEEP */
113 113
@@ -182,9 +182,11 @@ static inline void __pm_relax(struct wakeup_source *ws) {}
182 182
183static inline void pm_relax(struct device *dev) {} 183static inline void pm_relax(struct device *dev) {}
184 184
185static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) {} 185static inline void pm_wakeup_ws_event(struct wakeup_source *ws,
186 unsigned int msec, bool hard) {}
186 187
187static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} 188static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec,
189 bool hard) {}
188 190
189#endif /* !CONFIG_PM_SLEEP */ 191#endif /* !CONFIG_PM_SLEEP */
190 192
@@ -201,4 +203,19 @@ static inline void wakeup_source_trash(struct wakeup_source *ws)
201 wakeup_source_drop(ws); 203 wakeup_source_drop(ws);
202} 204}
203 205
206static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
207{
208 return pm_wakeup_ws_event(ws, msec, false);
209}
210
211static inline void pm_wakeup_event(struct device *dev, unsigned int msec)
212{
213 return pm_wakeup_dev_event(dev, msec, false);
214}
215
216static inline void pm_wakeup_hard_event(struct device *dev)
217{
218 return pm_wakeup_dev_event(dev, 0, true);
219}
220
204#endif /* _LINUX_PM_WAKEUP_H */ 221#endif /* _LINUX_PM_WAKEUP_H */