aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-07-19 04:47:36 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:42 -0400
commitb10d911749d37dccfa5873d2088aea3f074b9e45 (patch)
tree56bd0ccb2861d7ae562d4e48a737727628358b42
parentc2cf7d87d804c66e063829d5ca739053e901dc15 (diff)
PM: introduce hibernation and suspend notifiers
Make it possible to register hibernation and suspend notifiers, so that subsystems can perform hibernation-related or suspend-related operations that should not be carried out by device drivers' .suspend() and .resume() routines. [akpm@linux-foundation.org: build fixes] [akpm@linux-foundation.org: cleanups] Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Cc: Nigel Cunningham <nigel@nigel.suspend2.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/power/notifiers.txt50
-rw-r--r--include/linux/notifier.h6
-rw-r--r--include/linux/suspend.h48
-rw-r--r--kernel/power/disk.c16
-rw-r--r--kernel/power/main.c9
-rw-r--r--kernel/power/power.h10
-rw-r--r--kernel/power/user.c11
7 files changed, 138 insertions, 12 deletions
diff --git a/Documentation/power/notifiers.txt b/Documentation/power/notifiers.txt
new file mode 100644
index 000000000000..9293e4bc857c
--- /dev/null
+++ b/Documentation/power/notifiers.txt
@@ -0,0 +1,50 @@
1Suspend notifiers
2 (C) 2007 Rafael J. Wysocki <rjw@sisk.pl>, GPL
3
4There are some operations that device drivers may want to carry out in their
5.suspend() routines, but shouldn't, because they can cause the hibernation or
6suspend to fail. For example, a driver may want to allocate a substantial amount
7of memory (like 50 MB) in .suspend(), but that shouldn't be done after the
8swsusp's memory shrinker has run.
9
10Also, there may be some operations, that subsystems want to carry out before a
11hibernation/suspend or after a restore/resume, requiring the system to be fully
12functional, so the drivers' .suspend() and .resume() routines are not suitable
13for this purpose. For example, device drivers may want to upload firmware to
14their devices after a restore from a hibernation image, but they cannot do it by
15calling request_firmware() from their .resume() routines (user land processes
16are frozen at this point). The solution may be to load the firmware into
17memory before processes are frozen and upload it from there in the .resume()
18routine. Of course, a hibernation notifier may be used for this purpose.
19
20The subsystems that have such needs can register suspend notifiers that will be
21called upon the following events by the suspend core:
22
23PM_HIBERNATION_PREPARE The system is going to hibernate or suspend, tasks will
24 be frozen immediately.
25
26PM_POST_HIBERNATION The system memory state has been restored from a
27 hibernation image or an error occured during the
28 hibernation. Device drivers' .resume() callbacks have
29 been executed and tasks have been thawed.
30
31PM_SUSPEND_PREPARE The system is preparing for a suspend.
32
33PM_POST_SUSPEND The system has just resumed or an error occured during
34 the suspend. Device drivers' .resume() callbacks have
35 been executed and tasks have been thawed.
36
37It is generally assumed that whatever the notifiers do for
38PM_HIBERNATION_PREPARE, should be undone for PM_POST_HIBERNATION. Analogously,
39operations performed for PM_SUSPEND_PREPARE should be reversed for
40PM_POST_SUSPEND. Additionally, all of the notifiers are called for
41PM_POST_HIBERNATION if one of them fails for PM_HIBERNATION_PREPARE, and
42all of the notifiers are called for PM_POST_SUSPEND if one of them fails for
43PM_SUSPEND_PREPARE.
44
45The hibernation and suspend notifiers are called with pm_mutex held. They are
46defined in the usual way, but their last argument is meaningless (it is always
47NULL). To register and/or unregister a suspend notifier use the functions
48register_pm_notifier() and unregister_pm_notifier(), respectively, defined in
49include/linux/suspend.h . If you don't need to unregister the notifier, you can
50also use the pm_notifier() macro defined in include/linux/suspend.h .
diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index 576f2bb34cc8..be3f2bb6fcf3 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -212,5 +212,11 @@ extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
212#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) 212#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN)
213#define CPU_DYING_FROZEN (CPU_DYING | CPU_TASKS_FROZEN) 213#define CPU_DYING_FROZEN (CPU_DYING | CPU_TASKS_FROZEN)
214 214
215/* Hibernation and suspend events */
216#define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
217#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */
218#define PM_SUSPEND_PREPARE 0x0003 /* Going to suspend the system */
219#define PM_POST_SUSPEND 0x0004 /* Suspend finished */
220
215#endif /* __KERNEL__ */ 221#endif /* __KERNEL__ */
216#endif /* _LINUX_NOTIFIER_H */ 222#endif /* _LINUX_NOTIFIER_H */
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index d235c146da2b..e8e6da394c92 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -54,7 +54,8 @@ struct hibernation_ops {
54 void (*restore_cleanup)(void); 54 void (*restore_cleanup)(void);
55}; 55};
56 56
57#if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) 57#ifdef CONFIG_PM
58#ifdef CONFIG_SOFTWARE_SUSPEND
58/* kernel/power/snapshot.c */ 59/* kernel/power/snapshot.c */
59extern void __register_nosave_region(unsigned long b, unsigned long e, int km); 60extern void __register_nosave_region(unsigned long b, unsigned long e, int km);
60static inline void register_nosave_region(unsigned long b, unsigned long e) 61static inline void register_nosave_region(unsigned long b, unsigned long e)
@@ -72,16 +73,14 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
72 73
73extern void hibernation_set_ops(struct hibernation_ops *ops); 74extern void hibernation_set_ops(struct hibernation_ops *ops);
74extern int hibernate(void); 75extern int hibernate(void);
75#else 76#else /* CONFIG_SOFTWARE_SUSPEND */
76static inline void register_nosave_region(unsigned long b, unsigned long e) {}
77static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
78static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } 77static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
79static inline void swsusp_set_page_free(struct page *p) {} 78static inline void swsusp_set_page_free(struct page *p) {}
80static inline void swsusp_unset_page_free(struct page *p) {} 79static inline void swsusp_unset_page_free(struct page *p) {}
81 80
82static inline void hibernation_set_ops(struct hibernation_ops *ops) {} 81static inline void hibernation_set_ops(struct hibernation_ops *ops) {}
83static inline int hibernate(void) { return -ENOSYS; } 82static inline int hibernate(void) { return -ENOSYS; }
84#endif /* defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) */ 83#endif /* CONFIG_SOFTWARE_SUSPEND */
85 84
86void save_processor_state(void); 85void save_processor_state(void);
87void restore_processor_state(void); 86void restore_processor_state(void);
@@ -89,4 +88,43 @@ struct saved_context;
89void __save_processor_state(struct saved_context *ctxt); 88void __save_processor_state(struct saved_context *ctxt);
90void __restore_processor_state(struct saved_context *ctxt); 89void __restore_processor_state(struct saved_context *ctxt);
91 90
91/* kernel/power/main.c */
92extern struct blocking_notifier_head pm_chain_head;
93
94static inline int register_pm_notifier(struct notifier_block *nb)
95{
96 return blocking_notifier_chain_register(&pm_chain_head, nb);
97}
98
99static inline int unregister_pm_notifier(struct notifier_block *nb)
100{
101 return blocking_notifier_chain_unregister(&pm_chain_head, nb);
102}
103
104#define pm_notifier(fn, pri) { \
105 static struct notifier_block fn##_nb = \
106 { .notifier_call = fn, .priority = pri }; \
107 register_pm_notifier(&fn##_nb); \
108}
109#else /* CONFIG_PM */
110
111static inline int register_pm_notifier(struct notifier_block *nb)
112{
113 return 0;
114}
115
116static inline int unregister_pm_notifier(struct notifier_block *nb)
117{
118 return 0;
119}
120
121#define pm_notifier(fn, pri) do { (void)(fn); } while (0)
122#endif /* CONFIG_PM */
123
124#if !defined CONFIG_SOFTWARE_SUSPEND || !defined(CONFIG_PM)
125static inline void register_nosave_region(unsigned long b, unsigned long e)
126{
127}
128#endif
129
92#endif /* _LINUX_SWSUSP_H */ 130#endif /* _LINUX_SWSUSP_H */
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 885c653509c9..324ac0188ce1 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -281,9 +281,16 @@ int hibernate(void)
281{ 281{
282 int error; 282 int error;
283 283
284 mutex_lock(&pm_mutex);
284 /* The snapshot device should not be opened while we're running */ 285 /* The snapshot device should not be opened while we're running */
285 if (!atomic_add_unless(&snapshot_device_available, -1, 0)) 286 if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
286 return -EBUSY; 287 error = -EBUSY;
288 goto Unlock;
289 }
290
291 error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
292 if (error)
293 goto Exit;
287 294
288 /* Allocate memory management structures */ 295 /* Allocate memory management structures */
289 error = create_basic_memory_bitmaps(); 296 error = create_basic_memory_bitmaps();
@@ -294,7 +301,6 @@ int hibernate(void)
294 if (error) 301 if (error)
295 goto Finish; 302 goto Finish;
296 303
297 mutex_lock(&pm_mutex);
298 if (hibernation_mode == HIBERNATION_TESTPROC) { 304 if (hibernation_mode == HIBERNATION_TESTPROC) {
299 printk("swsusp debug: Waiting for 5 seconds.\n"); 305 printk("swsusp debug: Waiting for 5 seconds.\n");
300 mdelay(5000); 306 mdelay(5000);
@@ -316,12 +322,14 @@ int hibernate(void)
316 swsusp_free(); 322 swsusp_free();
317 } 323 }
318 Thaw: 324 Thaw:
319 mutex_unlock(&pm_mutex);
320 unprepare_processes(); 325 unprepare_processes();
321 Finish: 326 Finish:
322 free_basic_memory_bitmaps(); 327 free_basic_memory_bitmaps();
323 Exit: 328 Exit:
329 pm_notifier_call_chain(PM_POST_HIBERNATION);
324 atomic_inc(&snapshot_device_available); 330 atomic_inc(&snapshot_device_available);
331 Unlock:
332 mutex_unlock(&pm_mutex);
325 return error; 333 return error;
326} 334}
327 335
diff --git a/kernel/power/main.c b/kernel/power/main.c
index fc45ed22620f..4d26ad394fb3 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -23,6 +23,8 @@
23 23
24#include "power.h" 24#include "power.h"
25 25
26BLOCKING_NOTIFIER_HEAD(pm_chain_head);
27
26/*This is just an arbitrary number */ 28/*This is just an arbitrary number */
27#define FREE_PAGE_NUMBER (100) 29#define FREE_PAGE_NUMBER (100)
28 30
@@ -78,6 +80,10 @@ static int suspend_prepare(suspend_state_t state)
78 if (!pm_ops || !pm_ops->enter) 80 if (!pm_ops || !pm_ops->enter)
79 return -EPERM; 81 return -EPERM;
80 82
83 error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
84 if (error)
85 goto Finish;
86
81 pm_prepare_console(); 87 pm_prepare_console();
82 88
83 if (freeze_processes()) { 89 if (freeze_processes()) {
@@ -125,6 +131,8 @@ static int suspend_prepare(suspend_state_t state)
125 Thaw: 131 Thaw:
126 thaw_processes(); 132 thaw_processes();
127 pm_restore_console(); 133 pm_restore_console();
134 Finish:
135 pm_notifier_call_chain(PM_POST_SUSPEND);
128 return error; 136 return error;
129} 137}
130 138
@@ -176,6 +184,7 @@ static void suspend_finish(suspend_state_t state)
176 resume_console(); 184 resume_console();
177 thaw_processes(); 185 thaw_processes();
178 pm_restore_console(); 186 pm_restore_console();
187 pm_notifier_call_chain(PM_POST_SUSPEND);
179} 188}
180 189
181 190
diff --git a/kernel/power/power.h b/kernel/power/power.h
index eab3603b7caf..01c2275b15b2 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -173,5 +173,15 @@ extern void swsusp_close(void);
173extern int suspend_enter(suspend_state_t state); 173extern int suspend_enter(suspend_state_t state);
174 174
175struct timeval; 175struct timeval;
176/* kernel/power/swsusp.c */
176extern void swsusp_show_speed(struct timeval *, struct timeval *, 177extern void swsusp_show_speed(struct timeval *, struct timeval *,
177 unsigned int, char *); 178 unsigned int, char *);
179
180/* kernel/power/main.c */
181extern struct blocking_notifier_head pm_chain_head;
182
183static inline int pm_notifier_call_chain(unsigned long val)
184{
185 return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
186 == NOTIFY_BAD) ? -EINVAL : 0;
187}
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 1f24f30b951b..7f19afe01b48 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -151,10 +151,14 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
151 if (data->frozen) 151 if (data->frozen)
152 break; 152 break;
153 mutex_lock(&pm_mutex); 153 mutex_lock(&pm_mutex);
154 if (freeze_processes()) { 154 error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
155 thaw_processes(); 155 if (!error) {
156 error = -EBUSY; 156 error = freeze_processes();
157 if (error)
158 thaw_processes();
157 } 159 }
160 if (error)
161 pm_notifier_call_chain(PM_POST_HIBERNATION);
158 mutex_unlock(&pm_mutex); 162 mutex_unlock(&pm_mutex);
159 if (!error) 163 if (!error)
160 data->frozen = 1; 164 data->frozen = 1;
@@ -165,6 +169,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
165 break; 169 break;
166 mutex_lock(&pm_mutex); 170 mutex_lock(&pm_mutex);
167 thaw_processes(); 171 thaw_processes();
172 pm_notifier_call_chain(PM_POST_HIBERNATION);
168 mutex_unlock(&pm_mutex); 173 mutex_unlock(&pm_mutex);
169 data->frozen = 0; 174 data->frozen = 0;
170 break; 175 break;