Merge branch 'pm-freezer' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc into pm-freezer

* 'pm-freezer' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc: (24 commits)
  freezer: fix wait_event_freezable/__thaw_task races
  freezer: kill unused set_freezable_with_signal()
  dmatest: don't use set_freezable_with_signal()
  usb_storage: don't use set_freezable_with_signal()
  freezer: remove unused @sig_only from freeze_task()
  freezer: use lock_task_sighand() in fake_signal_wake_up()
  freezer: restructure __refrigerator()
  freezer: fix set_freezable[_with_signal]() race
  freezer: remove should_send_signal() and update frozen()
  freezer: remove now unused TIF_FREEZE
  freezer: make freezing() test freeze conditions in effect instead of TIF_FREEZE
  cgroup_freezer: prepare for removal of TIF_FREEZE
  freezer: clean up freeze_processes() failure path
  freezer: kill PF_FREEZING
  freezer: test freezable conditions while holding freezer_lock
  freezer: make freezing indicate freeze condition in effect
  freezer: use dedicated lock instead of task_lock() + memory barrier
  freezer: don't distinguish nosig tasks on thaw
  freezer: remove racy clear_freeze_flag() and set PF_NOFREEZE on dead tasks
  freezer: rename thaw_process() to __thaw_task() and simplify the implementation
  ...
This commit is contained in:
Rafael J. Wysocki
2011-11-23 21:09:02 +01:00
57 changed files with 310 additions and 408 deletions

View File

