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 b850173e7561..e1bcdedd1464 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); |