extern int freeze_kernel_threads(void);
extern void thaw_processes(void);
- static inline int try_to_freeze(void)
+ static inline bool try_to_freeze(void)
{
- if (freezing(current)) {
- refrigerator();
- return 1;
- } else
- return 0;
+ might_sleep();
+ if (likely(!freezing(current)))
+ return false;
+ return __refrigerator(false);
}
- extern bool freeze_task(struct task_struct *p, bool sig_only);
- extern void cancel_freezing(struct task_struct *p);
+ extern bool freeze_task(struct task_struct *p);
-extern bool set_freezable(void);
++extern bool __set_freezable(bool with_signal);
#ifdef CONFIG_CGROUP_FREEZER
- extern int cgroup_freezing_or_frozen(struct task_struct *task);
+ extern bool cgroup_freezing(struct task_struct *task);
#else /* !CONFIG_CGROUP_FREEZER */
- static inline int cgroup_freezing_or_frozen(struct task_struct *task)
+ static inline bool cgroup_freezing(struct task_struct *task)
{
- return 0;
+ return false;
}
#endif /* !CONFIG_CGROUP_FREEZER */
return !!(p->flags & PF_FREEZER_SKIP);
}
- static inline void set_freezable(void)
+/*
+ * Tell the freezer that the current task should be frozen by it
+ */
- current->flags &= ~PF_NOFREEZE;
++static inline bool set_freezable(void)
+{
- static inline void set_freezable_with_signal(void)
++ return __set_freezable(false);
+}
+
+/*
+ * Tell the freezer that the current task should be frozen by it and that it
+ * should send a fake signal to the task to freeze it.
+ */
- current->flags &= ~(PF_NOFREEZE | PF_FREEZER_NOSIG);
++static inline bool set_freezable_with_signal(void)
+{
++ return __set_freezable(true);
+}
+
/*
* Freezer-friendly wrappers around wait_event_interruptible(),
* wait_event_killable() and wait_event_interruptible_timeout(), originally
__retval = wait_event_interruptible_timeout(wq, \
(condition) || freezing(current), \
__retval); \
- } while (try_to_freeze()); \
+ if (__retval <= 0 || (condition)) \
+ break; \
+ try_to_freeze(); \
+ } \
__retval; \
})
+
#else /* !CONFIG_FREEZER */
- static inline int frozen(struct task_struct *p) { return 0; }
- static inline int freezing(struct task_struct *p) { return 0; }
- static inline void set_freeze_flag(struct task_struct *p) {}
- static inline void clear_freeze_flag(struct task_struct *p) {}
- static inline int thaw_process(struct task_struct *p) { return 1; }
+ static inline bool frozen(struct task_struct *p) { return false; }
+ static inline bool freezing(struct task_struct *p) { return false; }
++static inline void __thaw_task(struct task_struct *t) {}
- static inline void refrigerator(void) {}
+ static inline bool __refrigerator(bool check_kthr_stop) { return false; }
static inline int freeze_processes(void) { return -ENOSYS; }
static inline int freeze_kernel_threads(void) { return -ENOSYS; }
static inline void thaw_processes(void) {}
#include <linux/export.h>
#include <linux/syscalls.h>
#include <linux/freezer.h>
+ #include <linux/kthread.h>
- /*
- * freezing is complete, mark current process as frozen
+ /* total number of freezing conditions in effect */
+ atomic_t system_freezing_cnt = ATOMIC_INIT(0);
+ EXPORT_SYMBOL(system_freezing_cnt);
+
+ /* indicate whether PM freezing is in effect, protected by pm_mutex */
+ bool pm_freezing;
+ bool pm_nosig_freezing;
+
+ /* protects freezing and frozen transitions */
+ static DEFINE_SPINLOCK(freezer_lock);
+
+ /**
+ * freezing_slow_path - slow path for testing whether a task needs to be frozen
+ * @p: task to be tested
+ *
+ * This function is called by freezing() if system_freezing_cnt isn't zero
+ * and tests whether @p needs to enter and stay in frozen state. Can be
+ * called under any context. The freezers are responsible for ensuring the
+ * target tasks see the updated state.
*/
- static inline void frozen_process(void)
+ bool freezing_slow_path(struct task_struct *p)
{
- if (!unlikely(current->flags & PF_NOFREEZE)) {
- current->flags |= PF_FROZEN;
- smp_wmb();
- }
- clear_freeze_flag(current);
+ if (p->flags & PF_NOFREEZE)
+ return false;
+
+ if (pm_nosig_freezing || cgroup_freezing(p))
+ return true;
+
- if (pm_freezing && !(p->flags & PF_KTHREAD))
++ if (pm_freezing && !(p->flags & PF_FREEZER_NOSIG))
+ return true;
+
+ return false;
}
+ EXPORT_SYMBOL(freezing_slow_path);
/* Refrigerator is place where frozen processes are stored :-). */
- void refrigerator(void)
+ bool __refrigerator(bool check_kthr_stop)
{
/* Hmm, should we be allowed to suspend when there are realtime
processes around? */
schedule();
}
- /* Remove the accounting blocker */
- current->flags &= ~PF_FREEZING;
++ spin_lock_irq(¤t->sighand->siglock);
++ recalc_sigpending(); /* We sent fake signal, clean it up */
++ spin_unlock_irq(¤t->sighand->siglock);
+
pr_debug("%s left refrigerator\n", current->comm);
- __set_current_state(save);
+
+ /*
+ * Restore saved task state before returning. The mb'd version
+ * needs to be used; otherwise, it might silently break
+ * synchronization which depends on ordered task state change.
+ */
+ set_current_state(save);
+
+ return was_frozen;
}
- EXPORT_SYMBOL(refrigerator);
+ EXPORT_SYMBOL(__refrigerator);
static void fake_signal_wake_up(struct task_struct *p)
{
}
/**
- * freeze_task - send a freeze request to given task
- * @p: task to send the request to
- * @sig_only: if set, the request will only be sent if the task has the
- * PF_FREEZER_NOSIG flag unset
- * Return value: 'false', if @sig_only is set and the task has
- * PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
+ * freeze_task - send a freeze request to given task
+ * @p: task to send the request to
*
- * The freeze request is sent by setting the tasks's TIF_FREEZE flag and
- * either sending a fake signal to it or waking it up, depending on whether
- * or not it has PF_FREEZER_NOSIG set. If @sig_only is set and the task
- * has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
- * TIF_FREEZE flag will not be set.
+ * If @p is freezing, the freeze request is sent by setting %TIF_FREEZE
+ * flag and either sending a fake signal to it or waking it up, depending
+ * on whether it has %PF_FREEZER_NOSIG set.
+ *
+ * RETURNS:
+ * %false, if @p is not freezing or already frozen; %true, otherwise
*/
- bool freeze_task(struct task_struct *p, bool sig_only)
+ bool freeze_task(struct task_struct *p)
{
- /*
- * We first check if the task is freezing and next if it has already
- * been frozen to avoid the race with frozen_process() which first marks
- * the task as frozen and next clears its TIF_FREEZE.
- */
- if (!freezing(p)) {
- smp_rmb();
- if (frozen(p))
- return false;
-
- if (!sig_only || should_send_signal(p))
- set_freeze_flag(p);
- else
- return false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&freezer_lock, flags);
+ if (!freezing(p) || frozen(p)) {
+ spin_unlock_irqrestore(&freezer_lock, flags);
+ return false;
}
- if (should_send_signal(p)) {
- if (!(p->flags & PF_KTHREAD)) {
++ if (!(p->flags & PF_FREEZER_NOSIG)) {
fake_signal_wake_up(p);
/*
* fake_signal_wake_up() goes through p's scheduler
{
unsigned long flags;
- if (freezing(p)) {
- pr_debug(" clean up: %s\n", p->comm);
- clear_freeze_flag(p);
- spin_lock_irqsave(&p->sighand->siglock, flags);
- recalc_sigpending_and_wake(p);
- spin_unlock_irqrestore(&p->sighand->siglock, flags);
- }
- }
-
- static int __thaw_process(struct task_struct *p)
- {
+ /*
+ * Clear freezing and kick @p if FROZEN. Clearing is guaranteed to
+ * be visible to @p as waking up implies wmb. Waking up inside
+ * freezer_lock also prevents wakeups from leaking outside
+ * refrigerator.
++ *
++ * If !FROZEN, @p hasn't reached refrigerator, recalc sigpending to
++ * avoid leaving dangling TIF_SIGPENDING behind.
+ */
+ spin_lock_irqsave(&freezer_lock, flags);
- if (frozen(p))
+ if (frozen(p)) {
- p->flags &= ~PF_FROZEN;
- return 1;
+ wake_up_process(p);
++ } else {
++ spin_lock(&p->sighand->siglock);
++ recalc_sigpending_and_wake(p);
++ spin_unlock(&p->sighand->siglock);
+ }
- clear_freeze_flag(p);
- return 0;
+ spin_unlock_irqrestore(&freezer_lock, flags);
}
- /*
- * Wake up a frozen process
+ /**
- * set_freezable - make %current freezable
++ * __set_freezable - make %current freezable
++ * @with_signal: do we want %TIF_SIGPENDING for notification too?
*
- * task_lock() is needed to prevent the race with refrigerator() which may
- * occur if the freezing of tasks fails. Namely, without the lock, if the
- * freezing of tasks failed, thaw_tasks() might have run before a task in
- * refrigerator() could call frozen_process(), in which case the task would be
- * frozen and no one would thaw it.
+ * Mark %current freezable and enter refrigerator if necessary.
*/
- int thaw_process(struct task_struct *p)
-bool set_freezable(void)
++bool __set_freezable(bool with_signal)
{
- task_lock(p);
- if (__thaw_process(p) == 1) {
- task_unlock(p);
- wake_up_process(p);
- return 1;
- }
- task_unlock(p);
- return 0;
+ might_sleep();
+
+ /*
+ * Modify flags while holding freezer_lock. This ensures the
+ * freezer notices that we aren't frozen yet or the freezing
+ * condition is visible to try_to_freeze() below.
+ */
+ spin_lock_irq(&freezer_lock);
+ current->flags &= ~PF_NOFREEZE;
++ if (with_signal)
++ current->flags &= ~PF_FREEZER_NOSIG;
+ spin_unlock_irq(&freezer_lock);
+
+ return try_to_freeze();
}
- EXPORT_SYMBOL(thaw_process);
-EXPORT_SYMBOL(set_freezable);
++EXPORT_SYMBOL(__set_freezable);