xref: /openbmc/linux/kernel/freezer.c (revision 00c2e63c31d0f431952ff2a671c5c6997dd4f8b2)
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>
98174f150SMatt Helsley #include <linux/module.h>
108174f150SMatt Helsley #include <linux/syscalls.h>
118174f150SMatt Helsley #include <linux/freezer.h>
128174f150SMatt Helsley 
138174f150SMatt Helsley /*
148174f150SMatt Helsley  * freezing is complete, mark current process as frozen
158174f150SMatt Helsley  */
168174f150SMatt Helsley static inline void frozen_process(void)
178174f150SMatt Helsley {
188174f150SMatt Helsley 	if (!unlikely(current->flags & PF_NOFREEZE)) {
198174f150SMatt Helsley 		current->flags |= PF_FROZEN;
208174f150SMatt Helsley 		wmb();
218174f150SMatt Helsley 	}
228174f150SMatt Helsley 	clear_freeze_flag(current);
238174f150SMatt Helsley }
248174f150SMatt Helsley 
258174f150SMatt Helsley /* Refrigerator is place where frozen processes are stored :-). */
268174f150SMatt Helsley void refrigerator(void)
278174f150SMatt Helsley {
288174f150SMatt Helsley 	/* Hmm, should we be allowed to suspend when there are realtime
298174f150SMatt Helsley 	   processes around? */
308174f150SMatt Helsley 	long save;
318174f150SMatt Helsley 
328174f150SMatt Helsley 	task_lock(current);
338174f150SMatt Helsley 	if (freezing(current)) {
348174f150SMatt Helsley 		frozen_process();
358174f150SMatt Helsley 		task_unlock(current);
368174f150SMatt Helsley 	} else {
378174f150SMatt Helsley 		task_unlock(current);
388174f150SMatt Helsley 		return;
398174f150SMatt Helsley 	}
408174f150SMatt Helsley 	save = current->state;
418174f150SMatt Helsley 	pr_debug("%s entered refrigerator\n", current->comm);
428174f150SMatt Helsley 
438174f150SMatt Helsley 	spin_lock_irq(&current->sighand->siglock);
448174f150SMatt Helsley 	recalc_sigpending(); /* We sent fake signal, clean it up */
458174f150SMatt Helsley 	spin_unlock_irq(&current->sighand->siglock);
468174f150SMatt Helsley 
478174f150SMatt Helsley 	for (;;) {
488174f150SMatt Helsley 		set_current_state(TASK_UNINTERRUPTIBLE);
498174f150SMatt Helsley 		if (!frozen(current))
508174f150SMatt Helsley 			break;
518174f150SMatt Helsley 		schedule();
528174f150SMatt Helsley 	}
538174f150SMatt Helsley 	pr_debug("%s left refrigerator\n", current->comm);
548174f150SMatt Helsley 	__set_current_state(save);
558174f150SMatt Helsley }
568174f150SMatt Helsley EXPORT_SYMBOL(refrigerator);
578174f150SMatt Helsley 
588174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p)
598174f150SMatt Helsley {
608174f150SMatt Helsley 	unsigned long flags;
618174f150SMatt Helsley 
628174f150SMatt Helsley 	spin_lock_irqsave(&p->sighand->siglock, flags);
638174f150SMatt Helsley 	signal_wake_up(p, 0);
648174f150SMatt Helsley 	spin_unlock_irqrestore(&p->sighand->siglock, flags);
658174f150SMatt Helsley }
668174f150SMatt Helsley 
678174f150SMatt Helsley /**
688174f150SMatt Helsley  *	freeze_task - send a freeze request to given task
698174f150SMatt Helsley  *	@p: task to send the request to
708174f150SMatt Helsley  *	@sig_only: if set, the request will only be sent if the task has the
718174f150SMatt Helsley  *		PF_FREEZER_NOSIG flag unset
728174f150SMatt Helsley  *	Return value: 'false', if @sig_only is set and the task has
738174f150SMatt Helsley  *		PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
748174f150SMatt Helsley  *
758174f150SMatt Helsley  *	The freeze request is sent by setting the tasks's TIF_FREEZE flag and
768174f150SMatt Helsley  *	either sending a fake signal to it or waking it up, depending on whether
778174f150SMatt Helsley  *	or not it has PF_FREEZER_NOSIG set.  If @sig_only is set and the task
788174f150SMatt Helsley  *	has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
798174f150SMatt Helsley  *	TIF_FREEZE flag will not be set.
808174f150SMatt Helsley  */
818174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only)
828174f150SMatt Helsley {
838174f150SMatt Helsley 	/*
848174f150SMatt Helsley 	 * We first check if the task is freezing and next if it has already
858174f150SMatt Helsley 	 * been frozen to avoid the race with frozen_process() which first marks
868174f150SMatt Helsley 	 * the task as frozen and next clears its TIF_FREEZE.
878174f150SMatt Helsley 	 */
888174f150SMatt Helsley 	if (!freezing(p)) {
898174f150SMatt Helsley 		rmb();
908174f150SMatt Helsley 		if (frozen(p))
918174f150SMatt Helsley 			return false;
928174f150SMatt Helsley 
938174f150SMatt Helsley 		if (!sig_only || should_send_signal(p))
948174f150SMatt Helsley 			set_freeze_flag(p);
958174f150SMatt Helsley 		else
968174f150SMatt Helsley 			return false;
978174f150SMatt Helsley 	}
988174f150SMatt Helsley 
998174f150SMatt Helsley 	if (should_send_signal(p)) {
1008174f150SMatt Helsley 		if (!signal_pending(p))
1018174f150SMatt Helsley 			fake_signal_wake_up(p);
1028174f150SMatt Helsley 	} else if (sig_only) {
1038174f150SMatt Helsley 		return false;
1048174f150SMatt Helsley 	} else {
1058174f150SMatt Helsley 		wake_up_state(p, TASK_INTERRUPTIBLE);
1068174f150SMatt Helsley 	}
1078174f150SMatt Helsley 
1088174f150SMatt Helsley 	return true;
1098174f150SMatt Helsley }
1108174f150SMatt Helsley 
1118174f150SMatt Helsley void cancel_freezing(struct task_struct *p)
1128174f150SMatt Helsley {
1138174f150SMatt Helsley 	unsigned long flags;
1148174f150SMatt Helsley 
1158174f150SMatt Helsley 	if (freezing(p)) {
1168174f150SMatt Helsley 		pr_debug("  clean up: %s\n", p->comm);
1178174f150SMatt Helsley 		clear_freeze_flag(p);
1188174f150SMatt Helsley 		spin_lock_irqsave(&p->sighand->siglock, flags);
1198174f150SMatt Helsley 		recalc_sigpending_and_wake(p);
1208174f150SMatt Helsley 		spin_unlock_irqrestore(&p->sighand->siglock, flags);
1218174f150SMatt Helsley 	}
1228174f150SMatt Helsley }
123dc52ddc0SMatt Helsley 
124*00c2e63cSLi Zefan static int __thaw_process(struct task_struct *p)
125dc52ddc0SMatt Helsley {
126dc52ddc0SMatt Helsley 	if (frozen(p)) {
127dc52ddc0SMatt Helsley 		p->flags &= ~PF_FROZEN;
128dc52ddc0SMatt Helsley 		return 1;
129dc52ddc0SMatt Helsley 	}
130dc52ddc0SMatt Helsley 	clear_freeze_flag(p);
131dc52ddc0SMatt Helsley 	return 0;
132dc52ddc0SMatt Helsley }
133dc52ddc0SMatt Helsley 
134*00c2e63cSLi Zefan /*
135*00c2e63cSLi Zefan  * Wake up a frozen process
136*00c2e63cSLi Zefan  *
137*00c2e63cSLi Zefan  * task_lock() is needed to prevent the race with refrigerator() which may
138*00c2e63cSLi Zefan  * occur if the freezing of tasks fails.  Namely, without the lock, if the
139*00c2e63cSLi Zefan  * freezing of tasks failed, thaw_tasks() might have run before a task in
140*00c2e63cSLi Zefan  * refrigerator() could call frozen_process(), in which case the task would be
141*00c2e63cSLi Zefan  * frozen and no one would thaw it.
142*00c2e63cSLi Zefan  */
143dc52ddc0SMatt Helsley int thaw_process(struct task_struct *p)
144dc52ddc0SMatt Helsley {
145dc52ddc0SMatt Helsley 	task_lock(p);
146dc52ddc0SMatt Helsley 	if (__thaw_process(p) == 1) {
147dc52ddc0SMatt Helsley 		task_unlock(p);
148dc52ddc0SMatt Helsley 		wake_up_process(p);
149dc52ddc0SMatt Helsley 		return 1;
150dc52ddc0SMatt Helsley 	}
151dc52ddc0SMatt Helsley 	task_unlock(p);
152dc52ddc0SMatt Helsley 	return 0;
153dc52ddc0SMatt Helsley }
154dc52ddc0SMatt Helsley EXPORT_SYMBOL(thaw_process);
155