mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
f0af566da6
Fix try_to_freeze_tasks()'s use of do_div() on an s64 by making
elapsed_csecs64 a u64 instead and dividing that.
Possibly this should be guarded lest the interval calculation turn up
negative, but the possible negativity of the result of the division is
cast away anyway.
This was introduced by patch 438e2ce68d
.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
267 lines
6.1 KiB
C
267 lines
6.1 KiB
C
/*
|
|
* drivers/power/process.c - Functions for starting/stopping processes on
|
|
* suspend transitions.
|
|
*
|
|
* Originally from swsusp.
|
|
*/
|
|
|
|
|
|
#undef DEBUG
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/module.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/freezer.h>
|
|
|
|
/*
|
|
* Timeout for stopping processes
|
|
*/
|
|
#define TIMEOUT (20 * HZ)
|
|
|
|
static inline int freezeable(struct task_struct * p)
|
|
{
|
|
if ((p == current) ||
|
|
(p->flags & PF_NOFREEZE) ||
|
|
(p->exit_state != 0))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* freezing is complete, mark current process as frozen
|
|
*/
|
|
static inline void frozen_process(void)
|
|
{
|
|
if (!unlikely(current->flags & PF_NOFREEZE)) {
|
|
current->flags |= PF_FROZEN;
|
|
wmb();
|
|
}
|
|
clear_freeze_flag(current);
|
|
}
|
|
|
|
/* Refrigerator is place where frozen processes are stored :-). */
|
|
void refrigerator(void)
|
|
{
|
|
/* Hmm, should we be allowed to suspend when there are realtime
|
|
processes around? */
|
|
long save;
|
|
|
|
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(¤t->sighand->siglock);
|
|
recalc_sigpending(); /* We sent fake signal, clean it up */
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
|
|
for (;;) {
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
if (!frozen(current))
|
|
break;
|
|
schedule();
|
|
}
|
|
pr_debug("%s left refrigerator\n", current->comm);
|
|
__set_current_state(save);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static inline bool should_send_signal(struct task_struct *p)
|
|
{
|
|
return !(p->flags & PF_FREEZER_NOSIG);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* 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.
|
|
*/
|
|
static bool freeze_task(struct task_struct *p, bool sig_only)
|
|
{
|
|
/*
|
|
* 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)) {
|
|
rmb();
|
|
if (frozen(p))
|
|
return false;
|
|
|
|
if (!sig_only || should_send_signal(p))
|
|
set_freeze_flag(p);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
if (should_send_signal(p)) {
|
|
if (!signal_pending(p))
|
|
fake_signal_wake_up(p);
|
|
} else if (sig_only) {
|
|
return false;
|
|
} else {
|
|
wake_up_state(p, TASK_INTERRUPTIBLE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cancel_freezing(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 try_to_freeze_tasks(bool sig_only)
|
|
{
|
|
struct task_struct *g, *p;
|
|
unsigned long end_time;
|
|
unsigned int todo;
|
|
struct timeval start, end;
|
|
u64 elapsed_csecs64;
|
|
unsigned int elapsed_csecs;
|
|
|
|
do_gettimeofday(&start);
|
|
|
|
end_time = jiffies + TIMEOUT;
|
|
do {
|
|
todo = 0;
|
|
read_lock(&tasklist_lock);
|
|
do_each_thread(g, p) {
|
|
if (frozen(p) || !freezeable(p))
|
|
continue;
|
|
|
|
if (!freeze_task(p, sig_only))
|
|
continue;
|
|
|
|
/*
|
|
* Now that we've done set_freeze_flag, don't
|
|
* perturb a task in TASK_STOPPED or TASK_TRACED.
|
|
* It is "frozen enough". If the task does wake
|
|
* up, it will immediately call try_to_freeze.
|
|
*/
|
|
if (!task_is_stopped_or_traced(p) &&
|
|
!freezer_should_skip(p))
|
|
todo++;
|
|
} while_each_thread(g, p);
|
|
read_unlock(&tasklist_lock);
|
|
yield(); /* Yield is okay here */
|
|
if (time_after(jiffies, end_time))
|
|
break;
|
|
} while (todo);
|
|
|
|
do_gettimeofday(&end);
|
|
elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
|
|
do_div(elapsed_csecs64, NSEC_PER_SEC / 100);
|
|
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 failed after %d.%02d seconds "
|
|
"(%d tasks refusing to freeze):\n",
|
|
elapsed_csecs / 100, elapsed_csecs % 100, todo);
|
|
show_state();
|
|
read_lock(&tasklist_lock);
|
|
do_each_thread(g, p) {
|
|
task_lock(p);
|
|
if (freezing(p) && !freezer_should_skip(p))
|
|
printk(KERN_ERR " %s\n", p->comm);
|
|
cancel_freezing(p);
|
|
task_unlock(p);
|
|
} while_each_thread(g, p);
|
|
read_unlock(&tasklist_lock);
|
|
} else {
|
|
printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
|
|
elapsed_csecs % 100);
|
|
}
|
|
|
|
return todo ? -EBUSY : 0;
|
|
}
|
|
|
|
/**
|
|
* freeze_processes - tell processes to enter the refrigerator
|
|
*/
|
|
int freeze_processes(void)
|
|
{
|
|
int error;
|
|
|
|
printk("Freezing user space processes ... ");
|
|
error = try_to_freeze_tasks(true);
|
|
if (error)
|
|
goto Exit;
|
|
printk("done.\n");
|
|
|
|
printk("Freezing remaining freezable tasks ... ");
|
|
error = try_to_freeze_tasks(false);
|
|
if (error)
|
|
goto Exit;
|
|
printk("done.");
|
|
Exit:
|
|
BUG_ON(in_atomic());
|
|
printk("\n");
|
|
return error;
|
|
}
|
|
|
|
static void thaw_tasks(bool nosig_only)
|
|
{
|
|
struct task_struct *g, *p;
|
|
|
|
read_lock(&tasklist_lock);
|
|
do_each_thread(g, p) {
|
|
if (!freezeable(p))
|
|
continue;
|
|
|
|
if (nosig_only && should_send_signal(p))
|
|
continue;
|
|
|
|
thaw_process(p);
|
|
} while_each_thread(g, p);
|
|
read_unlock(&tasklist_lock);
|
|
}
|
|
|
|
void thaw_processes(void)
|
|
{
|
|
printk("Restarting tasks ... ");
|
|
thaw_tasks(true);
|
|
thaw_tasks(false);
|
|
schedule();
|
|
printk("done.\n");
|
|
}
|
|
|
|
EXPORT_SYMBOL(refrigerator);
|