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(¤t->sighand->siglock); 448174f150SMatt Helsley recalc_sigpending(); /* We sent fake signal, clean it up */ 458174f150SMatt Helsley spin_unlock_irq(¤t->sighand->siglock); 468174f150SMatt Helsley 478174f150SMatt Helsley for (;;) { 488174f150SMatt Helsley set_current_state(TASK_UNINTERRUPTIBLE); 496907483bSTejun Heo if (!freezing(current) || 508a32c441STejun Heo (check_kthr_stop && kthread_should_stop())) 518174f150SMatt Helsley break; 52a0acae0eSTejun Heo was_frozen = true; 538174f150SMatt Helsley schedule(); 548174f150SMatt Helsley } 556301cb95SThomas Gleixner 566907483bSTejun Heo /* leave FROZEN */ 576907483bSTejun Heo spin_lock_irq(&freezer_lock); 586907483bSTejun Heo if (freezing(current)) 596907483bSTejun Heo goto repeat; 606907483bSTejun Heo current->flags &= ~PF_FROZEN; 616907483bSTejun Heo spin_unlock_irq(&freezer_lock); 626907483bSTejun Heo 638174f150SMatt Helsley pr_debug("%s left refrigerator\n", current->comm); 6450fb4f7fSTejun Heo 6550fb4f7fSTejun Heo /* 6650fb4f7fSTejun Heo * Restore saved task state before returning. The mb'd version 6750fb4f7fSTejun Heo * needs to be used; otherwise, it might silently break 6850fb4f7fSTejun Heo * synchronization which depends on ordered task state change. 6950fb4f7fSTejun Heo */ 7050fb4f7fSTejun Heo set_current_state(save); 71a0acae0eSTejun Heo 72a0acae0eSTejun Heo return was_frozen; 738174f150SMatt Helsley } 74a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator); 758174f150SMatt Helsley 768174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p) 778174f150SMatt Helsley { 788174f150SMatt Helsley unsigned long flags; 798174f150SMatt Helsley 808174f150SMatt Helsley spin_lock_irqsave(&p->sighand->siglock, flags); 81d6cc7685STejun Heo signal_wake_up(p, 0); 828174f150SMatt Helsley spin_unlock_irqrestore(&p->sighand->siglock, flags); 838174f150SMatt Helsley } 848174f150SMatt Helsley 858174f150SMatt Helsley /** 868174f150SMatt Helsley * freeze_task - send a freeze request to given task 878174f150SMatt Helsley * @p: task to send the request to 888174f150SMatt Helsley * @sig_only: if set, the request will only be sent if the task has the 898174f150SMatt Helsley * PF_FREEZER_NOSIG flag unset 908174f150SMatt Helsley * Return value: 'false', if @sig_only is set and the task has 918174f150SMatt Helsley * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise 928174f150SMatt Helsley * 938174f150SMatt Helsley * The freeze request is sent by setting the tasks's TIF_FREEZE flag and 948174f150SMatt Helsley * either sending a fake signal to it or waking it up, depending on whether 958174f150SMatt Helsley * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task 968174f150SMatt Helsley * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its 978174f150SMatt Helsley * TIF_FREEZE flag will not be set. 988174f150SMatt Helsley */ 998174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only) 1008174f150SMatt Helsley { 1010c9af092STejun Heo unsigned long flags; 1020c9af092STejun Heo bool ret = false; 1038174f150SMatt Helsley 1040c9af092STejun Heo spin_lock_irqsave(&freezer_lock, flags); 1050c9af092STejun Heo 10685f1d476STejun Heo if ((p->flags & PF_NOFREEZE) || 10785f1d476STejun Heo (sig_only && !should_send_signal(p))) 1080c9af092STejun Heo goto out_unlock; 1090c9af092STejun Heo 1100c9af092STejun Heo if (frozen(p)) 1110c9af092STejun Heo goto out_unlock; 1120c9af092STejun Heo 1138174f150SMatt Helsley set_freeze_flag(p); 1148174f150SMatt Helsley 1158174f150SMatt Helsley if (should_send_signal(p)) { 1168174f150SMatt Helsley fake_signal_wake_up(p); 1178cfe400cSTejun Heo /* 1188cfe400cSTejun Heo * fake_signal_wake_up() goes through p's scheduler 1198cfe400cSTejun Heo * lock and guarantees that TASK_STOPPED/TRACED -> 1208cfe400cSTejun Heo * TASK_RUNNING transition can't race with task state 1218cfe400cSTejun Heo * testing in try_to_freeze_tasks(). 1228cfe400cSTejun Heo */ 1238174f150SMatt Helsley } else { 1248174f150SMatt Helsley wake_up_state(p, TASK_INTERRUPTIBLE); 1258174f150SMatt Helsley } 1260c9af092STejun Heo ret = true; 1270c9af092STejun Heo out_unlock: 1280c9af092STejun Heo spin_unlock_irqrestore(&freezer_lock, flags); 1290c9af092STejun Heo return ret; 1308174f150SMatt Helsley } 1318174f150SMatt Helsley 132a5be2d0dSTejun Heo void __thaw_task(struct task_struct *p) 133dc52ddc0SMatt Helsley { 1340c9af092STejun Heo unsigned long flags; 135a5be2d0dSTejun Heo 1366907483bSTejun Heo /* 1376907483bSTejun Heo * Clear freezing and kick @p if FROZEN. Clearing is guaranteed to 1386907483bSTejun Heo * be visible to @p as waking up implies wmb. Waking up inside 1396907483bSTejun Heo * freezer_lock also prevents wakeups from leaking outside 1406907483bSTejun Heo * refrigerator. 141*03afed8bSTejun Heo * 142*03afed8bSTejun Heo * If !FROZEN, @p hasn't reached refrigerator, recalc sigpending to 143*03afed8bSTejun Heo * avoid leaving dangling TIF_SIGPENDING behind. 1446907483bSTejun Heo */ 1450c9af092STejun Heo spin_lock_irqsave(&freezer_lock, flags); 1460c9af092STejun Heo clear_freeze_flag(p); 147*03afed8bSTejun Heo if (frozen(p)) { 1486907483bSTejun Heo wake_up_process(p); 149*03afed8bSTejun Heo } else { 150*03afed8bSTejun Heo spin_lock(&p->sighand->siglock); 151*03afed8bSTejun Heo recalc_sigpending_and_wake(p); 152*03afed8bSTejun Heo spin_unlock(&p->sighand->siglock); 153*03afed8bSTejun Heo } 1540c9af092STejun Heo spin_unlock_irqrestore(&freezer_lock, flags); 155dc52ddc0SMatt Helsley } 156