aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-09-26 14:32:27 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-10-16 17:28:52 -0400
commit2aede851ddf08666f68ffc17be446420e9d2a056 (patch)
treef63a0477c9fe618cd374065bac4cb5f172aaf7db
parent8f88893c05f2f677f18f2ce5591b4bed5d4a7535 (diff)
PM / Hibernate: Freeze kernel threads after preallocating memory
There is a problem with the current ordering of hibernate code which leads to deadlocks in some filesystems' memory shrinkers. Namely, some filesystems use freezable kernel threads that are inactive when the hibernate memory preallocation is carried out. Those same filesystems use memory shrinkers that may be triggered by the hibernate memory preallocation. If those memory shrinkers wait for the frozen kernel threads, the hibernate process deadlocks (this happens with XFS, for one example). Apparently, it is not technically viable to redesign the filesystems in question to avoid the situation described above, so the only possible solution of this issue is to defer the freezing of kernel threads until the hibernate memory preallocation is done, which is implemented by this change. Unfortunately, this requires the memory preallocation to be done before the "prepare" stage of device freeze, so after this change the only way drivers can allocate additional memory for their freeze routines in a clean way is to use PM notifiers. Reported-by: Christoph <cr2005@u-club.de> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--Documentation/power/devices.txt4
-rw-r--r--include/linux/freezer.h4
-rw-r--r--kernel/power/hibernate.c12
-rw-r--r--kernel/power/power.h3
-rw-r--r--kernel/power/process.c30
5 files changed, 33 insertions, 20 deletions
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
index 29b7a9817f5..646a89e0c07 100644
--- a/Documentation/power/devices.txt
+++ b/Documentation/power/devices.txt
@@ -281,10 +281,6 @@ When the system goes into the standby or memory sleep state, the phases are:
281 time.) Unlike the other suspend-related phases, during the prepare 281 time.) Unlike the other suspend-related phases, during the prepare
282 phase the device tree is traversed top-down. 282 phase the device tree is traversed top-down.
283 283
284 In addition to that, if device drivers need to allocate additional
285 memory to be able to hadle device suspend correctly, that should be
286 done in the prepare phase.
287
288 After the prepare callback method returns, no new children may be 284 After the prepare callback method returns, no new children may be
289 registered below the device. The method may also prepare the device or 285 registered below the device. The method may also prepare the device or
290 driver in some way for the upcoming system power transition (for 286 driver in some way for the upcoming system power transition (for
diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index 1effc8b56b4..aa56cf31f7f 100644
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -49,6 +49,7 @@ extern int thaw_process(struct task_struct *p);
49 49
50extern void refrigerator(void); 50extern void refrigerator(void);
51extern int freeze_processes(void); 51extern int freeze_processes(void);
52extern int freeze_kernel_threads(void);
52extern void thaw_processes(void); 53extern void thaw_processes(void);
53 54
54static inline int try_to_freeze(void) 55static inline int try_to_freeze(void)
@@ -171,7 +172,8 @@ static inline void clear_freeze_flag(struct task_struct *p) {}
171static inline int thaw_process(struct task_struct *p) { return 1; } 172static inline int thaw_process(struct task_struct *p) { return 1; }
172 173
173static inline void refrigerator(void) {} 174static inline void refrigerator(void) {}
174static inline int freeze_processes(void) { BUG(); return 0; } 175static inline int freeze_processes(void) { return -ENOSYS; }
176static inline int freeze_kernel_threads(void) { return -ENOSYS; }
175static inline void thaw_processes(void) {} 177static inline void thaw_processes(void) {}
176 178
177static inline int try_to_freeze(void) { return 0; } 179static inline int try_to_freeze(void) { return 0; }
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 8f7b1db1ece..3a20466015f 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -334,13 +334,17 @@ int hibernation_snapshot(int platform_mode)
334 if (error) 334 if (error)
335 goto Close; 335 goto Close;
336 336
337 error = dpm_prepare(PMSG_FREEZE);
338 if (error)
339 goto Complete_devices;
340
341 /* Preallocate image memory before shutting down devices. */ 337 /* Preallocate image memory before shutting down devices. */
342 error = hibernate_preallocate_memory(); 338 error = hibernate_preallocate_memory();
343 if (error) 339 if (error)
340 goto Close;
341
342 error = freeze_kernel_threads();
343 if (error)
344 goto Close;
345
346 error = dpm_prepare(PMSG_FREEZE);
347 if (error)
344 goto Complete_devices; 348 goto Complete_devices;
345 349
346 suspend_console(); 350 suspend_console();
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 9a00a0a2628..e6206397ce6 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -228,7 +228,8 @@ extern int pm_test_level;
228#ifdef CONFIG_SUSPEND_FREEZER 228#ifdef CONFIG_SUSPEND_FREEZER
229static inline int suspend_freeze_processes(void) 229static inline int suspend_freeze_processes(void)
230{ 230{
231 return freeze_processes(); 231 int error = freeze_processes();
232 return error ? : freeze_kernel_threads();
232} 233}
233 234
234static inline void suspend_thaw_processes(void) 235static inline void suspend_thaw_processes(void)
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 0cf3a27a6c9..addbbe5531b 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -135,7 +135,7 @@ static int try_to_freeze_tasks(bool sig_only)
135} 135}
136 136
137/** 137/**
138 * freeze_processes - tell processes to enter the refrigerator 138 * freeze_processes - Signal user space processes to enter the refrigerator.
139 */ 139 */
140int freeze_processes(void) 140int freeze_processes(void)
141{ 141{
@@ -143,20 +143,30 @@ int freeze_processes(void)
143 143
144 printk("Freezing user space processes ... "); 144 printk("Freezing user space processes ... ");
145 error = try_to_freeze_tasks(true); 145 error = try_to_freeze_tasks(true);
146 if (error) 146 if (!error) {
147 goto Exit; 147 printk("done.");
148 printk("done.\n"); 148 oom_killer_disable();
149 }
150 printk("\n");
151 BUG_ON(in_atomic());
152
153 return error;
154}
155
156/**
157 * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator.
158 */
159int freeze_kernel_threads(void)
160{
161 int error;
149 162
150 printk("Freezing remaining freezable tasks ... "); 163 printk("Freezing remaining freezable tasks ... ");
151 error = try_to_freeze_tasks(false); 164 error = try_to_freeze_tasks(false);
152 if (error) 165 if (!error)
153 goto Exit; 166 printk("done.");
154 printk("done.");
155 167
156 oom_killer_disable();
157 Exit:
158 BUG_ON(in_atomic());
159 printk("\n"); 168 printk("\n");
169 BUG_ON(in_atomic());
160 170
161 return error; 171 return error;
162} 172}