freezer: do not send signals to kernel threads

The freezer should not send signals to kernel threads, since that may lead to
subtle problems.  In particular, commit
b74d0deb96 has changed recalc_sigpending_tsk()
so that it doesn't clear TIF_SIGPENDING.  For this reason, if the freezer
continues to send fake signals to kernel threads and the freezing of kernel
threads fails, some of them may be running with TIF_SIGPENDING set forever.

Accordingly, recalc_sigpending_tsk() shouldn't set the task's TIF_SIGPENDING
flag if TIF_FREEZE is set.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Cc: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Rafael J. Wysocki
2007-10-18 03:04:46 -07:00
committed by Linus Torvalds
parent e42837bcd3
commit d5d8c5976d
3 changed files with 93 additions and 46 deletions

View File

@@ -75,21 +75,79 @@ void refrigerator(void)
__set_current_state(save);
}
static void freeze_task(struct task_struct *p)
static void fake_signal_wake_up(struct task_struct *p, int resume)
{
unsigned long flags;
if (!freezing(p)) {
spin_lock_irqsave(&p->sighand->siglock, flags);
signal_wake_up(p, resume);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
}
static void send_fake_signal(struct task_struct *p)
{
if (p->state == TASK_STOPPED)
force_sig_specific(SIGSTOP, p);
fake_signal_wake_up(p, p->state == TASK_STOPPED);
}
static int has_mm(struct task_struct *p)
{
return (p->mm && !(p->flags & PF_BORROWED_MM));
}
/**
* freeze_task - send a freeze request to given task
* @p: task to send the request to
* @with_mm_only: if set, the request will only be sent if the task has its
* own mm
* Return value: 0, if @with_mm_only is set and the task has no mm of its
* own or the task is frozen, 1, otherwise
*
* The freeze request is sent by seting the tasks's TIF_FREEZE flag and
* 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
* is set and the task has no mm of its own (ie. it is a kernel thread),
* its TIF_FREEZE flag should 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)
{
int ret = 1;
task_lock(p);
if (freezing(p)) {
if (has_mm(p)) {
if (!signal_pending(p))
fake_signal_wake_up(p, 0);
} else {
if (with_mm_only)
ret = 0;
else
wake_up_state(p, TASK_INTERRUPTIBLE);
}
} else {
rmb();
if (!frozen(p)) {
set_freeze_flag(p);
if (p->state == TASK_STOPPED)
force_sig_specific(SIGSTOP, p);
spin_lock_irqsave(&p->sighand->siglock, flags);
signal_wake_up(p, p->state == TASK_STOPPED);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
if (frozen(p)) {
ret = 0;
} else {
if (has_mm(p)) {
set_freeze_flag(p);
send_fake_signal(p);
} else {
if (with_mm_only) {
ret = 0;
} else {
set_freeze_flag(p);
wake_up_state(p, TASK_INTERRUPTIBLE);
}
}
}
}
task_unlock(p);
return ret;
}
static void cancel_freezing(struct task_struct *p)
@@ -119,31 +177,14 @@ static int try_to_freeze_tasks(int freeze_user_space)
if (frozen(p) || !freezeable(p))
continue;
if (freeze_user_space) {
if (p->state == TASK_TRACED &&
frozen(p->parent)) {
cancel_freezing(p);
continue;
}
/*
* Kernel threads should not have TIF_FREEZE set
* at this point, so we must ensure that either
* p->mm is not NULL *and* PF_BORROWED_MM is
* unset, or TIF_FRREZE is left unset.
* The task_lock() is necessary to prevent races
* with exit_mm() or use_mm()/unuse_mm() from
* occuring.
*/
task_lock(p);
if (!p->mm || (p->flags & PF_BORROWED_MM)) {
task_unlock(p);
continue;
}
freeze_task(p);
task_unlock(p);
} else {
freeze_task(p);
if (p->state == TASK_TRACED && frozen(p->parent)) {
cancel_freezing(p);
continue;
}
if (!freeze_task(p, freeze_user_space))
continue;
if (!freezer_should_skip(p))
todo++;
} while_each_thread(g, p);