X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=kernel%2Fpower%2Fprocess.c;h=f1d0b345c9ba86a24ac48ee29cc4cb6f800b1c94;hb=6afe1a1fe8ff83f6ac2726b04665e76ba7b14f3e;hp=dba2f3acb4f802289e7399ee1e758d780267453c;hpb=232b14328050a4639130b0dec185f43968e72035;p=safe%2Fjmp%2Flinux-2.6 diff --git a/kernel/power/process.c b/kernel/power/process.c index dba2f3a..f1d0b34 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -75,21 +75,72 @@ void refrigerator(void) __set_current_state(save); } -static void freeze_task(struct task_struct *p) +static void fake_signal_wake_up(struct task_struct *p) { unsigned long flags; - if (!freezing(p)) { + spin_lock_irqsave(&p->sighand->siglock, flags); + signal_wake_up(p, 0); + spin_unlock_irqrestore(&p->sighand->siglock, flags); +} + +static int has_mm(struct task_struct *p) +{ + return (p->mm && !(p->flags & PF_BORROWED_MM)); +} + +/** + * freeze_task - send a freeze request to given task + * @p: task to send the request to + * @with_mm_only: if set, the request will only be sent if the task has its + * own mm + * Return value: 0, if @with_mm_only is set and the task has no mm of its + * own or the task is frozen, 1, otherwise + * + * The freeze request is sent by seting 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 its own mm (ie. it is a user land task). If @with_mm_only + * is set and the task has no mm of its own (ie. it is a kernel thread), + * its TIF_FREEZE flag should not be set. + * + * The task_lock() is necessary to prevent races with exit_mm() or + * use_mm()/unuse_mm() from occuring. + */ +static int freeze_task(struct task_struct *p, int with_mm_only) +{ + int ret = 1; + + task_lock(p); + if (freezing(p)) { + if (has_mm(p)) { + if (!signal_pending(p)) + fake_signal_wake_up(p); + } else { + if (with_mm_only) + ret = 0; + else + wake_up_state(p, TASK_INTERRUPTIBLE); + } + } else { rmb(); - if (!frozen(p)) { - set_freeze_flag(p); - if (p->state == TASK_STOPPED) - force_sig_specific(SIGSTOP, p); - spin_lock_irqsave(&p->sighand->siglock, flags); - signal_wake_up(p, p->state == TASK_STOPPED); - spin_unlock_irqrestore(&p->sighand->siglock, flags); + if (frozen(p)) { + ret = 0; + } else { + if (has_mm(p)) { + set_freeze_flag(p); + fake_signal_wake_up(p); + } else { + if (with_mm_only) { + ret = 0; + } else { + set_freeze_flag(p); + wake_up_state(p, TASK_INTERRUPTIBLE); + } + } } } + task_unlock(p); + return ret; } static void cancel_freezing(struct task_struct *p) @@ -110,6 +161,11 @@ static int try_to_freeze_tasks(int freeze_user_space) struct task_struct *g, *p; unsigned long end_time; unsigned int todo; + struct timeval start, end; + s64 elapsed_csecs64; + unsigned int elapsed_csecs; + + do_gettimeofday(&start); end_time = jiffies + TIMEOUT; do { @@ -119,32 +175,17 @@ static int try_to_freeze_tasks(int freeze_user_space) if (frozen(p) || !freezeable(p)) continue; - if (freeze_user_space) { - if (p->state == TASK_TRACED && - frozen(p->parent)) { - cancel_freezing(p); - continue; - } - /* - * Kernel threads should not have TIF_FREEZE set - * at this point, so we must ensure that either - * p->mm is not NULL *and* PF_BORROWED_MM is - * unset, or TIF_FRREZE is left unset. - * The task_lock() is necessary to prevent races - * with exit_mm() or use_mm()/unuse_mm() from - * occuring. - */ - task_lock(p); - if (!p->mm || (p->flags & PF_BORROWED_MM)) { - task_unlock(p); - continue; - } - freeze_task(p); - task_unlock(p); - } else { - freeze_task(p); - } - if (!freezer_should_skip(p)) + if (!freeze_task(p, freeze_user_space)) + 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); @@ -153,6 +194,11 @@ static int try_to_freeze_tasks(int freeze_user_space) 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, @@ -160,10 +206,9 @@ static int try_to_freeze_tasks(int freeze_user_space) * but it cleans up leftover PF_FREEZE requests. */ printk("\n"); - printk(KERN_ERR "Freezing of %s timed out after %d seconds " + printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds " "(%d tasks refusing to freeze):\n", - freeze_user_space ? "user space " : "tasks ", - TIMEOUT / HZ, todo); + elapsed_csecs / 100, elapsed_csecs % 100, todo); show_state(); read_lock(&tasklist_lock); do_each_thread(g, p) { @@ -174,6 +219,9 @@ static int try_to_freeze_tasks(int freeze_user_space) 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; @@ -186,18 +234,21 @@ int freeze_processes(void) { int error; - printk("Stopping tasks ... "); + printk("Freezing user space processes ... "); error = try_to_freeze_tasks(FREEZER_USER_SPACE); if (error) - return error; + goto Exit; + printk("done.\n"); + printk("Freezing remaining freezable tasks ... "); error = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); if (error) - return error; - - printk("done.\n"); + goto Exit; + printk("done."); + Exit: BUG_ON(in_atomic()); - return 0; + printk("\n"); + return error; } static void thaw_tasks(int thaw_user_space)