Commit 8a32c441 authored by Tejun Heo's avatar Tejun Heo

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: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarHenrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Oleg Nesterov <oleg@redhat.com>
parent a0acae0e
...@@ -2456,8 +2456,9 @@ static int hotkey_kthread(void *data) ...@@ -2456,8 +2456,9 @@ static int hotkey_kthread(void *data)
u32 poll_mask, event_mask; u32 poll_mask, event_mask;
unsigned int si, so; unsigned int si, so;
unsigned long t; unsigned long t;
unsigned int change_detector, must_reset; unsigned int change_detector;
unsigned int poll_freq; unsigned int poll_freq;
bool was_frozen;
mutex_lock(&hotkey_thread_mutex); mutex_lock(&hotkey_thread_mutex);
...@@ -2488,14 +2489,14 @@ static int hotkey_kthread(void *data) ...@@ -2488,14 +2489,14 @@ static int hotkey_kthread(void *data)
t = 100; /* should never happen... */ t = 100; /* should never happen... */
} }
t = msleep_interruptible(t); t = msleep_interruptible(t);
if (unlikely(kthread_should_stop())) if (unlikely(kthread_freezable_should_stop(&was_frozen)))
break; break;
must_reset = try_to_freeze();
if (t > 0 && !must_reset) if (t > 0 && !was_frozen)
continue; continue;
mutex_lock(&hotkey_thread_data_mutex); mutex_lock(&hotkey_thread_data_mutex);
if (must_reset || hotkey_config_change != change_detector) { if (was_frozen || hotkey_config_change != change_detector) {
/* forget old state on thaw or config change */ /* forget old state on thaw or config change */
si = so; si = so;
t = 0; t = 0;
...@@ -2528,10 +2529,6 @@ static int hotkey_kthread(void *data) ...@@ -2528,10 +2529,6 @@ static int hotkey_kthread(void *data)
static void hotkey_poll_stop_sync(void) static void hotkey_poll_stop_sync(void)
{ {
if (tpacpi_hotkey_task) { if (tpacpi_hotkey_task) {
if (frozen(tpacpi_hotkey_task) ||
freezing(tpacpi_hotkey_task))
thaw_process(tpacpi_hotkey_task);
kthread_stop(tpacpi_hotkey_task); kthread_stop(tpacpi_hotkey_task);
tpacpi_hotkey_task = NULL; tpacpi_hotkey_task = NULL;
mutex_lock(&hotkey_thread_mutex); mutex_lock(&hotkey_thread_mutex);
......
...@@ -947,7 +947,7 @@ int bdi_writeback_thread(void *data) ...@@ -947,7 +947,7 @@ int bdi_writeback_thread(void *data)
trace_writeback_thread_start(bdi); trace_writeback_thread_start(bdi);
while (!kthread_should_stop()) { while (!kthread_freezable_should_stop(NULL)) {
/* /*
* Remove own delayed wake-up timer, since we are already awake * Remove own delayed wake-up timer, since we are already awake
* and we'll take care of the preriodic write-back. * and we'll take care of the preriodic write-back.
...@@ -977,8 +977,6 @@ int bdi_writeback_thread(void *data) ...@@ -977,8 +977,6 @@ int bdi_writeback_thread(void *data)
*/ */
schedule(); schedule();
} }
try_to_freeze();
} }
/* Flush any work that raced with us exiting */ /* Flush any work that raced with us exiting */
......
...@@ -47,7 +47,7 @@ static inline bool should_send_signal(struct task_struct *p) ...@@ -47,7 +47,7 @@ static inline bool should_send_signal(struct task_struct *p)
/* Takes and releases task alloc lock using task_lock() */ /* Takes and releases task alloc lock using task_lock() */
extern int thaw_process(struct task_struct *p); extern int thaw_process(struct task_struct *p);
extern bool __refrigerator(void); extern bool __refrigerator(bool check_kthr_stop);
extern int freeze_processes(void); extern int freeze_processes(void);
extern int freeze_kernel_threads(void); extern int freeze_kernel_threads(void);
extern void thaw_processes(void); extern void thaw_processes(void);
...@@ -57,7 +57,7 @@ static inline bool try_to_freeze(void) ...@@ -57,7 +57,7 @@ static inline bool try_to_freeze(void)
might_sleep(); might_sleep();
if (likely(!freezing(current))) if (likely(!freezing(current)))
return false; return false;
return __refrigerator(); return __refrigerator(false);
} }
extern bool freeze_task(struct task_struct *p, bool sig_only); 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,7 +180,7 @@ static inline void set_freeze_flag(struct task_struct *p) {}
static inline void clear_freeze_flag(struct task_struct *p) {} static inline void clear_freeze_flag(struct task_struct *p) {}
static inline int thaw_process(struct task_struct *p) { return 1; } static inline int thaw_process(struct task_struct *p) { return 1; }
static inline bool __refrigerator(void) { return false; } static inline bool __refrigerator(bool check_kthr_stop) { return false; }
static inline int freeze_processes(void) { return -ENOSYS; } static inline int freeze_processes(void) { return -ENOSYS; }
static inline int freeze_kernel_threads(void) { return -ENOSYS; } static inline int freeze_kernel_threads(void) { return -ENOSYS; }
static inline void thaw_processes(void) {} static inline void thaw_processes(void) {}
......
...@@ -35,6 +35,7 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), ...@@ -35,6 +35,7 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void kthread_bind(struct task_struct *k, unsigned int cpu); void kthread_bind(struct task_struct *k, unsigned int cpu);
int kthread_stop(struct task_struct *k); int kthread_stop(struct task_struct *k);
int kthread_should_stop(void); int kthread_should_stop(void);
bool kthread_freezable_should_stop(bool *was_frozen);
void *kthread_data(struct task_struct *k); void *kthread_data(struct task_struct *k);
int kthreadd(void *unused); int kthreadd(void *unused);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h>
/* /*
* freezing is complete, mark current process as frozen * freezing is complete, mark current process as frozen
...@@ -23,7 +24,7 @@ static inline void frozen_process(void) ...@@ -23,7 +24,7 @@ static inline void frozen_process(void)
} }
/* Refrigerator is place where frozen processes are stored :-). */ /* Refrigerator is place where frozen processes are stored :-). */
bool __refrigerator(void) bool __refrigerator(bool check_kthr_stop)
{ {
/* Hmm, should we be allowed to suspend when there are realtime /* Hmm, should we be allowed to suspend when there are realtime
processes around? */ processes around? */
...@@ -50,7 +51,8 @@ bool __refrigerator(void) ...@@ -50,7 +51,8 @@ bool __refrigerator(void)
for (;;) { for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
if (!frozen(current)) if (!frozen(current) ||
(check_kthr_stop && kthread_should_stop()))
break; break;
was_frozen = true; was_frozen = true;
schedule(); schedule();
......
...@@ -58,6 +58,31 @@ int kthread_should_stop(void) ...@@ -58,6 +58,31 @@ int kthread_should_stop(void)
} }
EXPORT_SYMBOL(kthread_should_stop); EXPORT_SYMBOL(kthread_should_stop);
/**
* kthread_freezable_should_stop - should this freezable kthread return now?
* @was_frozen: optional out parameter, indicates whether %current was frozen
*
* kthread_should_stop() for freezable kthreads, which will enter
* refrigerator if necessary. This function is safe from kthread_stop() /
* freezer deadlock and freezable kthreads should use this function instead
* of calling try_to_freeze() directly.
*/
bool kthread_freezable_should_stop(bool *was_frozen)
{
bool frozen = false;
might_sleep();
if (unlikely(freezing(current)))
frozen = __refrigerator(true);
if (was_frozen)
*was_frozen = frozen;
return kthread_should_stop();
}
EXPORT_SYMBOL_GPL(kthread_freezable_should_stop);
/** /**
* kthread_data - return data value specified on kthread creation * kthread_data - return data value specified on kthread creation
* @task: kthread task in question * @task: kthread task in question
......
...@@ -600,14 +600,10 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) ...@@ -600,14 +600,10 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
/* /*
* Finally, kill the kernel thread. We don't need to be RCU * Finally, kill the kernel thread. We don't need to be RCU
* safe anymore, since the bdi is gone from visibility. Force * safe anymore, since the bdi is gone from visibility.
* unfreeze of the thread before calling kthread_stop(), otherwise
* it would never exet if it is currently stuck in the refrigerator.
*/ */
if (bdi->wb.task) { if (bdi->wb.task)
thaw_process(bdi->wb.task);
kthread_stop(bdi->wb.task); kthread_stop(bdi->wb.task);
}
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment