aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-07-05 16:43:53 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2010-07-18 19:58:48 -0400
commitc125e96f044427f38d106fab7bc5e4a5e6a18262 (patch)
treed9bbd40cc933fe522dbdf8ca2f7edf7b6f2f7ca4 /include
parentb14e033e17d0ea0ba12668d0d2f371cd31586994 (diff)
PM: Make it possible to avoid races between wakeup and system sleep
One of the arguments during the suspend blockers discussion was that the mainline kernel didn't contain any mechanisms making it possible to avoid races between wakeup and system suspend. Generally, there are two problems in that area. First, if a wakeup event occurs exactly when /sys/power/state is being written to, it may be delivered to user space right before the freezer kicks in, so the user space consumer of the event may not be able to process it before the system is suspended. Second, if a wakeup event occurs after user space has been frozen, it is not generally guaranteed that the ongoing transition of the system into a sleep state will be aborted. To address these issues introduce a new global sysfs attribute, /sys/power/wakeup_count, associated with a running counter of wakeup events and three helper functions, pm_stay_awake(), pm_relax(), and pm_wakeup_event(), that may be used by kernel subsystems to control the behavior of this attribute and to request the PM core to abort system transitions into a sleep state already in progress. The /sys/power/wakeup_count file may be read from or written to by user space. Reads will always succeed (unless interrupted by a signal) and return the current value of the wakeup events counter. Writes, however, will only succeed if the written number is equal to the current value of the wakeup events counter. If a write is successful, it will cause the kernel to save the current value of the wakeup events counter and to abort the subsequent system transition into a sleep state if any wakeup events are reported after the write has returned. [The assumption is that before writing to /sys/power/state user space will first read from /sys/power/wakeup_count. Next, user space consumers of wakeup events will have a chance to acknowledge or veto the upcoming system transition to a sleep state. Finally, if the transition is allowed to proceed, /sys/power/wakeup_count will be written to and if that succeeds, /sys/power/state will be written to as well. Still, if any wakeup events are reported to the PM core by kernel subsystems after that point, the transition will be aborted.] Additionally, put a wakeup events counter into struct dev_pm_info and make these per-device wakeup event counters available via sysfs, so that it's possible to check the activity of various wakeup event sources within the kernel. To illustrate how subsystems can use pm_wakeup_event(), make the low-level PCI runtime PM wakeup-handling code use it. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org> Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Acked-by: markgross <markgross@thegnar.org> Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Diffstat (limited to 'include')
-rw-r--r--include/linux/pm.h10
-rw-r--r--include/linux/suspend.h7
2 files changed, 17 insertions, 0 deletions
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 8e258c727971..b417fc46f3fc 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -457,6 +457,7 @@ struct dev_pm_info {
457#ifdef CONFIG_PM_SLEEP 457#ifdef CONFIG_PM_SLEEP
458 struct list_head entry; 458 struct list_head entry;
459 struct completion completion; 459 struct completion completion;
460 unsigned long wakeup_count;
460#endif 461#endif
461#ifdef CONFIG_PM_RUNTIME 462#ifdef CONFIG_PM_RUNTIME
462 struct timer_list suspend_timer; 463 struct timer_list suspend_timer;
@@ -552,6 +553,11 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
552 } while (0) 553 } while (0)
553 554
554extern void device_pm_wait_for_dev(struct device *sub, struct device *dev); 555extern void device_pm_wait_for_dev(struct device *sub, struct device *dev);
556
557/* drivers/base/power/wakeup.c */
558extern void pm_wakeup_event(struct device *dev, unsigned int msec);
559extern void pm_stay_awake(struct device *dev);
560extern void pm_relax(void);
555#else /* !CONFIG_PM_SLEEP */ 561#else /* !CONFIG_PM_SLEEP */
556 562
557#define device_pm_lock() do {} while (0) 563#define device_pm_lock() do {} while (0)
@@ -565,6 +571,10 @@ static inline int dpm_suspend_start(pm_message_t state)
565#define suspend_report_result(fn, ret) do {} while (0) 571#define suspend_report_result(fn, ret) do {} while (0)
566 572
567static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {} 573static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {}
574
575static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {}
576static inline void pm_stay_awake(struct device *dev) {}
577static inline void pm_relax(void) {}
568#endif /* !CONFIG_PM_SLEEP */ 578#endif /* !CONFIG_PM_SLEEP */
569 579
570/* How to reorder dpm_list after device_move() */ 580/* How to reorder dpm_list after device_move() */
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index bc7d6bb4cd8e..bf1bab7b059c 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -286,6 +286,13 @@ extern int unregister_pm_notifier(struct notifier_block *nb);
286 { .notifier_call = fn, .priority = pri }; \ 286 { .notifier_call = fn, .priority = pri }; \
287 register_pm_notifier(&fn##_nb); \ 287 register_pm_notifier(&fn##_nb); \
288} 288}
289
290/* drivers/base/power/wakeup.c */
291extern bool events_check_enabled;
292
293extern bool pm_check_wakeup_events(void);
294extern bool pm_get_wakeup_count(unsigned long *count);
295extern bool pm_save_wakeup_count(unsigned long count);
289#else /* !CONFIG_PM_SLEEP */ 296#else /* !CONFIG_PM_SLEEP */
290 297
291static inline int register_pm_notifier(struct notifier_block *nb) 298static inline int register_pm_notifier(struct notifier_block *nb)