xref: /linux-6.15/kernel/freezer.c (revision 85f1d476)
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 
140c9af092STejun Heo /* protects freezing and frozen transitions */
150c9af092STejun 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 
256907483bSTejun Heo 	/*
266907483bSTejun Heo 	 * Enter FROZEN.  If NOFREEZE, schedule immediate thawing by
276907483bSTejun Heo 	 * clearing freezing.
286907483bSTejun Heo 	 */
290c9af092STejun Heo 	spin_lock_irq(&freezer_lock);
306907483bSTejun Heo repeat:
310c9af092STejun Heo 	if (!freezing(current)) {
320c9af092STejun Heo 		spin_unlock_irq(&freezer_lock);
33a0acae0eSTejun Heo 		return was_frozen;
348174f150SMatt Helsley 	}
356907483bSTejun Heo 	if (current->flags & PF_NOFREEZE)
360c9af092STejun Heo 		clear_freeze_flag(current);
376907483bSTejun Heo 	current->flags |= PF_FROZEN;
380c9af092STejun Heo 	spin_unlock_irq(&freezer_lock);
390c9af092STejun Heo 
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 
476301cb95SThomas Gleixner 	/* prevent accounting of that task to load */
486301cb95SThomas Gleixner 	current->flags |= PF_FREEZING;
496301cb95SThomas Gleixner 
508174f150SMatt Helsley 	for (;;) {
518174f150SMatt Helsley 		set_current_state(TASK_UNINTERRUPTIBLE);
526907483bSTejun Heo 		if (!freezing(current) ||
538a32c441STejun Heo 		    (check_kthr_stop && kthread_should_stop()))
548174f150SMatt Helsley 			break;
55a0acae0eSTejun Heo 		was_frozen = true;
568174f150SMatt Helsley 		schedule();
578174f150SMatt Helsley 	}
586301cb95SThomas Gleixner 
596301cb95SThomas Gleixner 	/* Remove the accounting blocker */
606301cb95SThomas Gleixner 	current->flags &= ~PF_FREEZING;
616301cb95SThomas Gleixner 
626907483bSTejun Heo 	/* leave FROZEN */
636907483bSTejun Heo 	spin_lock_irq(&freezer_lock);
646907483bSTejun Heo 	if (freezing(current))
656907483bSTejun Heo 		goto repeat;
666907483bSTejun Heo 	current->flags &= ~PF_FROZEN;
676907483bSTejun Heo 	spin_unlock_irq(&freezer_lock);
686907483bSTejun Heo 
698174f150SMatt Helsley 	pr_debug("%s left refrigerator\n", current->comm);
7050fb4f7fSTejun Heo 
7150fb4f7fSTejun Heo 	/*
7250fb4f7fSTejun Heo 	 * Restore saved task state before returning.  The mb'd version
7350fb4f7fSTejun Heo 	 * needs to be used; otherwise, it might silently break
7450fb4f7fSTejun Heo 	 * synchronization which depends on ordered task state change.
7550fb4f7fSTejun Heo 	 */
7650fb4f7fSTejun Heo 	set_current_state(save);
77a0acae0eSTejun Heo 
78a0acae0eSTejun Heo 	return was_frozen;
798174f150SMatt Helsley }
80a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator);
818174f150SMatt Helsley 
828174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p)
838174f150SMatt Helsley {
848174f150SMatt Helsley 	unsigned long flags;
858174f150SMatt Helsley 
868174f150SMatt Helsley 	spin_lock_irqsave(&p->sighand->siglock, flags);
87d6cc7685STejun Heo 	signal_wake_up(p, 0);
888174f150SMatt Helsley 	spin_unlock_irqrestore(&p->sighand->siglock, flags);
898174f150SMatt Helsley }
908174f150SMatt Helsley 
918174f150SMatt Helsley /**
928174f150SMatt Helsley  *	freeze_task - send a freeze request to given task
938174f150SMatt Helsley  *	@p: task to send the request to
948174f150SMatt Helsley  *	@sig_only: if set, the request will only be sent if the task has the
958174f150SMatt Helsley  *		PF_FREEZER_NOSIG flag unset
968174f150SMatt Helsley  *	Return value: 'false', if @sig_only is set and the task has
978174f150SMatt Helsley  *		PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
988174f150SMatt Helsley  *
998174f150SMatt Helsley  *	The freeze request is sent by setting the tasks's TIF_FREEZE flag and
1008174f150SMatt Helsley  *	either sending a fake signal to it or waking it up, depending on whether
1018174f150SMatt Helsley  *	or not it has PF_FREEZER_NOSIG set.  If @sig_only is set and the task
1028174f150SMatt Helsley  *	has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
1038174f150SMatt Helsley  *	TIF_FREEZE flag will not be set.
1048174f150SMatt Helsley  */
1058174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only)
1068174f150SMatt Helsley {
1070c9af092STejun Heo 	unsigned long flags;
1080c9af092STejun Heo 	bool ret = false;
1098174f150SMatt Helsley 
1100c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
1110c9af092STejun Heo 
112*85f1d476STejun Heo 	if ((p->flags & PF_NOFREEZE) ||
113*85f1d476STejun Heo 	    (sig_only && !should_send_signal(p)))
1140c9af092STejun Heo 		goto out_unlock;
1150c9af092STejun Heo 
1160c9af092STejun Heo 	if (frozen(p))
1170c9af092STejun Heo 		goto out_unlock;
1180c9af092STejun Heo 
1198174f150SMatt Helsley 	set_freeze_flag(p);
1208174f150SMatt Helsley 
1218174f150SMatt Helsley 	if (should_send_signal(p)) {
1228174f150SMatt Helsley 		fake_signal_wake_up(p);
1238cfe400cSTejun Heo 		/*
1248cfe400cSTejun Heo 		 * fake_signal_wake_up() goes through p's scheduler
1258cfe400cSTejun Heo 		 * lock and guarantees that TASK_STOPPED/TRACED ->
1268cfe400cSTejun Heo 		 * TASK_RUNNING transition can't race with task state
1278cfe400cSTejun Heo 		 * testing in try_to_freeze_tasks().
1288cfe400cSTejun Heo 		 */
1298174f150SMatt Helsley 	} else {
1308174f150SMatt Helsley 		wake_up_state(p, TASK_INTERRUPTIBLE);
1318174f150SMatt Helsley 	}
1320c9af092STejun Heo 	ret = true;
1330c9af092STejun Heo out_unlock:
1340c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
1350c9af092STejun Heo 	return ret;
1368174f150SMatt Helsley }
1378174f150SMatt Helsley 
1388174f150SMatt Helsley void cancel_freezing(struct task_struct *p)
1398174f150SMatt Helsley {
1408174f150SMatt Helsley 	unsigned long flags;
1418174f150SMatt Helsley 
1420c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
1438174f150SMatt Helsley 	if (freezing(p)) {
1448174f150SMatt Helsley 		pr_debug("  clean up: %s\n", p->comm);
1458174f150SMatt Helsley 		clear_freeze_flag(p);
1460c9af092STejun Heo 		spin_lock(&p->sighand->siglock);
1478174f150SMatt Helsley 		recalc_sigpending_and_wake(p);
1480c9af092STejun Heo 		spin_unlock(&p->sighand->siglock);
1498174f150SMatt Helsley 	}
1500c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
1518174f150SMatt Helsley }
152dc52ddc0SMatt Helsley 
153a5be2d0dSTejun Heo void __thaw_task(struct task_struct *p)
154dc52ddc0SMatt Helsley {
1550c9af092STejun Heo 	unsigned long flags;
156a5be2d0dSTejun Heo 
1576907483bSTejun Heo 	/*
1586907483bSTejun Heo 	 * Clear freezing and kick @p if FROZEN.  Clearing is guaranteed to
1596907483bSTejun Heo 	 * be visible to @p as waking up implies wmb.  Waking up inside
1606907483bSTejun Heo 	 * freezer_lock also prevents wakeups from leaking outside
1616907483bSTejun Heo 	 * refrigerator.
1626907483bSTejun Heo 	 */
1630c9af092STejun Heo 	spin_lock_irqsave(&freezer_lock, flags);
1640c9af092STejun Heo 	clear_freeze_flag(p);
1656907483bSTejun Heo 	if (frozen(p))
1666907483bSTejun Heo 		wake_up_process(p);
1670c9af092STejun Heo 	spin_unlock_irqrestore(&freezer_lock, flags);
168dc52ddc0SMatt Helsley }
169