/*1* kernel/freezer.c - Function to freeze a process2*3* Originally from kernel/power/process.c4*/56#include <linux/interrupt.h>7#include <linux/suspend.h>8#include <linux/module.h>9#include <linux/syscalls.h>10#include <linux/freezer.h>1112/*13* freezing is complete, mark current process as frozen14*/15static inline void frozen_process(void)16{17if (!unlikely(current->flags & PF_NOFREEZE)) {18current->flags |= PF_FROZEN;19smp_wmb();20}21clear_freeze_flag(current);22}2324/* Refrigerator is place where frozen processes are stored :-). */25void refrigerator(void)26{27/* Hmm, should we be allowed to suspend when there are realtime28processes around? */29long save;3031task_lock(current);32if (freezing(current)) {33frozen_process();34task_unlock(current);35} else {36task_unlock(current);37return;38}39save = current->state;40pr_debug("%s entered refrigerator\n", current->comm);4142spin_lock_irq(¤t->sighand->siglock);43recalc_sigpending(); /* We sent fake signal, clean it up */44spin_unlock_irq(¤t->sighand->siglock);4546/* prevent accounting of that task to load */47current->flags |= PF_FREEZING;4849for (;;) {50set_current_state(TASK_UNINTERRUPTIBLE);51if (!frozen(current))52break;53schedule();54}5556/* Remove the accounting blocker */57current->flags &= ~PF_FREEZING;5859pr_debug("%s left refrigerator\n", current->comm);60__set_current_state(save);61}62EXPORT_SYMBOL(refrigerator);6364static void fake_signal_wake_up(struct task_struct *p)65{66unsigned long flags;6768spin_lock_irqsave(&p->sighand->siglock, flags);69signal_wake_up(p, 0);70spin_unlock_irqrestore(&p->sighand->siglock, flags);71}7273/**74* freeze_task - send a freeze request to given task75* @p: task to send the request to76* @sig_only: if set, the request will only be sent if the task has the77* PF_FREEZER_NOSIG flag unset78* Return value: 'false', if @sig_only is set and the task has79* PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise80*81* The freeze request is sent by setting the tasks's TIF_FREEZE flag and82* either sending a fake signal to it or waking it up, depending on whether83* or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task84* has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its85* TIF_FREEZE flag will not be set.86*/87bool freeze_task(struct task_struct *p, bool sig_only)88{89/*90* We first check if the task is freezing and next if it has already91* been frozen to avoid the race with frozen_process() which first marks92* the task as frozen and next clears its TIF_FREEZE.93*/94if (!freezing(p)) {95smp_rmb();96if (frozen(p))97return false;9899if (!sig_only || should_send_signal(p))100set_freeze_flag(p);101else102return false;103}104105if (should_send_signal(p)) {106fake_signal_wake_up(p);107/*108* fake_signal_wake_up() goes through p's scheduler109* lock and guarantees that TASK_STOPPED/TRACED ->110* TASK_RUNNING transition can't race with task state111* testing in try_to_freeze_tasks().112*/113} else if (sig_only) {114return false;115} else {116wake_up_state(p, TASK_INTERRUPTIBLE);117}118119return true;120}121122void cancel_freezing(struct task_struct *p)123{124unsigned long flags;125126if (freezing(p)) {127pr_debug(" clean up: %s\n", p->comm);128clear_freeze_flag(p);129spin_lock_irqsave(&p->sighand->siglock, flags);130recalc_sigpending_and_wake(p);131spin_unlock_irqrestore(&p->sighand->siglock, flags);132}133}134135static int __thaw_process(struct task_struct *p)136{137if (frozen(p)) {138p->flags &= ~PF_FROZEN;139return 1;140}141clear_freeze_flag(p);142return 0;143}144145/*146* Wake up a frozen process147*148* task_lock() is needed to prevent the race with refrigerator() which may149* occur if the freezing of tasks fails. Namely, without the lock, if the150* freezing of tasks failed, thaw_tasks() might have run before a task in151* refrigerator() could call frozen_process(), in which case the task would be152* frozen and no one would thaw it.153*/154int thaw_process(struct task_struct *p)155{156task_lock(p);157if (__thaw_process(p) == 1) {158task_unlock(p);159wake_up_process(p);160return 1;161}162task_unlock(p);163return 0;164}165EXPORT_SYMBOL(thaw_process);166167168