mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 01:51:53 +00:00
a5be2d0d1a
thaw_process() now has only internal users - system and cgroup freezers. Remove the unnecessary return value, rename, unexport and collapse __thaw_process() into it. This will help further updates to the freezer code. -v3: oom_kill grew a use of thaw_process() while this patch was pending. Convert it to use __thaw_task() for now. In the longer term, this should be handled by allowing tasks to die if killed even if it's frozen. -v2: minor style update as suggested by Matt. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Paul Menage <menage@google.com> Cc: Matt Helsley <matthltc@us.ibm.com>
172 lines
4.3 KiB
C
172 lines
4.3 KiB
C
/*
|
|
* kernel/freezer.c - Function to freeze a process
|
|
*
|
|
* Originally from kernel/power/process.c
|
|
*/
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/export.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/freezer.h>
|
|
#include <linux/kthread.h>
|
|
|
|
/*
|
|
* freezing is complete, mark current process as frozen
|
|
*/
|
|
static inline void frozen_process(void)
|
|
{
|
|
if (!unlikely(current->flags & PF_NOFREEZE)) {
|
|
current->flags |= PF_FROZEN;
|
|
smp_wmb();
|
|
}
|
|
clear_freeze_flag(current);
|
|
}
|
|
|
|
/* Refrigerator is place where frozen processes are stored :-). */
|
|
bool __refrigerator(bool check_kthr_stop)
|
|
{
|
|
/* Hmm, should we be allowed to suspend when there are realtime
|
|
processes around? */
|
|
bool was_frozen = false;
|
|
long save;
|
|
|
|
task_lock(current);
|
|
if (freezing(current)) {
|
|
frozen_process();
|
|
task_unlock(current);
|
|
} else {
|
|
task_unlock(current);
|
|
return was_frozen;
|
|
}
|
|
save = current->state;
|
|
pr_debug("%s entered refrigerator\n", current->comm);
|
|
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
recalc_sigpending(); /* We sent fake signal, clean it up */
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
|
|
/* prevent accounting of that task to load */
|
|
current->flags |= PF_FREEZING;
|
|
|
|
for (;;) {
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
if (!frozen(current) ||
|
|
(check_kthr_stop && kthread_should_stop()))
|
|
break;
|
|
was_frozen = true;
|
|
schedule();
|
|
}
|
|
|
|
/* Remove the accounting blocker */
|
|
current->flags &= ~PF_FREEZING;
|
|
|
|
pr_debug("%s left refrigerator\n", current->comm);
|
|
|
|
/*
|
|
* Restore saved task state before returning. The mb'd version
|
|
* needs to be used; otherwise, it might silently break
|
|
* synchronization which depends on ordered task state change.
|
|
*/
|
|
set_current_state(save);
|
|
|
|
return was_frozen;
|
|
}
|
|
EXPORT_SYMBOL(__refrigerator);
|
|
|
|
static void fake_signal_wake_up(struct task_struct *p)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&p->sighand->siglock, flags);
|
|
signal_wake_up(p, 0);
|
|
spin_unlock_irqrestore(&p->sighand->siglock, flags);
|
|
}
|
|
|
|
/**
|
|
* freeze_task - send a freeze request to given task
|
|
* @p: task to send the request to
|
|
* @sig_only: if set, the request will only be sent if the task has the
|
|
* PF_FREEZER_NOSIG flag unset
|
|
* Return value: 'false', if @sig_only is set and the task has
|
|
* PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
|
|
*
|
|
* 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
|
|
* or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task
|
|
* has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
|
|
* TIF_FREEZE flag will not be set.
|
|
*/
|
|
bool freeze_task(struct task_struct *p, bool sig_only)
|
|
{
|
|
/*
|
|
* 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)) {
|
|
smp_rmb();
|
|
if (frozen(p))
|
|
return false;
|
|
|
|
if (!sig_only || should_send_signal(p))
|
|
set_freeze_flag(p);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
if (should_send_signal(p)) {
|
|
fake_signal_wake_up(p);
|
|
/*
|
|
* fake_signal_wake_up() goes through p's scheduler
|
|
* lock and guarantees that TASK_STOPPED/TRACED ->
|
|
* TASK_RUNNING transition can't race with task state
|
|
* testing in try_to_freeze_tasks().
|
|
*/
|
|
} else if (sig_only) {
|
|
return false;
|
|
} else {
|
|
wake_up_state(p, TASK_INTERRUPTIBLE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void cancel_freezing(struct task_struct *p)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (freezing(p)) {
|
|
pr_debug(" clean up: %s\n", p->comm);
|
|
clear_freeze_flag(p);
|
|
spin_lock_irqsave(&p->sighand->siglock, flags);
|
|
recalc_sigpending_and_wake(p);
|
|
spin_unlock_irqrestore(&p->sighand->siglock, flags);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wake up a frozen task
|
|
*
|
|
* task_lock() is needed to prevent the race with refrigerator() which may
|
|
* occur if the freezing of tasks fails. Namely, without the lock, if the
|
|
* freezing of tasks failed, thaw_tasks() might have run before a task in
|
|
* refrigerator() could call frozen_process(), in which case the task would be
|
|
* frozen and no one would thaw it.
|
|
*/
|
|
void __thaw_task(struct task_struct *p)
|
|
{
|
|
bool was_frozen;
|
|
|
|
task_lock(p);
|
|
was_frozen = frozen(p);
|
|
if (was_frozen)
|
|
p->flags &= ~PF_FROZEN;
|
|
else
|
|
clear_freeze_flag(p);
|
|
task_unlock(p);
|
|
|
|
if (was_frozen)
|
|
wake_up_process(p);
|
|
}
|