diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-07-19 04:47:33 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:42 -0400 |
| commit | 0c1eecfb345401629aa57c9d3b077273e56c45a7 (patch) | |
| tree | 522d7090966c6e70f3147c30e3308781e0309938 /kernel/power/process.c | |
| parent | b1457bcc3a00a0446c7f6e2f22fd24b6d8d0a309 (diff) | |
Freezer: avoid freezing kernel threads prematurely
Kernel threads should not have TIF_FREEZE set when user space processes are
being frozen, since otherwise some of them might be frozen prematurely.
To prevent this from happening we can (1) make exit_mm() unset TIF_FREEZE
unconditionally just after clearing tsk->mm and (2) make try_to_freeze_tasks()
check if p->mm is different from zero and PF_BORROWED_MM is unset in p->flags
when user space processes are to be frozen.
Namely, when user space processes are being frozen, we only should set
TIF_FREEZE for tasks that have p->mm different from NULL and don't have
PF_BORROWED_MM set in p->flags. For this reason task_lock() must be used to
prevent try_to_freeze_tasks() from racing with use_mm()/unuse_mm(), in which
p->mm and p->flags.PF_BORROWED_MM are changed under task_lock(p). Also, we
need to prevent the following scenario from happening:
* daemonize() is called by a task spawned from a user space code path
* freezer checks if the task has p->mm set and the result is positive
* task enters exit_mm() and clears its TIF_FREEZE
* freezer sets TIF_FREEZE for the task
* task calls try_to_freeze() and goes to the refrigerator, which is wrong at
that point
This requires us to acquire task_lock(p) before p->flags.PF_BORROWED_MM and
p->mm are examined and release it after TIF_FREEZE is set for p (or it turns
out that TIF_FREEZE should not be set).
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Gautham R Shenoy <ego@in.ibm.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/power/process.c')
| -rw-r--r-- | kernel/power/process.c | 64 |
1 files changed, 33 insertions, 31 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c index b850173e75..e1bcdedd14 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
| @@ -40,7 +40,7 @@ static inline void frozen_process(void) | |||
| 40 | current->flags |= PF_FROZEN; | 40 | current->flags |= PF_FROZEN; |
| 41 | wmb(); | 41 | wmb(); |
| 42 | } | 42 | } |
| 43 | clear_tsk_thread_flag(current, TIF_FREEZE); | 43 | clear_freeze_flag(current); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | /* Refrigerator is place where frozen processes are stored :-). */ | 46 | /* Refrigerator is place where frozen processes are stored :-). */ |
| @@ -75,17 +75,16 @@ void refrigerator(void) | |||
| 75 | current->state = save; | 75 | current->state = save; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | static inline void freeze_process(struct task_struct *p) | 78 | static void freeze_task(struct task_struct *p) |
| 79 | { | 79 | { |
| 80 | unsigned long flags; | 80 | unsigned long flags; |
| 81 | 81 | ||
| 82 | if (!freezing(p)) { | 82 | if (!freezing(p)) { |
| 83 | rmb(); | 83 | rmb(); |
| 84 | if (!frozen(p)) { | 84 | if (!frozen(p)) { |
| 85 | set_freeze_flag(p); | ||
| 85 | if (p->state == TASK_STOPPED) | 86 | if (p->state == TASK_STOPPED) |
| 86 | force_sig_specific(SIGSTOP, p); | 87 | force_sig_specific(SIGSTOP, p); |
| 87 | |||
| 88 | freeze(p); | ||
| 89 | spin_lock_irqsave(&p->sighand->siglock, flags); | 88 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 90 | signal_wake_up(p, p->state == TASK_STOPPED); | 89 | signal_wake_up(p, p->state == TASK_STOPPED); |
| 91 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 90 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| @@ -99,18 +98,13 @@ static void cancel_freezing(struct task_struct *p) | |||
| 99 | 98 | ||
| 100 | if (freezing(p)) { | 99 | if (freezing(p)) { |
| 101 | pr_debug(" clean up: %s\n", p->comm); | 100 | pr_debug(" clean up: %s\n", p->comm); |
| 102 | do_not_freeze(p); | 101 | clear_freeze_flag(p); |
| 103 | spin_lock_irqsave(&p->sighand->siglock, flags); | 102 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 104 | recalc_sigpending_and_wake(p); | 103 | recalc_sigpending_and_wake(p); |
| 105 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 104 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| 106 | } | 105 | } |
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | static inline int is_user_space(struct task_struct *p) | ||
| 110 | { | ||
| 111 | return p->mm && !(p->flags & PF_BORROWED_MM); | ||
| 112 | } | ||
| 113 | |||
| 114 | static unsigned int try_to_freeze_tasks(int freeze_user_space) | 108 | static unsigned int try_to_freeze_tasks(int freeze_user_space) |
| 115 | { | 109 | { |
| 116 | struct task_struct *g, *p; | 110 | struct task_struct *g, *p; |
| @@ -122,20 +116,34 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space) | |||
| 122 | todo = 0; | 116 | todo = 0; |
| 123 | read_lock(&tasklist_lock); | 117 | read_lock(&tasklist_lock); |
| 124 | do_each_thread(g, p) { | 118 | do_each_thread(g, p) { |
| 125 | if (!freezeable(p)) | 119 | if (frozen(p) || !freezeable(p)) |
| 126 | continue; | ||
| 127 | |||
| 128 | if (frozen(p)) | ||
| 129 | continue; | 120 | continue; |
| 130 | 121 | ||
| 131 | if (p->state == TASK_TRACED && frozen(p->parent)) { | 122 | if (freeze_user_space) { |
| 132 | cancel_freezing(p); | 123 | if (p->state == TASK_TRACED && |
| 133 | continue; | 124 | frozen(p->parent)) { |
| 125 | cancel_freezing(p); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | /* | ||
| 129 | * Kernel threads should not have TIF_FREEZE set | ||
| 130 | * at this point, so we must ensure that either | ||
| 131 | * p->mm is not NULL *and* PF_BORROWED_MM is | ||
| 132 | * unset, or TIF_FRREZE is left unset. | ||
| 133 | * The task_lock() is necessary to prevent races | ||
| 134 | * with exit_mm() or use_mm()/unuse_mm() from | ||
| 135 | * occuring. | ||
| 136 | */ | ||
| 137 | task_lock(p); | ||
| 138 | if (!p->mm || (p->flags & PF_BORROWED_MM)) { | ||
| 139 | task_unlock(p); | ||
| 140 | continue; | ||
| 141 | } | ||
| 142 | freeze_task(p); | ||
| 143 | task_unlock(p); | ||
| 144 | } else { | ||
| 145 | freeze_task(p); | ||
| 134 | } | 146 | } |
| 135 | if (freeze_user_space && !is_user_space(p)) | ||
| 136 | continue; | ||
| 137 | |||
| 138 | freeze_process(p); | ||
| 139 | if (!freezer_should_skip(p)) | 147 | if (!freezer_should_skip(p)) |
| 140 | todo++; | 148 | todo++; |
| 141 | } while_each_thread(g, p); | 149 | } while_each_thread(g, p); |
| @@ -152,22 +160,16 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space) | |||
| 152 | * but it cleans up leftover PF_FREEZE requests. | 160 | * but it cleans up leftover PF_FREEZE requests. |
| 153 | */ | 161 | */ |
| 154 | printk("\n"); | 162 | printk("\n"); |
| 155 | printk(KERN_ERR "Stopping %s timed out after %d seconds " | 163 | printk(KERN_ERR "Freezing of %s timed out after %d seconds " |
| 156 | "(%d tasks refusing to freeze):\n", | 164 | "(%d tasks refusing to freeze):\n", |
| 157 | freeze_user_space ? "user space processes" : | 165 | freeze_user_space ? "user space " : "tasks ", |
| 158 | "kernel threads", | ||
| 159 | TIMEOUT / HZ, todo); | 166 | TIMEOUT / HZ, todo); |
| 160 | show_state(); | 167 | show_state(); |
| 161 | read_lock(&tasklist_lock); | 168 | read_lock(&tasklist_lock); |
| 162 | do_each_thread(g, p) { | 169 | do_each_thread(g, p) { |
| 163 | if (freeze_user_space && !is_user_space(p)) | ||
| 164 | continue; | ||
| 165 | |||
| 166 | task_lock(p); | 170 | task_lock(p); |
| 167 | if (freezeable(p) && !frozen(p) && | 171 | if (freezing(p) && !freezer_should_skip(p)) |
| 168 | !freezer_should_skip(p)) | ||
| 169 | printk(KERN_ERR " %s\n", p->comm); | 172 | printk(KERN_ERR " %s\n", p->comm); |
| 170 | |||
| 171 | cancel_freezing(p); | 173 | cancel_freezing(p); |
| 172 | task_unlock(p); | 174 | task_unlock(p); |
| 173 | } while_each_thread(g, p); | 175 | } while_each_thread(g, p); |
| @@ -211,7 +213,7 @@ static void thaw_tasks(int thaw_user_space) | |||
| 211 | if (!freezeable(p)) | 213 | if (!freezeable(p)) |
| 212 | continue; | 214 | continue; |
| 213 | 215 | ||
| 214 | if (is_user_space(p) == !thaw_user_space) | 216 | if (!p->mm == thaw_user_space) |
| 215 | continue; | 217 | continue; |
| 216 | 218 | ||
| 217 | thaw_process(p); | 219 | thaw_process(p); |
