Currently, if a task is stopped (ie. it's in the TASK_STOPPED state), it is considered by the freezer as unfreezeable. However, there may be a race between the freezer and the delivery of the continuation signal to the task resulting in the task running after we have finished freezing other tasks. This, in turn, may lead to undesirable effects up to and including a corruption of data.
To prevent this from happening we first need to make the freezer consider stopped tasks as freezeable. For this purpose we need to make freezeable() stop returning 0 for these tasks. We must remember, however, that the stopped tasks need not receive the continuation signal before thaw_processes() is called, so as soon as PF_FREEZE is set for them try_to_freeze_tasks() should stop counting them as the ones to wait for. Additionally, if there's a traced task (ie. a task in the TASK_TRACED state) the parent of which has PF_FREEZE set and is stopped, try_to_freeze_tasks() should not wait for it. Moreover, if there are some stopped tasks that haven't received the continuation signal before thaw_processes() is called, we must clear PF_FREEZE for them so that they don't go to the refrigerator when it's no longer desirable. Still if a stopped tasks receives the continuation signal after the freezer has returned, it should immediately go to the refrigerator. For this reason we need to place the call to try_to_freeze() in get_signal_to_deliver() right after the relock label. It is also reasonable to change the error paths so that the names of stopped or traced tasks which have not frozen are not reported to the user, because in the majority of cases they would be false positives. Signed-off-by: Rafael J. Wysocki <[EMAIL PROTECTED]> --- kernel/power/process.c | 36 ++++++++++++++++++++++++++++++------ kernel/signal.c | 2 +- 2 files changed, 31 insertions(+), 7 deletions(-) Index: linux-2.6.19-rc6-mm2/kernel/power/process.c =================================================================== --- linux-2.6.19-rc6-mm2.orig/kernel/power/process.c +++ linux-2.6.19-rc6-mm2/kernel/power/process.c @@ -28,8 +28,7 @@ static inline int freezeable(struct task if ((p == current) || (p->flags & PF_NOFREEZE) || (p->exit_state == EXIT_ZOMBIE) || - (p->exit_state == EXIT_DEAD) || - (p->state == TASK_STOPPED)) + (p->exit_state == EXIT_DEAD)) return 0; return 1; } @@ -81,6 +80,11 @@ static void cancel_freezing(struct task_ } } +static inline int stopped_and_freezing(struct task_struct *p) +{ + return p->state == TASK_STOPPED && freezing(p); +} + static inline int is_user_space(struct task_struct *p) { return p->mm && !(p->flags & PF_BORROWED_MM); @@ -103,9 +107,11 @@ static unsigned int try_to_freeze_tasks( if (frozen(p)) continue; - if (p->state == TASK_TRACED && - (frozen(p->parent) || - p->parent->state == TASK_STOPPED)) { + if (stopped_and_freezing(p)) + continue; + + if (p->state == TASK_TRACED && (frozen(p->parent) || + stopped_and_freezing(p->parent))) { cancel_freezing(p); continue; } @@ -149,7 +155,8 @@ static unsigned int try_to_freeze_tasks( if (is_user_space(p) == !freeze_user_space) continue; - if (freezeable(p) && !frozen(p)) + if (freezeable(p) && !frozen(p) && + p->state != TASK_STOPPED && p->state != TASK_TRACED) printk(KERN_ERR " %s\n", p->comm); cancel_freezing(p); @@ -185,6 +192,18 @@ int freeze_processes(void) return 0; } +static void release_stopped_tasks(void) +{ + struct task_struct *g, *p; + + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (stopped_and_freezing(p)) + cancel_freezing(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); +} + static void thaw_tasks(int thaw_user_space) { struct task_struct *g, *p; @@ -197,6 +216,10 @@ static void thaw_tasks(int thaw_user_spa if (is_user_space(p) == !thaw_user_space) continue; + if (!frozen(p) && + (p->state == TASK_STOPPED || p->state == TASK_TRACED)) + continue; + if (!thaw_process(p)) printk(KERN_WARNING " Strange, %s not stopped\n", p->comm ); @@ -207,6 +230,7 @@ static void thaw_tasks(int thaw_user_spa void thaw_processes(void) { printk("Restarting tasks ... "); + release_stopped_tasks(); thaw_tasks(FREEZER_KERNEL_THREADS); thaw_tasks(FREEZER_USER_SPACE); schedule(); Index: linux-2.6.19-rc6-mm2/kernel/signal.c =================================================================== --- linux-2.6.19-rc6-mm2.orig/kernel/signal.c +++ linux-2.6.19-rc6-mm2/kernel/signal.c @@ -1937,9 +1937,9 @@ int get_signal_to_deliver(siginfo_t *inf sigset_t *mask = ¤t->blocked; int signr = 0; +relock: try_to_freeze(); -relock: spin_lock_irq(¤t->sighand->siglock); for (;;) { struct k_sigaction *ka; ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ Suspend-devel mailing list Suspend-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/suspend-devel