freezer.c 4.1 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * kernel/freezer.c - Function to freeze a process
 *
 * Originally from kernel/power/process.c
 */

#include <linux/interrupt.h>
#include <linux/suspend.h>
9
#include <linux/export.h>
10 11
#include <linux/syscalls.h>
#include <linux/freezer.h>
12
#include <linux/kthread.h>
13

14 15
/* protects freezing and frozen transitions */
static DEFINE_SPINLOCK(freezer_lock);
16 17

/* Refrigerator is place where frozen processes are stored :-). */
18
bool __refrigerator(bool check_kthr_stop)
19 20 21
{
	/* Hmm, should we be allowed to suspend when there are realtime
	   processes around? */
22
	bool was_frozen = false;
23 24
	long save;

25 26 27 28
	/*
	 * Enter FROZEN.  If NOFREEZE, schedule immediate thawing by
	 * clearing freezing.
	 */
29
	spin_lock_irq(&freezer_lock);
30
repeat:
31 32
	if (!freezing(current)) {
		spin_unlock_irq(&freezer_lock);
33
		return was_frozen;
34
	}
35 36 37
	if (current->flags & PF_NOFREEZE)
		clear_freeze_flag(current);
	current->flags |= PF_FROZEN;
38 39
	spin_unlock_irq(&freezer_lock);

40 41 42 43 44 45 46 47 48
	save = current->state;
	pr_debug("%s entered refrigerator\n", current->comm);

	spin_lock_irq(&current->sighand->siglock);
	recalc_sigpending(); /* We sent fake signal, clean it up */
	spin_unlock_irq(&current->sighand->siglock);

	for (;;) {
		set_current_state(TASK_UNINTERRUPTIBLE);
49
		if (!freezing(current) ||
50
		    (check_kthr_stop && kthread_should_stop()))
51
			break;
52
		was_frozen = true;
53 54
		schedule();
	}
55

56 57 58 59 60 61 62
	/* leave FROZEN */
	spin_lock_irq(&freezer_lock);
	if (freezing(current))
		goto repeat;
	current->flags &= ~PF_FROZEN;
	spin_unlock_irq(&freezer_lock);

63
	pr_debug("%s left refrigerator\n", current->comm);
64 65 66 67 68 69 70

	/*
	 * 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);
71 72

	return was_frozen;
73
}
74
EXPORT_SYMBOL(__refrigerator);
75 76 77 78 79 80

static void fake_signal_wake_up(struct task_struct *p)
{
	unsigned long flags;

	spin_lock_irqsave(&p->sighand->siglock, flags);
81
	signal_wake_up(p, 0);
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	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)
{
101 102 103 104 105
	unsigned long flags;
	bool ret = false;

	spin_lock_irqsave(&freezer_lock, flags);

106 107
	if ((p->flags & PF_NOFREEZE) ||
	    (sig_only && !should_send_signal(p)))
108 109 110 111 112 113
		goto out_unlock;

	if (frozen(p))
		goto out_unlock;

	set_freeze_flag(p);
114 115

	if (should_send_signal(p)) {
116 117 118 119 120 121 122
		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().
		 */
123 124 125
	} else {
		wake_up_state(p, TASK_INTERRUPTIBLE);
	}
126 127 128 129
	ret = true;
out_unlock:
	spin_unlock_irqrestore(&freezer_lock, flags);
	return ret;
130 131 132 133 134 135
}

void cancel_freezing(struct task_struct *p)
{
	unsigned long flags;

136
	spin_lock_irqsave(&freezer_lock, flags);
137 138 139
	if (freezing(p)) {
		pr_debug("  clean up: %s\n", p->comm);
		clear_freeze_flag(p);
140
		spin_lock(&p->sighand->siglock);
141
		recalc_sigpending_and_wake(p);
142
		spin_unlock(&p->sighand->siglock);
143
	}
144
	spin_unlock_irqrestore(&freezer_lock, flags);
145
}
146

147
void __thaw_task(struct task_struct *p)
148
{
149
	unsigned long flags;
150

151 152 153 154 155 156
	/*
	 * Clear freezing and kick @p if FROZEN.  Clearing is guaranteed to
	 * be visible to @p as waking up implies wmb.  Waking up inside
	 * freezer_lock also prevents wakeups from leaking outside
	 * refrigerator.
	 */
157
	spin_lock_irqsave(&freezer_lock, flags);
158 159
	clear_freeze_flag(p);
	if (frozen(p))
160
		wake_up_process(p);
161
	spin_unlock_irqrestore(&freezer_lock, flags);
162
}