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> 12*8a32c441STejun Heo #include <linux/kthread.h> 138174f150SMatt Helsley 148174f150SMatt Helsley /* 158174f150SMatt Helsley * freezing is complete, mark current process as frozen 168174f150SMatt Helsley */ 178174f150SMatt Helsley static inline void frozen_process(void) 188174f150SMatt Helsley { 198174f150SMatt Helsley if (!unlikely(current->flags & PF_NOFREEZE)) { 208174f150SMatt Helsley current->flags |= PF_FROZEN; 21ee940d8dSMike Frysinger smp_wmb(); 228174f150SMatt Helsley } 238174f150SMatt Helsley clear_freeze_flag(current); 248174f150SMatt Helsley } 258174f150SMatt Helsley 268174f150SMatt Helsley /* Refrigerator is place where frozen processes are stored :-). */ 27*8a32c441STejun Heo bool __refrigerator(bool check_kthr_stop) 288174f150SMatt Helsley { 298174f150SMatt Helsley /* Hmm, should we be allowed to suspend when there are realtime 308174f150SMatt Helsley processes around? */ 31a0acae0eSTejun Heo bool was_frozen = false; 328174f150SMatt Helsley long save; 338174f150SMatt Helsley 348174f150SMatt Helsley task_lock(current); 358174f150SMatt Helsley if (freezing(current)) { 368174f150SMatt Helsley frozen_process(); 378174f150SMatt Helsley task_unlock(current); 388174f150SMatt Helsley } else { 398174f150SMatt Helsley task_unlock(current); 40a0acae0eSTejun Heo return was_frozen; 418174f150SMatt Helsley } 428174f150SMatt Helsley save = current->state; 438174f150SMatt Helsley pr_debug("%s entered refrigerator\n", current->comm); 448174f150SMatt Helsley 458174f150SMatt Helsley spin_lock_irq(¤t->sighand->siglock); 468174f150SMatt Helsley recalc_sigpending(); /* We sent fake signal, clean it up */ 478174f150SMatt Helsley spin_unlock_irq(¤t->sighand->siglock); 488174f150SMatt Helsley 496301cb95SThomas Gleixner /* prevent accounting of that task to load */ 506301cb95SThomas Gleixner current->flags |= PF_FREEZING; 516301cb95SThomas Gleixner 528174f150SMatt Helsley for (;;) { 538174f150SMatt Helsley set_current_state(TASK_UNINTERRUPTIBLE); 54*8a32c441STejun Heo if (!frozen(current) || 55*8a32c441STejun Heo (check_kthr_stop && kthread_should_stop())) 568174f150SMatt Helsley break; 57a0acae0eSTejun Heo was_frozen = true; 588174f150SMatt Helsley schedule(); 598174f150SMatt Helsley } 606301cb95SThomas Gleixner 616301cb95SThomas Gleixner /* Remove the accounting blocker */ 626301cb95SThomas Gleixner current->flags &= ~PF_FREEZING; 636301cb95SThomas Gleixner 648174f150SMatt Helsley pr_debug("%s left refrigerator\n", current->comm); 6550fb4f7fSTejun Heo 6650fb4f7fSTejun Heo /* 6750fb4f7fSTejun Heo * Restore saved task state before returning. The mb'd version 6850fb4f7fSTejun Heo * needs to be used; otherwise, it might silently break 6950fb4f7fSTejun Heo * synchronization which depends on ordered task state change. 7050fb4f7fSTejun Heo */ 7150fb4f7fSTejun Heo set_current_state(save); 72a0acae0eSTejun Heo 73a0acae0eSTejun Heo return was_frozen; 748174f150SMatt Helsley } 75a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator); 768174f150SMatt Helsley 778174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p) 788174f150SMatt Helsley { 798174f150SMatt Helsley unsigned long flags; 808174f150SMatt Helsley 818174f150SMatt Helsley spin_lock_irqsave(&p->sighand->siglock, flags); 82d6cc7685STejun Heo signal_wake_up(p, 0); 838174f150SMatt Helsley spin_unlock_irqrestore(&p->sighand->siglock, flags); 848174f150SMatt Helsley } 858174f150SMatt Helsley 868174f150SMatt Helsley /** 878174f150SMatt Helsley * freeze_task - send a freeze request to given task 888174f150SMatt Helsley * @p: task to send the request to 898174f150SMatt Helsley * @sig_only: if set, the request will only be sent if the task has the 908174f150SMatt Helsley * PF_FREEZER_NOSIG flag unset 918174f150SMatt Helsley * Return value: 'false', if @sig_only is set and the task has 928174f150SMatt Helsley * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise 938174f150SMatt Helsley * 948174f150SMatt Helsley * The freeze request is sent by setting the tasks's TIF_FREEZE flag and 958174f150SMatt Helsley * either sending a fake signal to it or waking it up, depending on whether 968174f150SMatt Helsley * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task 978174f150SMatt Helsley * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its 988174f150SMatt Helsley * TIF_FREEZE flag will not be set. 998174f150SMatt Helsley */ 1008174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only) 1018174f150SMatt Helsley { 1028174f150SMatt Helsley /* 1038174f150SMatt Helsley * We first check if the task is freezing and next if it has already 1048174f150SMatt Helsley * been frozen to avoid the race with frozen_process() which first marks 1058174f150SMatt Helsley * the task as frozen and next clears its TIF_FREEZE. 1068174f150SMatt Helsley */ 1078174f150SMatt Helsley if (!freezing(p)) { 108ee940d8dSMike Frysinger smp_rmb(); 1098174f150SMatt Helsley if (frozen(p)) 1108174f150SMatt Helsley return false; 1118174f150SMatt Helsley 1128174f150SMatt Helsley if (!sig_only || should_send_signal(p)) 1138174f150SMatt Helsley set_freeze_flag(p); 1148174f150SMatt Helsley else 1158174f150SMatt Helsley return false; 1168174f150SMatt Helsley } 1178174f150SMatt Helsley 1188174f150SMatt Helsley if (should_send_signal(p)) { 1198174f150SMatt Helsley fake_signal_wake_up(p); 1208cfe400cSTejun Heo /* 1218cfe400cSTejun Heo * fake_signal_wake_up() goes through p's scheduler 1228cfe400cSTejun Heo * lock and guarantees that TASK_STOPPED/TRACED -> 1238cfe400cSTejun Heo * TASK_RUNNING transition can't race with task state 1248cfe400cSTejun Heo * testing in try_to_freeze_tasks(). 1258cfe400cSTejun Heo */ 1268174f150SMatt Helsley } else if (sig_only) { 1278174f150SMatt Helsley return false; 1288174f150SMatt Helsley } else { 1298174f150SMatt Helsley wake_up_state(p, TASK_INTERRUPTIBLE); 1308174f150SMatt Helsley } 1318174f150SMatt Helsley 1328174f150SMatt Helsley return true; 1338174f150SMatt Helsley } 1348174f150SMatt Helsley 1358174f150SMatt Helsley void cancel_freezing(struct task_struct *p) 1368174f150SMatt Helsley { 1378174f150SMatt Helsley unsigned long flags; 1388174f150SMatt Helsley 1398174f150SMatt Helsley if (freezing(p)) { 1408174f150SMatt Helsley pr_debug(" clean up: %s\n", p->comm); 1418174f150SMatt Helsley clear_freeze_flag(p); 1428174f150SMatt Helsley spin_lock_irqsave(&p->sighand->siglock, flags); 1438174f150SMatt Helsley recalc_sigpending_and_wake(p); 1448174f150SMatt Helsley spin_unlock_irqrestore(&p->sighand->siglock, flags); 1458174f150SMatt Helsley } 1468174f150SMatt Helsley } 147dc52ddc0SMatt Helsley 14800c2e63cSLi Zefan static int __thaw_process(struct task_struct *p) 149dc52ddc0SMatt Helsley { 150dc52ddc0SMatt Helsley if (frozen(p)) { 151dc52ddc0SMatt Helsley p->flags &= ~PF_FROZEN; 152dc52ddc0SMatt Helsley return 1; 153dc52ddc0SMatt Helsley } 154dc52ddc0SMatt Helsley clear_freeze_flag(p); 155dc52ddc0SMatt Helsley return 0; 156dc52ddc0SMatt Helsley } 157dc52ddc0SMatt Helsley 15800c2e63cSLi Zefan /* 15900c2e63cSLi Zefan * Wake up a frozen process 16000c2e63cSLi Zefan * 16100c2e63cSLi Zefan * task_lock() is needed to prevent the race with refrigerator() which may 16200c2e63cSLi Zefan * occur if the freezing of tasks fails. Namely, without the lock, if the 16300c2e63cSLi Zefan * freezing of tasks failed, thaw_tasks() might have run before a task in 16400c2e63cSLi Zefan * refrigerator() could call frozen_process(), in which case the task would be 16500c2e63cSLi Zefan * frozen and no one would thaw it. 16600c2e63cSLi Zefan */ 167dc52ddc0SMatt Helsley int thaw_process(struct task_struct *p) 168dc52ddc0SMatt Helsley { 169dc52ddc0SMatt Helsley task_lock(p); 170dc52ddc0SMatt Helsley if (__thaw_process(p) == 1) { 171dc52ddc0SMatt Helsley task_unlock(p); 172dc52ddc0SMatt Helsley wake_up_process(p); 173dc52ddc0SMatt Helsley return 1; 174dc52ddc0SMatt Helsley } 175dc52ddc0SMatt Helsley task_unlock(p); 176dc52ddc0SMatt Helsley return 0; 177dc52ddc0SMatt Helsley } 178dc52ddc0SMatt Helsley EXPORT_SYMBOL(thaw_process); 179