xref: /openbmc/linux/kernel/freezer.c (revision f5d39b02)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28174f150SMatt Helsley /*
38174f150SMatt Helsley  * kernel/freezer.c - Function to freeze a process
48174f150SMatt Helsley  *
58174f150SMatt Helsley  * Originally from kernel/power/process.c
68174f150SMatt Helsley  */
78174f150SMatt Helsley 
88174f150SMatt Helsley #include <linux/interrupt.h>
98174f150SMatt Helsley #include <linux/suspend.h>
109984de1aSPaul Gortmaker #include <linux/export.h>
118174f150SMatt Helsley #include <linux/syscalls.h>
128174f150SMatt Helsley #include <linux/freezer.h>
138a32c441STejun Heo #include <linux/kthread.h>
148174f150SMatt Helsley 
15a3201227STejun Heo /* total number of freezing conditions in effect */
16*f5d39b02SPeter Zijlstra DEFINE_STATIC_KEY_FALSE(freezer_active);
17*f5d39b02SPeter Zijlstra EXPORT_SYMBOL(freezer_active);
18a3201227STejun Heo 
19*f5d39b02SPeter Zijlstra /*
20*f5d39b02SPeter Zijlstra  * indicate whether PM freezing is in effect, protected by
2155f2503cSPingfan Liu  * system_transition_mutex
2255f2503cSPingfan Liu  */
23a3201227STejun Heo bool pm_freezing;
24a3201227STejun Heo bool pm_nosig_freezing;
25a3201227STejun Heo 
260c9af092STejun Heo /* protects freezing and frozen transitions */
270c9af092STejun Heo static DEFINE_SPINLOCK(freezer_lock);
288174f150SMatt Helsley 
29a3201227STejun Heo /**
30a3201227STejun Heo  * freezing_slow_path - slow path for testing whether a task needs to be frozen
31a3201227STejun Heo  * @p: task to be tested
32a3201227STejun Heo  *
33*f5d39b02SPeter Zijlstra  * This function is called by freezing() if freezer_active isn't zero
34a3201227STejun Heo  * and tests whether @p needs to enter and stay in frozen state.  Can be
35a3201227STejun Heo  * called under any context.  The freezers are responsible for ensuring the
36a3201227STejun Heo  * target tasks see the updated state.
37a3201227STejun Heo  */
freezing_slow_path(struct task_struct * p)38a3201227STejun Heo bool freezing_slow_path(struct task_struct *p)
39a3201227STejun Heo {
402b44c4dbSColin Cross 	if (p->flags & (PF_NOFREEZE | PF_SUSPEND_TASK))
41a3201227STejun Heo 		return false;
42a3201227STejun Heo 
43a34c80a7SMichal Hocko 	if (test_tsk_thread_flag(p, TIF_MEMDIE))
4451fae6daSCong Wang 		return false;
4551fae6daSCong Wang 
46a3201227STejun Heo 	if (pm_nosig_freezing || cgroup_freezing(p))
47a3201227STejun Heo 		return true;
48a3201227STejun Heo 
4934b087e4STejun Heo 	if (pm_freezing && !(p->flags & PF_KTHREAD))
50a3201227STejun Heo 		return true;
51a3201227STejun Heo 
52a3201227STejun Heo 	return false;
53a3201227STejun Heo }
54a3201227STejun Heo EXPORT_SYMBOL(freezing_slow_path);
55a3201227STejun Heo 
frozen(struct task_struct * p)56*f5d39b02SPeter Zijlstra bool frozen(struct task_struct *p)
57*f5d39b02SPeter Zijlstra {
58*f5d39b02SPeter Zijlstra 	return READ_ONCE(p->__state) & TASK_FROZEN;
59*f5d39b02SPeter Zijlstra }
60*f5d39b02SPeter Zijlstra 
618174f150SMatt Helsley /* Refrigerator is place where frozen processes are stored :-). */
__refrigerator(bool check_kthr_stop)628a32c441STejun Heo bool __refrigerator(bool check_kthr_stop)
638174f150SMatt Helsley {
64*f5d39b02SPeter Zijlstra 	unsigned int state = get_current_state();
65a0acae0eSTejun Heo 	bool was_frozen = false;
668174f150SMatt Helsley 
678174f150SMatt Helsley 	pr_debug("%s entered refrigerator\n", current->comm);
688174f150SMatt Helsley 
69*f5d39b02SPeter Zijlstra 	WARN_ON_ONCE(state && !(state & TASK_NORMAL));
70*f5d39b02SPeter Zijlstra 
718174f150SMatt Helsley 	for (;;) {
72*f5d39b02SPeter Zijlstra 		bool freeze;
73*f5d39b02SPeter Zijlstra 
74*f5d39b02SPeter Zijlstra 		set_current_state(TASK_FROZEN);
755ece3eaeSTejun Heo 
765ece3eaeSTejun Heo 		spin_lock_irq(&freezer_lock);
77*f5d39b02SPeter Zijlstra 		freeze = freezing(current) && !(check_kthr_stop && kthread_should_stop());
785ece3eaeSTejun Heo 		spin_unlock_irq(&freezer_lock);
795ece3eaeSTejun Heo 
80*f5d39b02SPeter Zijlstra 		if (!freeze)
818174f150SMatt Helsley 			break;
82*f5d39b02SPeter Zijlstra 
83a0acae0eSTejun Heo 		was_frozen = true;
848174f150SMatt Helsley 		schedule();
858174f150SMatt Helsley 	}
86*f5d39b02SPeter Zijlstra 	__set_current_state(TASK_RUNNING);
876301cb95SThomas Gleixner 
888174f150SMatt Helsley 	pr_debug("%s left refrigerator\n", current->comm);
8950fb4f7fSTejun Heo 
90a0acae0eSTejun Heo 	return was_frozen;
918174f150SMatt Helsley }
92a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator);
938174f150SMatt Helsley 
fake_signal_wake_up(struct task_struct * p)948174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p)
958174f150SMatt Helsley {
968174f150SMatt Helsley 	unsigned long flags;
978174f150SMatt Helsley 
9837ad8acaSTejun Heo 	if (lock_task_sighand(p, &flags)) {
99d6cc7685STejun Heo 		signal_wake_up(p, 0);
10037ad8acaSTejun Heo 		unlock_task_sighand(p, &flags);
10137ad8acaSTejun Heo 	}
1028174f150SMatt Helsley }
1038174f150SMatt Helsley 
__set_task_frozen(struct task_struct * p,void * arg)104*f5d39b02SPeter Zijlstra static int __set_task_frozen(struct task_struct *p, void *arg)
105*f5d39b02SPeter Zijlstra {
106*f5d39b02SPeter Zijlstra 	unsigned int state = READ_ONCE(p->__state);
107*f5d39b02SPeter Zijlstra 
108*f5d39b02SPeter Zijlstra 	if (p->on_rq)
109*f5d39b02SPeter Zijlstra 		return 0;
110*f5d39b02SPeter Zijlstra 
111*f5d39b02SPeter Zijlstra 	if (p != current && task_curr(p))
112*f5d39b02SPeter Zijlstra 		return 0;
113*f5d39b02SPeter Zijlstra 
114*f5d39b02SPeter Zijlstra 	if (!(state & (TASK_FREEZABLE | __TASK_STOPPED | __TASK_TRACED)))
115*f5d39b02SPeter Zijlstra 		return 0;
116*f5d39b02SPeter Zijlstra 
117*f5d39b02SPeter Zijlstra 	/*
118*f5d39b02SPeter Zijlstra 	 * Only TASK_NORMAL can be augmented with TASK_FREEZABLE, since they
119*f5d39b02SPeter Zijlstra 	 * can suffer spurious wakeups.
120*f5d39b02SPeter Zijlstra 	 */
121*f5d39b02SPeter Zijlstra 	if (state & TASK_FREEZABLE)
122*f5d39b02SPeter Zijlstra 		WARN_ON_ONCE(!(state & TASK_NORMAL));
123*f5d39b02SPeter Zijlstra 
124*f5d39b02SPeter Zijlstra #ifdef CONFIG_LOCKDEP
125*f5d39b02SPeter Zijlstra 	/*
126*f5d39b02SPeter Zijlstra 	 * It's dangerous to freeze with locks held; there be dragons there.
127*f5d39b02SPeter Zijlstra 	 */
128*f5d39b02SPeter Zijlstra 	if (!(state & __TASK_FREEZABLE_UNSAFE))
129*f5d39b02SPeter Zijlstra 		WARN_ON_ONCE(debug_locks && p->lockdep_depth);
130*f5d39b02SPeter Zijlstra #endif
131*f5d39b02SPeter Zijlstra 
132*f5d39b02SPeter Zijlstra 	WRITE_ONCE(p->__state, TASK_FROZEN);
133*f5d39b02SPeter Zijlstra 	return TASK_FROZEN;
134*f5d39b02SPeter Zijlstra }
135*f5d39b02SPeter Zijlstra 
__freeze_task(struct task_struct * p)136*f5d39b02SPeter Zijlstra static bool __freeze_task(struct task_struct *p)
137*f5d39b02SPeter Zijlstra {
138*f5d39b02SPeter Zijlstra 	/* TASK_FREEZABLE|TASK_STOPPED|TASK_TRACED -> TASK_FROZEN */
139*f5d39b02SPeter Zijlstra 	return task_call_func(p, __set_task_frozen, NULL);
140*f5d39b02SPeter Zijlstra }
141*f5d39b02SPeter Zijlstra 
1428174f150SMatt Helsley /**
1438174f150SMatt Helsley  * freeze_task - send a freeze request to given task
1448174f150SMatt Helsley  * @p: task to send the request to
1458174f150SMatt Helsley  *
14637f08be1SMarcos Paulo de Souza  * If @p is freezing, the freeze request is sent either by sending a fake
14737f08be1SMarcos Paulo de Souza  * signal (if it's not a kernel thread) or waking it up (if it's a kernel
14837f08be1SMarcos Paulo de Souza  * thread).
149839e3407STejun Heo  *
150839e3407STejun Heo  * RETURNS:
151839e3407STejun Heo  * %false, if @p is not freezing or already frozen; %true, otherwise
1528174f150SMatt Helsley  */
freeze_task(struct task_struct * p)153839e3407STejun Heo bool freeze_task(struct task_struct *p)
1548174f150SMatt Helsley {
1550c9af092STejun Heo 	unsigned long flags;
1568174f150SMatt Helsley 
1570c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
158*f5d39b02SPeter Zijlstra 	if (!freezing(p) || frozen(p) || __freeze_task(p)) {
159a3201227STejun Heo 		spin_unlock_irqrestore(&freezer_lock, flags);
160a3201227STejun Heo 		return false;
161a3201227STejun Heo 	}
1628174f150SMatt Helsley 
163d3dc04cdSJens Axboe 	if (!(p->flags & PF_KTHREAD))
1648174f150SMatt Helsley 		fake_signal_wake_up(p);
1655d8f72b5SOleg Nesterov 	else
166*f5d39b02SPeter Zijlstra 		wake_up_state(p, TASK_NORMAL);
167a3201227STejun Heo 
1680c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
169a3201227STejun Heo 	return true;
1708174f150SMatt Helsley }
1718174f150SMatt Helsley 
172*f5d39b02SPeter Zijlstra /*
173*f5d39b02SPeter Zijlstra  * The special task states (TASK_STOPPED, TASK_TRACED) keep their canonical
174*f5d39b02SPeter Zijlstra  * state in p->jobctl. If either of them got a wakeup that was missed because
175*f5d39b02SPeter Zijlstra  * TASK_FROZEN, then their canonical state reflects that and the below will
176*f5d39b02SPeter Zijlstra  * refuse to restore the special state and instead issue the wakeup.
177*f5d39b02SPeter Zijlstra  */
__set_task_special(struct task_struct * p,void * arg)178*f5d39b02SPeter Zijlstra static int __set_task_special(struct task_struct *p, void *arg)
179*f5d39b02SPeter Zijlstra {
180*f5d39b02SPeter Zijlstra 	unsigned int state = 0;
181*f5d39b02SPeter Zijlstra 
182*f5d39b02SPeter Zijlstra 	if (p->jobctl & JOBCTL_TRACED)
183*f5d39b02SPeter Zijlstra 		state = TASK_TRACED;
184*f5d39b02SPeter Zijlstra 
185*f5d39b02SPeter Zijlstra 	else if (p->jobctl & JOBCTL_STOPPED)
186*f5d39b02SPeter Zijlstra 		state = TASK_STOPPED;
187*f5d39b02SPeter Zijlstra 
188*f5d39b02SPeter Zijlstra 	if (state)
189*f5d39b02SPeter Zijlstra 		WRITE_ONCE(p->__state, state);
190*f5d39b02SPeter Zijlstra 
191*f5d39b02SPeter Zijlstra 	return state;
192*f5d39b02SPeter Zijlstra }
193*f5d39b02SPeter Zijlstra 
__thaw_task(struct task_struct * p)194a5be2d0dSTejun Heo void __thaw_task(struct task_struct *p)
195dc52ddc0SMatt Helsley {
196*f5d39b02SPeter Zijlstra 	unsigned long flags, flags2;
197a5be2d0dSTejun Heo 
1980c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
199*f5d39b02SPeter Zijlstra 	if (WARN_ON_ONCE(freezing(p)))
200*f5d39b02SPeter Zijlstra 		goto unlock;
201*f5d39b02SPeter Zijlstra 
202*f5d39b02SPeter Zijlstra 	if (lock_task_sighand(p, &flags2)) {
203*f5d39b02SPeter Zijlstra 		/* TASK_FROZEN -> TASK_{STOPPED,TRACED} */
204*f5d39b02SPeter Zijlstra 		bool ret = task_call_func(p, __set_task_special, NULL);
205*f5d39b02SPeter Zijlstra 		unlock_task_sighand(p, &flags2);
206*f5d39b02SPeter Zijlstra 		if (ret)
207*f5d39b02SPeter Zijlstra 			goto unlock;
208*f5d39b02SPeter Zijlstra 	}
209*f5d39b02SPeter Zijlstra 
210*f5d39b02SPeter Zijlstra 	wake_up_state(p, TASK_FROZEN);
211*f5d39b02SPeter Zijlstra unlock:
2120c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
213dc52ddc0SMatt Helsley }
21496ee6d85STejun Heo 
21596ee6d85STejun Heo /**
21634b087e4STejun Heo  * set_freezable - make %current freezable
21796ee6d85STejun Heo  *
21896ee6d85STejun Heo  * Mark %current freezable and enter refrigerator if necessary.
21996ee6d85STejun Heo  */
set_freezable(void)22034b087e4STejun Heo bool set_freezable(void)
22196ee6d85STejun Heo {
22296ee6d85STejun Heo 	might_sleep();
22396ee6d85STejun Heo 
22496ee6d85STejun Heo 	/*
22596ee6d85STejun Heo 	 * Modify flags while holding freezer_lock.  This ensures the
22696ee6d85STejun Heo 	 * freezer notices that we aren't frozen yet or the freezing
22796ee6d85STejun Heo 	 * condition is visible to try_to_freeze() below.
22896ee6d85STejun Heo 	 */
22996ee6d85STejun Heo 	spin_lock_irq(&freezer_lock);
23096ee6d85STejun Heo 	current->flags &= ~PF_NOFREEZE;
23196ee6d85STejun Heo 	spin_unlock_irq(&freezer_lock);
23296ee6d85STejun Heo 
23396ee6d85STejun Heo 	return try_to_freeze();
23496ee6d85STejun Heo }
23534b087e4STejun Heo EXPORT_SYMBOL(set_freezable);
236