Commit ebb12db5 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Andi Kleen

Freezer: Introduce PF_FREEZER_NOSIG

The freezer currently attempts to distinguish kernel threads from
user space tasks by checking if their mm pointer is unset and it
does not send fake signals to kernel threads.  However, there are
kernel threads, mostly related to networking, that behave like
user space tasks and may want to be sent a fake signal to be frozen.

Introduce the new process flag PF_FREEZER_NOSIG that will be set
by default for all kernel threads and make the freezer only send
fake signals to the tasks having PF_FREEZER_NOSIG unset.  Provide
the set_freezable_with_signal() function to be called by the kernel
threads that want to be sent a fake signal for freezing.

This patch should not change the freezer's observable behavior.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
Acked-by: default avatarPavel Machek <pavel@suse.cz>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent d20a4dca
...@@ -127,6 +127,15 @@ static inline void set_freezable(void) ...@@ -127,6 +127,15 @@ static inline void set_freezable(void)
current->flags &= ~PF_NOFREEZE; current->flags &= ~PF_NOFREEZE;
} }
/*
* Tell the freezer that the current task should be frozen by it and that it
* should send a fake signal to the task to freeze it.
*/
static inline void set_freezable_with_signal(void)
{
current->flags &= ~(PF_NOFREEZE | PF_FREEZER_NOSIG);
}
/* /*
* Freezer-friendly wrappers around wait_event_interruptible() and * Freezer-friendly wrappers around wait_event_interruptible() and
* wait_event_interruptible_timeout(), originally defined in <linux/wait.h> * wait_event_interruptible_timeout(), originally defined in <linux/wait.h>
...@@ -174,6 +183,7 @@ static inline void freezer_do_not_count(void) {} ...@@ -174,6 +183,7 @@ static inline void freezer_do_not_count(void) {}
static inline void freezer_count(void) {} static inline void freezer_count(void) {}
static inline int freezer_should_skip(struct task_struct *p) { return 0; } static inline int freezer_should_skip(struct task_struct *p) { return 0; }
static inline void set_freezable(void) {} static inline void set_freezable(void) {}
static inline void set_freezable_with_signal(void) {}
#define wait_event_freezable(wq, condition) \ #define wait_event_freezable(wq, condition) \
wait_event_interruptible(wq, condition) wait_event_interruptible(wq, condition)
......
...@@ -1494,6 +1494,7 @@ static inline void put_task_struct(struct task_struct *t) ...@@ -1494,6 +1494,7 @@ static inline void put_task_struct(struct task_struct *t)
#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ #define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */ #define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */
#define PF_FREEZER_NOSIG 0x80000000 /* Freezer won't send signals to it */
/* /*
* Only the _current_ task can read/write to tsk->flags, but other * Only the _current_ task can read/write to tsk->flags, but other
......
...@@ -235,7 +235,7 @@ int kthreadd(void *unused) ...@@ -235,7 +235,7 @@ int kthreadd(void *unused)
set_user_nice(tsk, KTHREAD_NICE_LEVEL); set_user_nice(tsk, KTHREAD_NICE_LEVEL);
set_cpus_allowed(tsk, CPU_MASK_ALL); set_cpus_allowed(tsk, CPU_MASK_ALL);
current->flags |= PF_NOFREEZE; current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
for (;;) { for (;;) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
......
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
*/ */
#define TIMEOUT (20 * HZ) #define TIMEOUT (20 * HZ)
#define FREEZER_KERNEL_THREADS 0
#define FREEZER_USER_SPACE 1
static inline int freezeable(struct task_struct * p) static inline int freezeable(struct task_struct * p)
{ {
if ((p == current) || if ((p == current) ||
...@@ -84,63 +81,53 @@ static void fake_signal_wake_up(struct task_struct *p) ...@@ -84,63 +81,53 @@ static void fake_signal_wake_up(struct task_struct *p)
spin_unlock_irqrestore(&p->sighand->siglock, flags); spin_unlock_irqrestore(&p->sighand->siglock, flags);
} }
static int has_mm(struct task_struct *p) static inline bool should_send_signal(struct task_struct *p)
{ {
return (p->mm && !(p->flags & PF_BORROWED_MM)); return !(p->flags & PF_FREEZER_NOSIG);
} }
/** /**
* freeze_task - send a freeze request to given task * freeze_task - send a freeze request to given task
* @p: task to send the request to * @p: task to send the request to
* @with_mm_only: if set, the request will only be sent if the task has its * @sig_only: if set, the request will only be sent if the task has the
* own mm * PF_FREEZER_NOSIG flag unset
* Return value: 0, if @with_mm_only is set and the task has no mm of its * Return value: 'false', if @sig_only is set and the task has
* own or the task is frozen, 1, otherwise * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
* *
* The freeze request is sent by seting the tasks's TIF_FREEZE flag and * The freeze request is sent by setting the tasks's TIF_FREEZE flag and
* either sending a fake signal to it or waking it up, depending on whether * either sending a fake signal to it or waking it up, depending on whether
* or not it has its own mm (ie. it is a user land task). If @with_mm_only * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task
* is set and the task has no mm of its own (ie. it is a kernel thread), * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
* its TIF_FREEZE flag should not be set. * TIF_FREEZE flag will not be set.
*
* The task_lock() is necessary to prevent races with exit_mm() or
* use_mm()/unuse_mm() from occuring.
*/ */
static int freeze_task(struct task_struct *p, int with_mm_only) static bool freeze_task(struct task_struct *p, bool sig_only)
{ {
int ret = 1; /*
* We first check if the task is freezing and next if it has already
* been frozen to avoid the race with frozen_process() which first marks
* the task as frozen and next clears its TIF_FREEZE.
*/
if (!freezing(p)) {
rmb();
if (frozen(p))
return false;
task_lock(p); if (!sig_only || should_send_signal(p))
if (freezing(p)) { set_freeze_flag(p);
if (has_mm(p)) {
if (!signal_pending(p))
fake_signal_wake_up(p);
} else {
if (with_mm_only)
ret = 0;
else else
wake_up_state(p, TASK_INTERRUPTIBLE); return false;
} }
} else {
rmb(); if (should_send_signal(p)) {
if (frozen(p)) { if (!signal_pending(p))
ret = 0;
} else {
if (has_mm(p)) {
set_freeze_flag(p);
fake_signal_wake_up(p); fake_signal_wake_up(p);
} else if (sig_only) {
return false;
} else { } else {
if (with_mm_only) {
ret = 0;
} else {
set_freeze_flag(p);
wake_up_state(p, TASK_INTERRUPTIBLE); wake_up_state(p, TASK_INTERRUPTIBLE);
} }
}
} return true;
}
task_unlock(p);
return ret;
} }
static void cancel_freezing(struct task_struct *p) static void cancel_freezing(struct task_struct *p)
...@@ -156,7 +143,7 @@ static void cancel_freezing(struct task_struct *p) ...@@ -156,7 +143,7 @@ static void cancel_freezing(struct task_struct *p)
} }
} }
static int try_to_freeze_tasks(int freeze_user_space) static int try_to_freeze_tasks(bool sig_only)
{ {
struct task_struct *g, *p; struct task_struct *g, *p;
unsigned long end_time; unsigned long end_time;
...@@ -175,7 +162,7 @@ static int try_to_freeze_tasks(int freeze_user_space) ...@@ -175,7 +162,7 @@ static int try_to_freeze_tasks(int freeze_user_space)
if (frozen(p) || !freezeable(p)) if (frozen(p) || !freezeable(p))
continue; continue;
if (!freeze_task(p, freeze_user_space)) if (!freeze_task(p, sig_only))
continue; continue;
/* /*
...@@ -235,13 +222,13 @@ int freeze_processes(void) ...@@ -235,13 +222,13 @@ int freeze_processes(void)
int error; int error;
printk("Freezing user space processes ... "); printk("Freezing user space processes ... ");
error = try_to_freeze_tasks(FREEZER_USER_SPACE); error = try_to_freeze_tasks(true);
if (error) if (error)
goto Exit; goto Exit;
printk("done.\n"); printk("done.\n");
printk("Freezing remaining freezable tasks ... "); printk("Freezing remaining freezable tasks ... ");
error = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); error = try_to_freeze_tasks(false);
if (error) if (error)
goto Exit; goto Exit;
printk("done."); printk("done.");
...@@ -251,7 +238,7 @@ int freeze_processes(void) ...@@ -251,7 +238,7 @@ int freeze_processes(void)
return error; return error;
} }
static void thaw_tasks(int thaw_user_space) static void thaw_tasks(bool nosig_only)
{ {
struct task_struct *g, *p; struct task_struct *g, *p;
...@@ -260,7 +247,7 @@ static void thaw_tasks(int thaw_user_space) ...@@ -260,7 +247,7 @@ static void thaw_tasks(int thaw_user_space)
if (!freezeable(p)) if (!freezeable(p))
continue; continue;
if (!p->mm == thaw_user_space) if (nosig_only && should_send_signal(p))
continue; continue;
thaw_process(p); thaw_process(p);
...@@ -271,8 +258,8 @@ static void thaw_tasks(int thaw_user_space) ...@@ -271,8 +258,8 @@ static void thaw_tasks(int thaw_user_space)
void thaw_processes(void) void thaw_processes(void)
{ {
printk("Restarting tasks ... "); printk("Restarting tasks ... ");
thaw_tasks(FREEZER_KERNEL_THREADS); thaw_tasks(true);
thaw_tasks(FREEZER_USER_SPACE); thaw_tasks(false);
schedule(); schedule();
printk("done.\n"); printk("done.\n");
} }
......
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