xref: /openbmc/linux/kernel/freezer.c (revision 0c9af09262864a2744091ee94c98c4a8fd60c98b)
18174f150SMatt Helsley /*
28174f150SMatt Helsley  * kernel/freezer.c - Function to freeze a process
38174f150SMatt Helsley  *
48174f150SMatt Helsley  * Originally from kernel/power/process.c
58174f150SMatt Helsley  */
68174f150SMatt Helsley 
78174f150SMatt Helsley #include <linux/interrupt.h>
88174f150SMatt Helsley #include <linux/suspend.h>
99984de1aSPaul Gortmaker #include <linux/export.h>
108174f150SMatt Helsley #include <linux/syscalls.h>
118174f150SMatt Helsley #include <linux/freezer.h>
128a32c441STejun Heo #include <linux/kthread.h>
138174f150SMatt Helsley 
14*0c9af092STejun Heo /* protects freezing and frozen transitions */
15*0c9af092STejun Heo static DEFINE_SPINLOCK(freezer_lock);
168174f150SMatt Helsley 
178174f150SMatt Helsley /* Refrigerator is place where frozen processes are stored :-). */
188a32c441STejun Heo bool __refrigerator(bool check_kthr_stop)
198174f150SMatt Helsley {
208174f150SMatt Helsley 	/* Hmm, should we be allowed to suspend when there are realtime
218174f150SMatt Helsley 	   processes around? */
22a0acae0eSTejun Heo 	bool was_frozen = false;
238174f150SMatt Helsley 	long save;
248174f150SMatt Helsley 
25*0c9af092STejun Heo 	spin_lock_irq(&freezer_lock);
26*0c9af092STejun Heo 	if (!freezing(current)) {
27*0c9af092STejun Heo 		spin_unlock_irq(&freezer_lock);
28a0acae0eSTejun Heo 		return was_frozen;
298174f150SMatt Helsley 	}
30*0c9af092STejun Heo 	if (!(current->flags & PF_NOFREEZE))
31*0c9af092STejun Heo 		current->flags |= PF_FROZEN;
32*0c9af092STejun Heo 	clear_freeze_flag(current);
33*0c9af092STejun Heo 	spin_unlock_irq(&freezer_lock);
34*0c9af092STejun Heo 
358174f150SMatt Helsley 	save = current->state;
368174f150SMatt Helsley 	pr_debug("%s entered refrigerator\n", current->comm);
378174f150SMatt Helsley 
388174f150SMatt Helsley 	spin_lock_irq(&current->sighand->siglock);
398174f150SMatt Helsley 	recalc_sigpending(); /* We sent fake signal, clean it up */
408174f150SMatt Helsley 	spin_unlock_irq(&current->sighand->siglock);
418174f150SMatt Helsley 
426301cb95SThomas Gleixner 	/* prevent accounting of that task to load */
436301cb95SThomas Gleixner 	current->flags |= PF_FREEZING;
446301cb95SThomas Gleixner 
458174f150SMatt Helsley 	for (;;) {
468174f150SMatt Helsley 		set_current_state(TASK_UNINTERRUPTIBLE);
478a32c441STejun Heo 		if (!frozen(current) ||
488a32c441STejun Heo 		    (check_kthr_stop && kthread_should_stop()))
498174f150SMatt Helsley 			break;
50a0acae0eSTejun Heo 		was_frozen = true;
518174f150SMatt Helsley 		schedule();
528174f150SMatt Helsley 	}
536301cb95SThomas Gleixner 
546301cb95SThomas Gleixner 	/* Remove the accounting blocker */
556301cb95SThomas Gleixner 	current->flags &= ~PF_FREEZING;
566301cb95SThomas Gleixner 
578174f150SMatt Helsley 	pr_debug("%s left refrigerator\n", current->comm);
5850fb4f7fSTejun Heo 
5950fb4f7fSTejun Heo 	/*
6050fb4f7fSTejun Heo 	 * Restore saved task state before returning.  The mb'd version
6150fb4f7fSTejun Heo 	 * needs to be used; otherwise, it might silently break
6250fb4f7fSTejun Heo 	 * synchronization which depends on ordered task state change.
6350fb4f7fSTejun Heo 	 */
6450fb4f7fSTejun Heo 	set_current_state(save);
65a0acae0eSTejun Heo 
66a0acae0eSTejun Heo 	return was_frozen;
678174f150SMatt Helsley }
68a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator);
698174f150SMatt Helsley 
708174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p)
718174f150SMatt Helsley {
728174f150SMatt Helsley 	unsigned long flags;
738174f150SMatt Helsley 
748174f150SMatt Helsley 	spin_lock_irqsave(&p->sighand->siglock, flags);
75d6cc7685STejun Heo 	signal_wake_up(p, 0);
768174f150SMatt Helsley 	spin_unlock_irqrestore(&p->sighand->siglock, flags);
778174f150SMatt Helsley }
788174f150SMatt Helsley 
798174f150SMatt Helsley /**
808174f150SMatt Helsley  *	freeze_task - send a freeze request to given task
818174f150SMatt Helsley  *	@p: task to send the request to
828174f150SMatt Helsley  *	@sig_only: if set, the request will only be sent if the task has the
838174f150SMatt Helsley  *		PF_FREEZER_NOSIG flag unset
848174f150SMatt Helsley  *	Return value: 'false', if @sig_only is set and the task has
858174f150SMatt Helsley  *		PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
868174f150SMatt Helsley  *
878174f150SMatt Helsley  *	The freeze request is sent by setting the tasks's TIF_FREEZE flag and
888174f150SMatt Helsley  *	either sending a fake signal to it or waking it up, depending on whether
898174f150SMatt Helsley  *	or not it has PF_FREEZER_NOSIG set.  If @sig_only is set and the task
908174f150SMatt Helsley  *	has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
918174f150SMatt Helsley  *	TIF_FREEZE flag will not be set.
928174f150SMatt Helsley  */
938174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only)
948174f150SMatt Helsley {
95*0c9af092STejun Heo 	unsigned long flags;
96*0c9af092STejun Heo 	bool ret = false;
978174f150SMatt Helsley 
98*0c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
99*0c9af092STejun Heo 
100*0c9af092STejun Heo 	if (sig_only && !should_send_signal(p))
101*0c9af092STejun Heo 		goto out_unlock;
102*0c9af092STejun Heo 
103*0c9af092STejun Heo 	if (frozen(p))
104*0c9af092STejun Heo 		goto out_unlock;
105*0c9af092STejun Heo 
1068174f150SMatt Helsley 	set_freeze_flag(p);
1078174f150SMatt Helsley 
1088174f150SMatt Helsley 	if (should_send_signal(p)) {
1098174f150SMatt Helsley 		fake_signal_wake_up(p);
1108cfe400cSTejun Heo 		/*
1118cfe400cSTejun Heo 		 * fake_signal_wake_up() goes through p's scheduler
1128cfe400cSTejun Heo 		 * lock and guarantees that TASK_STOPPED/TRACED ->
1138cfe400cSTejun Heo 		 * TASK_RUNNING transition can't race with task state
1148cfe400cSTejun Heo 		 * testing in try_to_freeze_tasks().
1158cfe400cSTejun Heo 		 */
1168174f150SMatt Helsley 	} else {
1178174f150SMatt Helsley 		wake_up_state(p, TASK_INTERRUPTIBLE);
1188174f150SMatt Helsley 	}
119*0c9af092STejun Heo 	ret = true;
120*0c9af092STejun Heo out_unlock:
121*0c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
122*0c9af092STejun Heo 	return ret;
1238174f150SMatt Helsley }
1248174f150SMatt Helsley 
1258174f150SMatt Helsley void cancel_freezing(struct task_struct *p)
1268174f150SMatt Helsley {
1278174f150SMatt Helsley 	unsigned long flags;
1288174f150SMatt Helsley 
129*0c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
1308174f150SMatt Helsley 	if (freezing(p)) {
1318174f150SMatt Helsley 		pr_debug("  clean up: %s\n", p->comm);
1328174f150SMatt Helsley 		clear_freeze_flag(p);
133*0c9af092STejun Heo 		spin_lock(&p->sighand->siglock);
1348174f150SMatt Helsley 		recalc_sigpending_and_wake(p);
135*0c9af092STejun Heo 		spin_unlock(&p->sighand->siglock);
1368174f150SMatt Helsley 	}
137*0c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
1388174f150SMatt Helsley }
139dc52ddc0SMatt Helsley 
14000c2e63cSLi Zefan /*
141a5be2d0dSTejun Heo  * Wake up a frozen task
14200c2e63cSLi Zefan  *
14300c2e63cSLi Zefan  * task_lock() is needed to prevent the race with refrigerator() which may
14400c2e63cSLi Zefan  * occur if the freezing of tasks fails.  Namely, without the lock, if the
14500c2e63cSLi Zefan  * freezing of tasks failed, thaw_tasks() might have run before a task in
14600c2e63cSLi Zefan  * refrigerator() could call frozen_process(), in which case the task would be
14700c2e63cSLi Zefan  * frozen and no one would thaw it.
14800c2e63cSLi Zefan  */
149a5be2d0dSTejun Heo void __thaw_task(struct task_struct *p)
150dc52ddc0SMatt Helsley {
151*0c9af092STejun Heo 	unsigned long flags;
152a5be2d0dSTejun Heo 
153*0c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
154*0c9af092STejun Heo 	if (frozen(p)) {
155a5be2d0dSTejun Heo 		p->flags &= ~PF_FROZEN;
156dc52ddc0SMatt Helsley 		wake_up_process(p);
157*0c9af092STejun Heo 	} else {
158*0c9af092STejun Heo 		clear_freeze_flag(p);
159*0c9af092STejun Heo 	}
160*0c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
161dc52ddc0SMatt Helsley }
162