diff options
| author | Tejun Heo <tj@kernel.org> | 2011-11-21 15:32:23 -0500 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2011-11-21 15:32:23 -0500 |
| commit | 8a32c441c1609f80e55df75422324a1151208f40 (patch) | |
| tree | 73884b06cc2db3ea155af9a88815bb5105a4473e | |
| parent | a0acae0e886d44bd5ce6d2f173c1ace0fcf0d9f6 (diff) | |
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>
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 15 | ||||
| -rw-r--r-- | fs/fs-writeback.c | 4 | ||||
| -rw-r--r-- | include/linux/freezer.h | 6 | ||||
| -rw-r--r-- | include/linux/kthread.h | 1 | ||||
| -rw-r--r-- | kernel/freezer.c | 6 | ||||
| -rw-r--r-- | kernel/kthread.c | 25 | ||||
| -rw-r--r-- | mm/backing-dev.c | 8 |
7 files changed, 42 insertions, 23 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7b828680b21d..4b11fc91fa7d 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -2456,8 +2456,9 @@ static int hotkey_kthread(void *data) | |||
| 2456 | u32 poll_mask, event_mask; | 2456 | u32 poll_mask, event_mask; |
| 2457 | unsigned int si, so; | 2457 | unsigned int si, so; |
| 2458 | unsigned long t; | 2458 | unsigned long t; |
| 2459 | unsigned int change_detector, must_reset; | 2459 | unsigned int change_detector; |
| 2460 | unsigned int poll_freq; | 2460 | unsigned int poll_freq; |
| 2461 | bool was_frozen; | ||
| 2461 | 2462 | ||
| 2462 | mutex_lock(&hotkey_thread_mutex); | 2463 | mutex_lock(&hotkey_thread_mutex); |
| 2463 | 2464 | ||
| @@ -2488,14 +2489,14 @@ static int hotkey_kthread(void *data) | |||
| 2488 | t = 100; /* should never happen... */ | 2489 | t = 100; /* should never happen... */ |
| 2489 | } | 2490 | } |
| 2490 | t = msleep_interruptible(t); | 2491 | t = msleep_interruptible(t); |
| 2491 | if (unlikely(kthread_should_stop())) | 2492 | if (unlikely(kthread_freezable_should_stop(&was_frozen))) |
| 2492 | break; | 2493 | break; |
| 2493 | must_reset = try_to_freeze(); | 2494 | |
| 2494 | if (t > 0 && !must_reset) | 2495 | if (t > 0 && !was_frozen) |
| 2495 | continue; | 2496 | continue; |
| 2496 | 2497 | ||
| 2497 | mutex_lock(&hotkey_thread_data_mutex); | 2498 | mutex_lock(&hotkey_thread_data_mutex); |
| 2498 | if (must_reset || hotkey_config_change != change_detector) { | 2499 | if (was_frozen || hotkey_config_change != change_detector) { |
| 2499 | /* forget old state on thaw or config change */ | 2500 | /* forget old state on thaw or config change */ |
| 2500 | si = so; | 2501 | si = so; |
| 2501 | t = 0; | 2502 | t = 0; |
| @@ -2528,10 +2529,6 @@ exit: | |||
| 2528 | static void hotkey_poll_stop_sync(void) | 2529 | static void hotkey_poll_stop_sync(void) |
| 2529 | { | 2530 | { |
| 2530 | if (tpacpi_hotkey_task) { | 2531 | if (tpacpi_hotkey_task) { |
| 2531 | if (frozen(tpacpi_hotkey_task) || | ||
| 2532 | freezing(tpacpi_hotkey_task)) | ||
| 2533 | thaw_process(tpacpi_hotkey_task); | ||
| 2534 | |||
| 2535 | kthread_stop(tpacpi_hotkey_task); | 2532 | kthread_stop(tpacpi_hotkey_task); |
| 2536 | tpacpi_hotkey_task = NULL; | 2533 | tpacpi_hotkey_task = NULL; |
| 2537 | mutex_lock(&hotkey_thread_mutex); | 2534 | mutex_lock(&hotkey_thread_mutex); |
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 73c3992b2bb4..271fde50f0ee 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c | |||
| @@ -947,7 +947,7 @@ int bdi_writeback_thread(void *data) | |||
| 947 | 947 | ||
| 948 | trace_writeback_thread_start(bdi); | 948 | trace_writeback_thread_start(bdi); |
| 949 | 949 | ||
| 950 | while (!kthread_should_stop()) { | 950 | while (!kthread_freezable_should_stop(NULL)) { |
| 951 | /* | 951 | /* |
| 952 | * Remove own delayed wake-up timer, since we are already awake | 952 | * Remove own delayed wake-up timer, since we are already awake |
| 953 | * and we'll take care of the preriodic write-back. | 953 | * and we'll take care of the preriodic write-back. |
| @@ -977,8 +977,6 @@ int bdi_writeback_thread(void *data) | |||
| 977 | */ | 977 | */ |
| 978 | schedule(); | 978 | schedule(); |
| 979 | } | 979 | } |
| 980 | |||
| 981 | try_to_freeze(); | ||
| 982 | } | 980 | } |
| 983 | 981 | ||
| 984 | /* Flush any work that raced with us exiting */ | 982 | /* Flush any work that raced with us exiting */ |
diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 7a9427e9fe47..d02b78448b0f 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h | |||
| @@ -47,7 +47,7 @@ static inline bool should_send_signal(struct task_struct *p) | |||
| 47 | /* Takes and releases task alloc lock using task_lock() */ | 47 | /* Takes and releases task alloc lock using task_lock() */ |
| 48 | extern int thaw_process(struct task_struct *p); | 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 | extern int freeze_processes(void); | 51 | extern int freeze_processes(void); |
| 52 | extern int freeze_kernel_threads(void); | 52 | extern int freeze_kernel_threads(void); |
| 53 | extern void thaw_processes(void); | 53 | extern void thaw_processes(void); |
| @@ -57,7 +57,7 @@ static inline bool try_to_freeze(void) | |||
| 57 | might_sleep(); | 57 | might_sleep(); |
| 58 | if (likely(!freezing(current))) | 58 | if (likely(!freezing(current))) |
| 59 | return false; | 59 | return false; |
| 60 | return __refrigerator(); | 60 | return __refrigerator(false); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | extern bool freeze_task(struct task_struct *p, bool sig_only); | 63 | extern bool freeze_task(struct task_struct *p, bool sig_only); |
| @@ -180,7 +180,7 @@ static inline void set_freeze_flag(struct task_struct *p) {} | |||
| 180 | static inline void clear_freeze_flag(struct task_struct *p) {} | 180 | static inline void clear_freeze_flag(struct task_struct *p) {} |
| 181 | static inline int thaw_process(struct task_struct *p) { return 1; } | 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 | static inline int freeze_processes(void) { return -ENOSYS; } | 184 | static inline int freeze_processes(void) { return -ENOSYS; } |
| 185 | static inline int freeze_kernel_threads(void) { return -ENOSYS; } | 185 | static inline int freeze_kernel_threads(void) { return -ENOSYS; } |
| 186 | static inline void thaw_processes(void) {} | 186 | static inline void thaw_processes(void) {} |
diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 5cac19b3a266..0714b24c0e45 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h | |||
| @@ -35,6 +35,7 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), | |||
| 35 | void kthread_bind(struct task_struct *k, unsigned int cpu); | 35 | void kthread_bind(struct task_struct *k, unsigned int cpu); |
| 36 | int kthread_stop(struct task_struct *k); | 36 | int kthread_stop(struct task_struct *k); |
| 37 | int kthread_should_stop(void); | 37 | int kthread_should_stop(void); |
| 38 | bool kthread_freezable_should_stop(bool *was_frozen); | ||
| 38 | void *kthread_data(struct task_struct *k); | 39 | void *kthread_data(struct task_struct *k); |
| 39 | 40 | ||
| 40 | int kthreadd(void *unused); | 41 | int kthreadd(void *unused); |
diff --git a/kernel/freezer.c b/kernel/freezer.c index 732f14f5944f..b83c30e9483a 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/export.h> | 9 | #include <linux/export.h> |
| 10 | #include <linux/syscalls.h> | 10 | #include <linux/syscalls.h> |
| 11 | #include <linux/freezer.h> | 11 | #include <linux/freezer.h> |
| 12 | #include <linux/kthread.h> | ||
| 12 | 13 | ||
| 13 | /* | 14 | /* |
| 14 | * freezing is complete, mark current process as frozen | 15 | * freezing is complete, mark current process as frozen |
| @@ -23,7 +24,7 @@ static inline void frozen_process(void) | |||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | /* Refrigerator is place where frozen processes are stored :-). */ | 26 | /* Refrigerator is place where frozen processes are stored :-). */ |
| 26 | bool __refrigerator(void) | 27 | bool __refrigerator(bool check_kthr_stop) |
| 27 | { | 28 | { |
| 28 | /* Hmm, should we be allowed to suspend when there are realtime | 29 | /* Hmm, should we be allowed to suspend when there are realtime |
| 29 | processes around? */ | 30 | processes around? */ |
| @@ -50,7 +51,8 @@ bool __refrigerator(void) | |||
| 50 | 51 | ||
| 51 | for (;;) { | 52 | for (;;) { |
| 52 | set_current_state(TASK_UNINTERRUPTIBLE); | 53 | set_current_state(TASK_UNINTERRUPTIBLE); |
| 53 | if (!frozen(current)) | 54 | if (!frozen(current) || |
| 55 | (check_kthr_stop && kthread_should_stop())) | ||
| 54 | break; | 56 | break; |
| 55 | was_frozen = true; | 57 | was_frozen = true; |
| 56 | schedule(); | 58 | schedule(); |
diff --git a/kernel/kthread.c b/kernel/kthread.c index b6d216a92639..1c36deaae2f1 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c | |||
| @@ -59,6 +59,31 @@ int kthread_should_stop(void) | |||
| 59 | EXPORT_SYMBOL(kthread_should_stop); | 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 | * kthread_data - return data value specified on kthread creation | 87 | * kthread_data - return data value specified on kthread creation |
| 63 | * @task: kthread task in question | 88 | * @task: kthread task in question |
| 64 | * | 89 | * |
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 71034f41a2ba..7ba8feae11b8 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
| @@ -600,14 +600,10 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) | |||
| 600 | 600 | ||
| 601 | /* | 601 | /* |
| 602 | * Finally, kill the kernel thread. We don't need to be RCU | 602 | * Finally, kill the kernel thread. We don't need to be RCU |
| 603 | * safe anymore, since the bdi is gone from visibility. Force | 603 | * safe anymore, since the bdi is gone from visibility. |
| 604 | * unfreeze of the thread before calling kthread_stop(), otherwise | ||
| 605 | * it would never exet if it is currently stuck in the refrigerator. | ||
| 606 | */ | 604 | */ |
| 607 | if (bdi->wb.task) { | 605 | if (bdi->wb.task) |
| 608 | thaw_process(bdi->wb.task); | ||
| 609 | kthread_stop(bdi->wb.task); | 606 | kthread_stop(bdi->wb.task); |
| 610 | } | ||
| 611 | } | 607 | } |
| 612 | 608 | ||
| 613 | /* | 609 | /* |