@@ -48,19 +48,17 @@ static inline struct freezer *task_freezer(struct task_struct *task)
struct freezer, css);
}
static inline int __cgroup_freezing_or_frozen(struct task_struct *task)
bool cgroup_freezing(struct task_struct *task)
{
enum freezer_state state = task_freezer(task)->state;
return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
}
enum freezer_state state;
bool ret;
int cgroup_freezing_or_frozen(struct task_struct *task)
{
int result;
task_lock(task);
result = __cgroup_freezing_or_frozen(task);
task_unlock(task);
return result;
rcu_read_lock();
state = task_freezer(task)->state;
ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
rcu_read_unlock();
return ret;
}
/*
@@ -102,9 +100,6 @@ struct cgroup_subsys freezer_subsys;
* freezer_can_attach():
* cgroup_mutex (held by caller of can_attach)
*
* cgroup_freezing_or_frozen():
* task->alloc_lock (to get task's cgroup)
*
* freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
* freezer->lock
* sighand->siglock (if the cgroup is freezing)
@@ -130,7 +125,7 @@ struct cgroup_subsys freezer_subsys;
* write_lock css_set_lock (cgroup iterator start)
* task->alloc_lock
* read_lock css_set_lock (cgroup iterator start)
* task->alloc_lock (inside thaw_process(), prevents race with refrigerator())
* task->alloc_lock (inside __thaw_task(), prevents race with refrigerator())
* sighand->siglock
*/
static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
@@ -150,7 +145,11 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
static void freezer_destroy(struct cgroup_subsys *ss,
struct cgroup *cgroup)
{
kfree(cgroup_freezer(cgroup));
struct freezer *freezer = cgroup_freezer(cgroup);
if (freezer->state != CGROUP_THAWED)
atomic_dec(&system_freezing_cnt);
kfree(freezer);
}
/*
@@ -177,13 +176,7 @@ static int freezer_can_attach(struct cgroup_subsys *ss,
static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
rcu_read_lock();
if (__cgroup_freezing_or_frozen(tsk)) {
rcu_read_unlock();
return -EBUSY;
}
rcu_read_unlock();
return 0;
return cgroup_freezing(tsk) ? -EBUSY : 0;
}
static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
@@ -213,7 +206,7 @@ static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
/* Locking avoids race with FREEZING -> THAWED transitions. */
if (freezer->state == CGROUP_FREEZING)
freeze_task(task, true);
freeze_task(task);
spin_unlock_irq(&freezer->lock);
}
@@ -231,7 +224,7 @@ static void update_if_frozen(struct cgroup *cgroup,
cgroup_iter_start(cgroup, &it);
while ((task = cgroup_iter_next(cgroup, &it))) {
ntotal++;
if (frozen(task))
if (freezing(task) && frozen(task))
nfrozen++;
}
@@ -279,10 +272,9 @@ static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
struct task_struct *task;
unsigned int num_cant_freeze_now = 0;
freezer->state = CGROUP_FREEZING;
cgroup_iter_start(cgroup, &it);
while ((task = cgroup_iter_next(cgroup, &it))) {
if (!freeze_task(task, true))
if (!freeze_task(task))
continue;
if (frozen(task))
continue;
@@ -300,12 +292,9 @@ static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
struct task_struct *task;
cgroup_iter_start(cgroup, &it);
while ((task = cgroup_iter_next(cgroup, &it))) {
thaw_process(task);
}
while ((task = cgroup_iter_next(cgroup, &it)))
__thaw_task(task);
cgroup_iter_end(cgroup, &it);
freezer->state = CGROUP_THAWED;
}
static int freezer_change_state(struct cgroup *cgroup,
@@ -319,20 +308,24 @@ static int freezer_change_state(struct cgroup *cgroup,
spin_lock_irq(&freezer->lock);
update_if_frozen(cgroup, freezer);
if (goal_state == freezer->state)
goto out;
switch (goal_state) {
case CGROUP_THAWED:
if (freezer->state != CGROUP_THAWED)
atomic_dec(&system_freezing_cnt);
freezer->state = CGROUP_THAWED;
unfreeze_cgroup(cgroup, freezer);
break;
case CGROUP_FROZEN:
if (freezer->state == CGROUP_THAWED)
atomic_inc(&system_freezing_cnt);
freezer->state = CGROUP_FREEZING;
retval = try_to_freeze_cgroup(cgroup, freezer);
break;
default:
BUG();
}
out:
spin_unlock_irq(&freezer->lock);
return retval;

View File

@@ -679,8 +679,6 @@ static void exit_mm(struct task_struct * tsk)
tsk->mm = NULL;
up_read(&mm->mmap_sem);
enter_lazy_tlb(mm, current);
/* We don't want this task to be frozen prematurely */
clear_freeze_flag(tsk);
task_unlock(tsk);
mm_update_next_owner(mm);
mmput(mm);
@@ -1040,6 +1038,7 @@ NORET_TYPE void do_exit(long code)
exit_rcu();
/* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD;
tsk->flags |= PF_NOFREEZE; /* tell freezer to ignore us */
schedule();
BUG();
/* Avoid "noreturn function does return". */

View File

@@ -992,7 +992,6 @@ static void copy_flags(unsigned long clone_flags, struct task_struct *p)
new_flags |= PF_FORKNOEXEC;
new_flags |= PF_STARTING;
p->flags = new_flags;
clear_freeze_flag(p);
}
SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)

View File

@@ -9,101 +9,114 @@
#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))
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? */
long save;
bool was_frozen = false;
long save = current->state;
task_lock(current);
if (freezing(current)) {
frozen_process();
task_unlock(current);
} else {
task_unlock(current);
return;
}
save = current->state;
pr_debug("%s entered refrigerator\n", current->comm);
spin_lock_irq(&current->sighand->siglock);
recalc_sigpending(); /* We sent fake signal, clean it up */
spin_unlock_irq(&current->sighand->siglock);
/* prevent accounting of that task to load */
current->flags |= PF_FREEZING;
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!frozen(current))
spin_lock_irq(&freezer_lock);
current->flags |= PF_FROZEN;
if (!freezing(current) ||
(check_kthr_stop && kthread_should_stop()))
current->flags &= ~PF_FROZEN;
spin_unlock_irq(&freezer_lock);
if (!(current->flags & PF_FROZEN))
break;
was_frozen = true;
schedule();
}
/* Remove the accounting blocker */
current->flags &= ~PF_FREEZING;
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)
{
unsigned long flags;
spin_lock_irqsave(&p->sighand->siglock, flags);
signal_wake_up(p, 0);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
if (lock_task_sighand(p, &flags)) {
signal_wake_up(p, 0);
unlock_task_sighand(p, &flags);
}
}
/**
* 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;
unsigned long flags;
if (!sig_only || should_send_signal(p))
set_freeze_flag(p);
else
return false;
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)) {
fake_signal_wake_up(p);
/*
* fake_signal_wake_up() goes through p's scheduler
@@ -111,56 +124,48 @@ bool freeze_task(struct task_struct *p, bool sig_only)
* TASK_RUNNING transition can't race with task state
* testing in try_to_freeze_tasks().
*/
} else if (sig_only) {
return false;
} else {
wake_up_state(p, TASK_INTERRUPTIBLE);
}
spin_unlock_irqrestore(&freezer_lock, flags);
return true;
}
void cancel_freezing(struct task_struct *p)
void __thaw_task(struct task_struct *p)
{
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)
{
if (frozen(p)) {
p->flags &= ~PF_FROZEN;
return 1;
}
clear_freeze_flag(p);
return 0;
}
/*
* Wake up a frozen process
*
* 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.
*/
int thaw_process(struct task_struct *p)
{
task_lock(p);
if (__thaw_process(p) == 1) {
task_unlock(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.
*/
spin_lock_irqsave(&freezer_lock, flags);
if (frozen(p))
wake_up_process(p);
return 1;
}
task_unlock(p);
return 0;
spin_unlock_irqrestore(&freezer_lock, flags);
}
EXPORT_SYMBOL(thaw_process);
/**
* set_freezable - make %current freezable
*
* Mark %current freezable and enter refrigerator if necessary.
*/
bool set_freezable(void)
{
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;
spin_unlock_irq(&freezer_lock);
return try_to_freeze();
}
EXPORT_SYMBOL(set_freezable);

View File

@@ -58,6 +58,31 @@ int kthread_should_stop(void)
}
EXPORT_SYMBOL(kthread_should_stop);
/**
* kthread_freezable_should_stop - should this freezable kthread return now?
* @was_frozen: optional out parameter, indicates whether %current was frozen
*
* kthread_should_stop() for freezable kthreads, which will enter
* refrigerator if necessary. This function is safe from kthread_stop() /
* freezer deadlock and freezable kthreads should use this function instead
* of calling try_to_freeze() directly.
*/
bool kthread_freezable_should_stop(bool *was_frozen)
{
bool frozen = false;
might_sleep();
if (unlikely(freezing(current)))
frozen = __refrigerator(true);
if (was_frozen)
*was_frozen = frozen;
return kthread_should_stop();
}
EXPORT_SYMBOL_GPL(kthread_freezable_should_stop);
/**
* kthread_data - return data value specified on kthread creation
* @task: kthread task in question
@@ -257,7 +282,7 @@ int kthreadd(void *unused)
set_cpus_allowed_ptr(tsk, cpu_all_mask);
set_mems_allowed(node_states[N_HIGH_MEMORY]);
current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
current->flags |= PF_NOFREEZE;
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);

View File

@@ -611,17 +611,6 @@ static void power_down(void)
while(1);
}
static int prepare_processes(void)
{
int error = 0;
if (freeze_processes()) {
error = -EBUSY;
thaw_processes();
}
return error;
}
/**
* hibernate - Carry out system hibernation, including saving the image.
*/
@@ -654,7 +643,7 @@ int hibernate(void)
sys_sync();
printk("done.\n");
error = prepare_processes();
error = freeze_processes();
if (error)
goto Finish;
@@ -815,7 +804,7 @@ static int software_resume(void)
goto close_finish;
pr_debug("PM: Preparing processes for restore.\n");
error = prepare_processes();
error = freeze_processes();
if (error) {
swsusp_close(FMODE_READ);
goto Done;

View File

@@ -22,16 +22,7 @@
*/
#define TIMEOUT (20 * HZ)
static inline int freezable(struct task_struct * p)
{
if ((p == current) ||
(p->flags & PF_NOFREEZE) ||
(p->exit_state != 0))
return 0;
return 1;
}
static int try_to_freeze_tasks(bool sig_only)
static int try_to_freeze_tasks(bool user_only)
{
struct task_struct *g, *p;
unsigned long end_time;
@@ -46,17 +37,14 @@ static int try_to_freeze_tasks(bool sig_only)
end_time = jiffies + TIMEOUT;
if (!sig_only)
if (!user_only)
freeze_workqueues_begin();
while (true) {
todo = 0;
read_lock(&tasklist_lock);
do_each_thread(g, p) {
if (frozen(p) || !freezable(p))
continue;
if (!freeze_task(p, sig_only))
if (p == current || !freeze_task(p))
continue;
/*
@@ -77,7 +65,7 @@ static int try_to_freeze_tasks(bool sig_only)
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
if (!sig_only) {
if (!user_only) {
wq_busy = freeze_workqueues_busy();
todo += wq_busy;
}
@@ -103,11 +91,6 @@ static int try_to_freeze_tasks(bool sig_only)
elapsed_csecs = elapsed_csecs64;
if (todo) {
/* This does not unfreeze processes that are already frozen
* (we have slightly ugly calling convention in that respect,
* and caller must call thaw_processes() if something fails),
* but it cleans up leftover PF_FREEZE requests.
*/
printk("\n");
printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds "
"(%d tasks refusing to freeze, wq_busy=%d):\n",
@@ -115,15 +98,11 @@ static int try_to_freeze_tasks(bool sig_only)
elapsed_csecs / 100, elapsed_csecs % 100,
todo - wq_busy, wq_busy);
thaw_workqueues();
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);
if (!wakeup && freezing(p) && !freezer_should_skip(p))
if (!wakeup && !freezer_should_skip(p) &&
p != current && freezing(p) && !frozen(p))
sched_show_task(p);
cancel_freezing(p);
task_unlock(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
} else {
@@ -136,12 +115,18 @@ static int try_to_freeze_tasks(bool sig_only)
/**
* freeze_processes - Signal user space processes to enter the refrigerator.
*
* On success, returns 0. On failure, -errno and system is fully thawed.
*/
int freeze_processes(void)
{
int error;
if (!pm_freezing)
atomic_inc(&system_freezing_cnt);
printk("Freezing user space processes ... ");
pm_freezing = true;
error = try_to_freeze_tasks(true);
if (!error) {
printk("done.");
@@ -150,17 +135,22 @@ int freeze_processes(void)
printk("\n");
BUG_ON(in_atomic());
if (error)
thaw_processes();
return error;
}
/**
* freeze_kernel_threads - Make freezable kernel threads go to the refrigerator.
*
* On success, returns 0. On failure, -errno and system is fully thawed.
*/
int freeze_kernel_threads(void)
{
int error;
printk("Freezing remaining freezable tasks ... ");
pm_nosig_freezing = true;
error = try_to_freeze_tasks(false);
if (!error)
printk("done.");
@@ -168,37 +158,32 @@ int freeze_kernel_threads(void)
printk("\n");
BUG_ON(in_atomic());
if (error)
thaw_processes();
return error;
}
static void thaw_tasks(bool nosig_only)
{
struct task_struct *g, *p;
read_lock(&tasklist_lock);
do_each_thread(g, p) {
if (!freezable(p))
continue;
if (nosig_only && should_send_signal(p))
continue;
if (cgroup_freezing_or_frozen(p))
continue;
thaw_process(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
}
void thaw_processes(void)
{
struct task_struct *g, *p;
if (pm_freezing)
atomic_dec(&system_freezing_cnt);
pm_freezing = false;
pm_nosig_freezing = false;
oom_killer_enable();
printk("Restarting tasks ... ");
thaw_workqueues();
thaw_tasks(true);
thaw_tasks(false);
read_lock(&tasklist_lock);
do_each_thread(g, p) {
__thaw_task(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
schedule();
printk("done.\n");
}

View File

@@ -106,13 +106,11 @@ static int suspend_prepare(void)
goto Finish;
error = suspend_freeze_processes();
if (error) {
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
} else
if (!error)
return 0;
suspend_thaw_processes();
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);

View File

@@ -257,10 +257,8 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break;
error = freeze_processes();
if (error) {
thaw_processes();
if (error)
usermodehelper_enable();
}
if (!error)
data->frozen = 1;
break;