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(¤t->sighand->siglock); 398174f150SMatt Helsley recalc_sigpending(); /* We sent fake signal, clean it up */ 408174f150SMatt Helsley spin_unlock_irq(¤t->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