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

freezer: implement and use kthread_freezable_should_stop()

Writeback and thinkpad_acpi have been using thaw_process() to prevent
deadlock between the freezer and kthread_stop(); unfortunately, this
is inherently racy - nothing prevents freezing from happening between
thaw_process() and kthread_stop().

This patch implements kthread_freezable_should_stop() which enters
refrigerator if necessary but is guaranteed to return if
kthread_stop() is invoked. Both thaw_process() users are converted to
use the new function.

Note that this deadlock condition exists for many of freezable
kthreads. They need to be converted to use the new should_stop or
freezable workqueue.

Tested with synthetic test case.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>

Tejun Heo 8a32c441 a0acae0e

+42 -23
+6 -9
drivers/platform/x86/thinkpad_acpi.c
··· 2456 2456 u32 poll_mask, event_mask; 2457 2457 unsigned int si, so; 2458 2458 unsigned long t; 2459 - unsigned int change_detector, must_reset; 2459 + unsigned int change_detector; 2460 2460 unsigned int poll_freq; 2461 + bool was_frozen; 2461 2462 2462 2463 mutex_lock(&hotkey_thread_mutex); 2463 2464 ··· 2489 2488 t = 100; /* should never happen... */ 2490 2489 } 2491 2490 t = msleep_interruptible(t); 2492 - if (unlikely(kthread_should_stop())) 2491 + if (unlikely(kthread_freezable_should_stop(&was_frozen))) 2493 2492 break; 2494 - must_reset = try_to_freeze(); 2495 - if (t > 0 && !must_reset) 2493 + 2494 + if (t > 0 && !was_frozen) 2496 2495 continue; 2497 2496 2498 2497 mutex_lock(&hotkey_thread_data_mutex); 2499 - if (must_reset || hotkey_config_change != change_detector) { 2498 + if (was_frozen || hotkey_config_change != change_detector) { 2500 2499 /* forget old state on thaw or config change */ 2501 2500 si = so; 2502 2501 t = 0; ··· 2529 2528 static void hotkey_poll_stop_sync(void) 2530 2529 { 2531 2530 if (tpacpi_hotkey_task) { 2532 - if (frozen(tpacpi_hotkey_task) || 2533 - freezing(tpacpi_hotkey_task)) 2534 - thaw_process(tpacpi_hotkey_task); 2535 - 2536 2531 kthread_stop(tpacpi_hotkey_task); 2537 2532 tpacpi_hotkey_task = NULL; 2538 2533 mutex_lock(&hotkey_thread_mutex);
+1 -3
fs/fs-writeback.c
··· 947 947 948 948 trace_writeback_thread_start(bdi); 949 949 950 - while (!kthread_should_stop()) { 950 + while (!kthread_freezable_should_stop(NULL)) { 951 951 /* 952 952 * Remove own delayed wake-up timer, since we are already awake 953 953 * and we'll take care of the preriodic write-back. ··· 977 977 */ 978 978 schedule(); 979 979 } 980 - 981 - try_to_freeze(); 982 980 } 983 981 984 982 /* Flush any work that raced with us exiting */
+3 -3
include/linux/freezer.h
··· 47 47 /* Takes and releases task alloc lock using task_lock() */ 48 48 extern int thaw_process(struct task_struct *p); 49 49 50 - extern bool __refrigerator(void); 50 + extern bool __refrigerator(bool check_kthr_stop); 51 51 extern int freeze_processes(void); 52 52 extern int freeze_kernel_threads(void); 53 53 extern void thaw_processes(void); ··· 57 57 might_sleep(); 58 58 if (likely(!freezing(current))) 59 59 return false; 60 - return __refrigerator(); 60 + return __refrigerator(false); 61 61 } 62 62 63 63 extern bool freeze_task(struct task_struct *p, bool sig_only); ··· 180 180 static inline void clear_freeze_flag(struct task_struct *p) {} 181 181 static inline int thaw_process(struct task_struct *p) { return 1; } 182 182 183 - static inline bool __refrigerator(void) { return false; } 183 + static inline bool __refrigerator(bool check_kthr_stop) { return false; } 184 184 static inline int freeze_processes(void) { return -ENOSYS; } 185 185 static inline int freeze_kernel_threads(void) { return -ENOSYS; } 186 186 static inline void thaw_processes(void) {}
+1
include/linux/kthread.h
··· 35 35 void kthread_bind(struct task_struct *k, unsigned int cpu); 36 36 int kthread_stop(struct task_struct *k); 37 37 int kthread_should_stop(void); 38 + bool kthread_freezable_should_stop(bool *was_frozen); 38 39 void *kthread_data(struct task_struct *k); 39 40 40 41 int kthreadd(void *unused);
+4 -2
kernel/freezer.c
··· 9 9 #include <linux/export.h> 10 10 #include <linux/syscalls.h> 11 11 #include <linux/freezer.h> 12 + #include <linux/kthread.h> 12 13 13 14 /* 14 15 * freezing is complete, mark current process as frozen ··· 24 23 } 25 24 26 25 /* Refrigerator is place where frozen processes are stored :-). */ 27 - bool __refrigerator(void) 26 + bool __refrigerator(bool check_kthr_stop) 28 27 { 29 28 /* Hmm, should we be allowed to suspend when there are realtime 30 29 processes around? */ ··· 51 50 52 51 for (;;) { 53 52 set_current_state(TASK_UNINTERRUPTIBLE); 54 - if (!frozen(current)) 53 + if (!frozen(current) || 54 + (check_kthr_stop && kthread_should_stop())) 55 55 break; 56 56 was_frozen = true; 57 57 schedule();
+25
kernel/kthread.c
··· 59 59 EXPORT_SYMBOL(kthread_should_stop); 60 60 61 61 /** 62 + * kthread_freezable_should_stop - should this freezable kthread return now? 63 + * @was_frozen: optional out parameter, indicates whether %current was frozen 64 + * 65 + * kthread_should_stop() for freezable kthreads, which will enter 66 + * refrigerator if necessary. This function is safe from kthread_stop() / 67 + * freezer deadlock and freezable kthreads should use this function instead 68 + * of calling try_to_freeze() directly. 69 + */ 70 + bool kthread_freezable_should_stop(bool *was_frozen) 71 + { 72 + bool frozen = false; 73 + 74 + might_sleep(); 75 + 76 + if (unlikely(freezing(current))) 77 + frozen = __refrigerator(true); 78 + 79 + if (was_frozen) 80 + *was_frozen = frozen; 81 + 82 + return kthread_should_stop(); 83 + } 84 + EXPORT_SYMBOL_GPL(kthread_freezable_should_stop); 85 + 86 + /** 62 87 * kthread_data - return data value specified on kthread creation 63 88 * @task: kthread task in question 64 89 *
+2 -6
mm/backing-dev.c
··· 600 600 601 601 /* 602 602 * Finally, kill the kernel thread. We don't need to be RCU 603 - * safe anymore, since the bdi is gone from visibility. Force 604 - * unfreeze of the thread before calling kthread_stop(), otherwise 605 - * it would never exet if it is currently stuck in the refrigerator. 603 + * safe anymore, since the bdi is gone from visibility. 606 604 */ 607 - if (bdi->wb.task) { 608 - thaw_process(bdi->wb.task); 605 + if (bdi->wb.task) 609 606 kthread_stop(bdi->wb.task); 610 - } 611 607 } 612 608 613 609 /*