Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+6 −9
Original line number Diff line number Diff line
@@ -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 @@ static int hotkey_kthread(void *data)
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);
+1 −3
Original line number Diff line number Diff line
@@ -947,7 +947,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.
@@ -977,8 +977,6 @@ int bdi_writeback_thread(void *data)
			 */
			schedule();
		}

		try_to_freeze();
	}

	/* Flush any work that raced with us exiting */
+3 −3
Original line number Diff line number Diff line
@@ -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);
@@ -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 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) {}
+1 −0
Original line number Diff line number Diff line
@@ -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);
+4 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/export.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();
Loading