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

PM / Hibernate: Fix s2disk regression related to freezing workqueues

Commit 2aede851ddf08666f68ffc17be446420e9d2a056

PM / Hibernate: Freeze kernel threads after preallocating memory

introduced a mechanism by which kernel threads were frozen after
the preallocation of hibernate image memory to avoid problems with
frozen kernel threads not responding to memory freeing requests.
However, it overlooked the s2disk code path in which the
SNAPSHOT_CREATE_IMAGE ioctl was run directly after SNAPSHOT_FREE,
which caused freeze_workqueues_begin() to BUG(), because it saw
that worqueues had been already frozen.

Although in principle this issue might be addressed by removing
the relevant BUG_ON() from freeze_workqueues_begin(), that would
reintroduce the very problem that commit 2aede851ddf08666f68ffc17be4
attempted to avoid into that particular code path. For this reason,
to fix the issue at hand, introduce thaw_kernel_threads() and make
the SNAPSHOT_FREE ioctl execute it.

Special thanks to Srivatsa S. Bhat for detailed analysis of the
problem.

Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Cc: stable@kernel.org

+30
+2
include/linux/freezer.h
··· 39 39 extern int freeze_processes(void); 40 40 extern int freeze_kernel_threads(void); 41 41 extern void thaw_processes(void); 42 + extern void thaw_kernel_threads(void); 42 43 43 44 static inline bool try_to_freeze(void) 44 45 { ··· 175 174 static inline int freeze_processes(void) { return -ENOSYS; } 176 175 static inline int freeze_kernel_threads(void) { return -ENOSYS; } 177 176 static inline void thaw_processes(void) {} 177 + static inline void thaw_kernel_threads(void) {} 178 178 179 179 static inline bool try_to_freeze(void) { return false; } 180 180
+19
kernel/power/process.c
··· 188 188 printk("done.\n"); 189 189 } 190 190 191 + void thaw_kernel_threads(void) 192 + { 193 + struct task_struct *g, *p; 194 + 195 + pm_nosig_freezing = false; 196 + printk("Restarting kernel threads ... "); 197 + 198 + thaw_workqueues(); 199 + 200 + read_lock(&tasklist_lock); 201 + do_each_thread(g, p) { 202 + if (p->flags & (PF_KTHREAD | PF_WQ_WORKER)) 203 + __thaw_task(p); 204 + } while_each_thread(g, p); 205 + read_unlock(&tasklist_lock); 206 + 207 + schedule(); 208 + printk("done.\n"); 209 + }
+9
kernel/power/user.c
··· 274 274 swsusp_free(); 275 275 memset(&data->handle, 0, sizeof(struct snapshot_handle)); 276 276 data->ready = 0; 277 + /* 278 + * It is necessary to thaw kernel threads here, because 279 + * SNAPSHOT_CREATE_IMAGE may be invoked directly after 280 + * SNAPSHOT_FREE. In that case, if kernel threads were not 281 + * thawed, the preallocation of memory carried out by 282 + * hibernation_snapshot() might run into problems (i.e. it 283 + * might fail or even deadlock). 284 + */ 285 + thaw_kernel_threads(); 277 286 break; 278 287 279 288 case SNAPSHOT_PREF_IMAGE_SIZE: