]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
freezer: implement and use kthread_freezable_should_stop()
authorTejun Heo <tj@kernel.org>
Mon, 31 Oct 2011 22:30:30 +0000 (15:30 -0700)
committerTejun Heo <tj@kernel.org>
Mon, 31 Oct 2011 22:30:30 +0000 (15:30 -0700)
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>
drivers/platform/x86/thinkpad_acpi.c
fs/fs-writeback.c
include/linux/freezer.h
include/linux/kthread.h
kernel/freezer.c
kernel/kthread.c
mm/backing-dev.c

index 7b828680b21d84315d657fedac17a274142a2930..4b11fc91fa7d3366843b51315ddcbf861c0441a9 100644 (file)
@@ -2456,8 +2456,9 @@ static int hotkey_kthread(void *data)
        u32 poll_mask, event_mask;
        unsigned int si, so;
        unsigned long t;
-       unsigned int change_detector, must_reset;
+       unsigned int change_detector;
        unsigned int poll_freq;
+       bool was_frozen;
 
        mutex_lock(&hotkey_thread_mutex);
 
@@ -2488,14 +2489,14 @@ static int hotkey_kthread(void *data)
                                t = 100;        /* should never happen... */
                }
                t = msleep_interruptible(t);
-               if (unlikely(kthread_should_stop()))
+               if (unlikely(kthread_freezable_should_stop(&was_frozen)))
                        break;
-               must_reset = try_to_freeze();
-               if (t > 0 && !must_reset)
+
+               if (t > 0 && !was_frozen)
                        continue;
 
                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 */
                        si = so;
                        t = 0;
@@ -2528,10 +2529,6 @@ exit:
 static void hotkey_poll_stop_sync(void)
 {
        if (tpacpi_hotkey_task) {
-               if (frozen(tpacpi_hotkey_task) ||
-                   freezing(tpacpi_hotkey_task))
-                       thaw_process(tpacpi_hotkey_task);
-
                kthread_stop(tpacpi_hotkey_task);
                tpacpi_hotkey_task = NULL;
                mutex_lock(&hotkey_thread_mutex);
index 04cf3b91e5016a1f7e3ceef734c7d85574ec296d..b36edb8418ced39b9ca8b761942a90da56e44308 100644 (file)
@@ -922,7 +922,7 @@ int bdi_writeback_thread(void *data)
 
        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
                 * and we'll take care of the preriodic write-back.
@@ -952,8 +952,6 @@ int bdi_writeback_thread(void *data)
                         */
                        schedule();
                }
-
-               try_to_freeze();
        }
 
        /* Flush any work that raced with us exiting */
index 984b36717d8f948a57f1dfb5ca81661e9c621d68..4a73dd2689b76233da22b510413eeb5517061c24 100644 (file)
@@ -47,7 +47,7 @@ static inline bool should_send_signal(struct task_struct *p)
 /* Takes and releases task alloc lock using task_lock() */
 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_kernel_threads(void);
 extern void thaw_processes(void);
@@ -57,7 +57,7 @@ static inline bool try_to_freeze(void)
        might_sleep();
        if (likely(!freezing(current)))
                return false;
-       return __refrigerator();
+       return __refrigerator(false);
 }
 
 extern bool freeze_task(struct task_struct *p, bool sig_only);
@@ -185,7 +185,7 @@ static inline void set_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 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_kernel_threads(void) { return -ENOSYS; }
 static inline void thaw_processes(void) {}
index 1e923e5e88e81cdc83c8455eefb776eb1b5de588..6c1903da180f8d7dc1209c88d7f561d55ca3d067 100644 (file)
@@ -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);
 int kthread_stop(struct task_struct *k);
 int kthread_should_stop(void);
+bool kthread_freezable_should_stop(bool *was_frozen);
 void *kthread_data(struct task_struct *k);
 
 int kthreadd(void *unused);
index 8ab0bdb0d3c77a2086e5f6ca47851b3ef5e3ee3d..57ba75f9fb15574f9c6f8ecb1087ec3f2c44e572 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/syscalls.h>
 #include <linux/freezer.h>
+#include <linux/kthread.h>
 
 /*
  * freezing is complete, mark current process as frozen
@@ -23,7 +24,7 @@ static inline void frozen_process(void)
 }
 
 /* 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
           processes around? */
@@ -50,7 +51,8 @@ bool __refrigerator(void)
 
        for (;;) {
                set_current_state(TASK_UNINTERRUPTIBLE);
-               if (!frozen(current))
+               if (!frozen(current) ||
+                   (check_kthr_stop && kthread_should_stop()))
                        break;
                was_frozen = true;
                schedule();
index 4ba7cccb4994f24d6bc5965cda9b7f568d214f35..a6cbeea573d8d95284a7ccf18981305ce6fd918c 100644 (file)
@@ -58,6 +58,31 @@ int kthread_should_stop(void)
 }
 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
  * @task: kthread task in question
index a87da524a4a01ad288e10e9648593d8934adb4fc..b08460c10cbbb47561ba69617daf02eb1adf8bf7 100644 (file)
@@ -598,14 +598,10 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
 
        /*
         * Finally, kill the kernel thread. We don't need to be RCU
-        * safe anymore, since the bdi is gone from visibility. Force
-        * unfreeze of the thread before calling kthread_stop(), otherwise
-        * it would never exet if it is currently stuck in the refrigerator.
+        * safe anymore, since the bdi is gone from visibility.
         */
-       if (bdi->wb.task) {
-               thaw_process(bdi->wb.task);
+       if (bdi->wb.task)
                kthread_stop(bdi->wb.task);
-       }
 }
 
 /*