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 14a3201227STejun Heo /* total number of freezing conditions in effect */ 15a3201227STejun Heo atomic_t system_freezing_cnt = ATOMIC_INIT(0); 16a3201227STejun Heo EXPORT_SYMBOL(system_freezing_cnt); 17a3201227STejun Heo 18a3201227STejun Heo /* indicate whether PM freezing is in effect, protected by pm_mutex */ 19a3201227STejun Heo bool pm_freezing; 20a3201227STejun Heo bool pm_nosig_freezing; 21a3201227STejun Heo 220c9af092STejun Heo /* protects freezing and frozen transitions */ 230c9af092STejun Heo static DEFINE_SPINLOCK(freezer_lock); 248174f150SMatt Helsley 25a3201227STejun Heo /** 26a3201227STejun Heo * freezing_slow_path - slow path for testing whether a task needs to be frozen 27a3201227STejun Heo * @p: task to be tested 28a3201227STejun Heo * 29a3201227STejun Heo * This function is called by freezing() if system_freezing_cnt isn't zero 30a3201227STejun Heo * and tests whether @p needs to enter and stay in frozen state. Can be 31a3201227STejun Heo * called under any context. The freezers are responsible for ensuring the 32a3201227STejun Heo * target tasks see the updated state. 33a3201227STejun Heo */ 34a3201227STejun Heo bool freezing_slow_path(struct task_struct *p) 35a3201227STejun Heo { 36a3201227STejun Heo if (p->flags & PF_NOFREEZE) 37a3201227STejun Heo return false; 38a3201227STejun Heo 39a3201227STejun Heo if (pm_nosig_freezing || cgroup_freezing(p)) 40a3201227STejun Heo return true; 41a3201227STejun Heo 42a3201227STejun Heo if (pm_freezing && !(p->flags & PF_FREEZER_NOSIG)) 43a3201227STejun Heo return true; 44a3201227STejun Heo 45a3201227STejun Heo return false; 46a3201227STejun Heo } 47a3201227STejun Heo EXPORT_SYMBOL(freezing_slow_path); 48a3201227STejun Heo 498174f150SMatt Helsley /* Refrigerator is place where frozen processes are stored :-). */ 508a32c441STejun Heo bool __refrigerator(bool check_kthr_stop) 518174f150SMatt Helsley { 528174f150SMatt Helsley /* Hmm, should we be allowed to suspend when there are realtime 538174f150SMatt Helsley processes around? */ 54a0acae0eSTejun Heo bool was_frozen = false; 55*5ece3eaeSTejun Heo long save = current->state; 568174f150SMatt Helsley 578174f150SMatt Helsley pr_debug("%s entered refrigerator\n", current->comm); 588174f150SMatt Helsley 598174f150SMatt Helsley for (;;) { 608174f150SMatt Helsley set_current_state(TASK_UNINTERRUPTIBLE); 61*5ece3eaeSTejun Heo 62*5ece3eaeSTejun Heo spin_lock_irq(&freezer_lock); 63*5ece3eaeSTejun Heo current->flags |= PF_FROZEN; 646907483bSTejun Heo if (!freezing(current) || 658a32c441STejun Heo (check_kthr_stop && kthread_should_stop())) 66*5ece3eaeSTejun Heo current->flags &= ~PF_FROZEN; 67*5ece3eaeSTejun Heo spin_unlock_irq(&freezer_lock); 68*5ece3eaeSTejun Heo 69*5ece3eaeSTejun Heo if (!(current->flags & PF_FROZEN)) 708174f150SMatt Helsley break; 71a0acae0eSTejun Heo was_frozen = true; 728174f150SMatt Helsley schedule(); 738174f150SMatt Helsley } 746301cb95SThomas Gleixner 75*5ece3eaeSTejun Heo spin_lock_irq(¤t->sighand->siglock); 76*5ece3eaeSTejun Heo recalc_sigpending(); /* We sent fake signal, clean it up */ 77*5ece3eaeSTejun Heo spin_unlock_irq(¤t->sighand->siglock); 786907483bSTejun Heo 798174f150SMatt Helsley pr_debug("%s left refrigerator\n", current->comm); 8050fb4f7fSTejun Heo 8150fb4f7fSTejun Heo /* 8250fb4f7fSTejun Heo * Restore saved task state before returning. The mb'd version 8350fb4f7fSTejun Heo * needs to be used; otherwise, it might silently break 8450fb4f7fSTejun Heo * synchronization which depends on ordered task state change. 8550fb4f7fSTejun Heo */ 8650fb4f7fSTejun Heo set_current_state(save); 87a0acae0eSTejun Heo 88a0acae0eSTejun Heo return was_frozen; 898174f150SMatt Helsley } 90a0acae0eSTejun Heo EXPORT_SYMBOL(__refrigerator); 918174f150SMatt Helsley 928174f150SMatt Helsley static void fake_signal_wake_up(struct task_struct *p) 938174f150SMatt Helsley { 948174f150SMatt Helsley unsigned long flags; 958174f150SMatt Helsley 968174f150SMatt Helsley spin_lock_irqsave(&p->sighand->siglock, flags); 97d6cc7685STejun Heo signal_wake_up(p, 0); 988174f150SMatt Helsley spin_unlock_irqrestore(&p->sighand->siglock, flags); 998174f150SMatt Helsley } 1008174f150SMatt Helsley 1018174f150SMatt Helsley /** 1028174f150SMatt Helsley * freeze_task - send a freeze request to given task 1038174f150SMatt Helsley * @p: task to send the request to 1048174f150SMatt Helsley * @sig_only: if set, the request will only be sent if the task has the 1058174f150SMatt Helsley * PF_FREEZER_NOSIG flag unset 1068174f150SMatt Helsley * Return value: 'false', if @sig_only is set and the task has 1078174f150SMatt Helsley * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise 1088174f150SMatt Helsley * 1098174f150SMatt Helsley * The freeze request is sent by setting the tasks's TIF_FREEZE flag and 1108174f150SMatt Helsley * either sending a fake signal to it or waking it up, depending on whether 1118174f150SMatt Helsley * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task 1128174f150SMatt Helsley * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its 1138174f150SMatt Helsley * TIF_FREEZE flag will not be set. 1148174f150SMatt Helsley */ 1158174f150SMatt Helsley bool freeze_task(struct task_struct *p, bool sig_only) 1168174f150SMatt Helsley { 1170c9af092STejun Heo unsigned long flags; 1188174f150SMatt Helsley 1190c9af092STejun Heo spin_lock_irqsave(&freezer_lock, flags); 120a3201227STejun Heo if (!freezing(p) || frozen(p)) { 121a3201227STejun Heo spin_unlock_irqrestore(&freezer_lock, flags); 122a3201227STejun Heo return false; 123a3201227STejun Heo } 1248174f150SMatt Helsley 125948246f7STejun Heo if (!(p->flags & PF_FREEZER_NOSIG)) { 1268174f150SMatt Helsley fake_signal_wake_up(p); 1278cfe400cSTejun Heo /* 1288cfe400cSTejun Heo * fake_signal_wake_up() goes through p's scheduler 1298cfe400cSTejun Heo * lock and guarantees that TASK_STOPPED/TRACED -> 1308cfe400cSTejun Heo * TASK_RUNNING transition can't race with task state 1318cfe400cSTejun Heo * testing in try_to_freeze_tasks(). 1328cfe400cSTejun Heo */ 1338174f150SMatt Helsley } else { 1348174f150SMatt Helsley wake_up_state(p, TASK_INTERRUPTIBLE); 1358174f150SMatt Helsley } 136a3201227STejun Heo 1370c9af092STejun Heo spin_unlock_irqrestore(&freezer_lock, flags); 138a3201227STejun Heo return true; 1398174f150SMatt Helsley } 1408174f150SMatt Helsley 141a5be2d0dSTejun Heo void __thaw_task(struct task_struct *p) 142dc52ddc0SMatt Helsley { 1430c9af092STejun Heo unsigned long flags; 144a5be2d0dSTejun Heo 1456907483bSTejun Heo /* 1466907483bSTejun Heo * Clear freezing and kick @p if FROZEN. Clearing is guaranteed to 1476907483bSTejun Heo * be visible to @p as waking up implies wmb. Waking up inside 1486907483bSTejun Heo * freezer_lock also prevents wakeups from leaking outside 1496907483bSTejun Heo * refrigerator. 15003afed8bSTejun Heo * 15103afed8bSTejun Heo * If !FROZEN, @p hasn't reached refrigerator, recalc sigpending to 15203afed8bSTejun Heo * avoid leaving dangling TIF_SIGPENDING behind. 1536907483bSTejun Heo */ 1540c9af092STejun Heo spin_lock_irqsave(&freezer_lock, flags); 15503afed8bSTejun Heo if (frozen(p)) { 1566907483bSTejun Heo wake_up_process(p); 15703afed8bSTejun Heo } else { 15803afed8bSTejun Heo spin_lock(&p->sighand->siglock); 15903afed8bSTejun Heo recalc_sigpending_and_wake(p); 16003afed8bSTejun Heo spin_unlock(&p->sighand->siglock); 16103afed8bSTejun Heo } 1620c9af092STejun Heo spin_unlock_irqrestore(&freezer_lock, flags); 163dc52ddc0SMatt Helsley } 16496ee6d85STejun Heo 16596ee6d85STejun Heo /** 16696ee6d85STejun Heo * __set_freezable - make %current freezable 16796ee6d85STejun Heo * @with_signal: do we want %TIF_SIGPENDING for notification too? 16896ee6d85STejun Heo * 16996ee6d85STejun Heo * Mark %current freezable and enter refrigerator if necessary. 17096ee6d85STejun Heo */ 17196ee6d85STejun Heo bool __set_freezable(bool with_signal) 17296ee6d85STejun Heo { 17396ee6d85STejun Heo might_sleep(); 17496ee6d85STejun Heo 17596ee6d85STejun Heo /* 17696ee6d85STejun Heo * Modify flags while holding freezer_lock. This ensures the 17796ee6d85STejun Heo * freezer notices that we aren't frozen yet or the freezing 17896ee6d85STejun Heo * condition is visible to try_to_freeze() below. 17996ee6d85STejun Heo */ 18096ee6d85STejun Heo spin_lock_irq(&freezer_lock); 18196ee6d85STejun Heo current->flags &= ~PF_NOFREEZE; 18296ee6d85STejun Heo if (with_signal) 18396ee6d85STejun Heo current->flags &= ~PF_FREEZER_NOSIG; 18496ee6d85STejun Heo spin_unlock_irq(&freezer_lock); 18596ee6d85STejun Heo 18696ee6d85STejun Heo return try_to_freeze(); 18796ee6d85STejun Heo } 18896ee6d85STejun Heo EXPORT_SYMBOL(__set_freezable); 189