diff options
Diffstat (limited to 'kernel/power/process.c')
-rw-r--r-- | kernel/power/process.c | 141 |
1 files changed, 98 insertions, 43 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c index 3434940a3df1..6533923e711b 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -75,21 +75,79 @@ void refrigerator(void) | |||
75 | __set_current_state(save); | 75 | __set_current_state(save); |
76 | } | 76 | } |
77 | 77 | ||
78 | static void freeze_task(struct task_struct *p) | 78 | static void fake_signal_wake_up(struct task_struct *p, int resume) |
79 | { | 79 | { |
80 | unsigned long flags; | 80 | unsigned long flags; |
81 | 81 | ||
82 | if (!freezing(p)) { | 82 | spin_lock_irqsave(&p->sighand->siglock, flags); |
83 | signal_wake_up(p, resume); | ||
84 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | ||
85 | } | ||
86 | |||
87 | static void send_fake_signal(struct task_struct *p) | ||
88 | { | ||
89 | if (p->state == TASK_STOPPED) | ||
90 | force_sig_specific(SIGSTOP, p); | ||
91 | fake_signal_wake_up(p, p->state == TASK_STOPPED); | ||
92 | } | ||
93 | |||
94 | static int has_mm(struct task_struct *p) | ||
95 | { | ||
96 | return (p->mm && !(p->flags & PF_BORROWED_MM)); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * freeze_task - send a freeze request to given task | ||
101 | * @p: task to send the request to | ||
102 | * @with_mm_only: if set, the request will only be sent if the task has its | ||
103 | * own mm | ||
104 | * Return value: 0, if @with_mm_only is set and the task has no mm of its | ||
105 | * own or the task is frozen, 1, otherwise | ||
106 | * | ||
107 | * The freeze request is sent by seting the tasks's TIF_FREEZE flag and | ||
108 | * either sending a fake signal to it or waking it up, depending on whether | ||
109 | * or not it has its own mm (ie. it is a user land task). If @with_mm_only | ||
110 | * is set and the task has no mm of its own (ie. it is a kernel thread), | ||
111 | * its TIF_FREEZE flag should not be set. | ||
112 | * | ||
113 | * The task_lock() is necessary to prevent races with exit_mm() or | ||
114 | * use_mm()/unuse_mm() from occuring. | ||
115 | */ | ||
116 | static int freeze_task(struct task_struct *p, int with_mm_only) | ||
117 | { | ||
118 | int ret = 1; | ||
119 | |||
120 | task_lock(p); | ||
121 | if (freezing(p)) { | ||
122 | if (has_mm(p)) { | ||
123 | if (!signal_pending(p)) | ||
124 | fake_signal_wake_up(p, 0); | ||
125 | } else { | ||
126 | if (with_mm_only) | ||
127 | ret = 0; | ||
128 | else | ||
129 | wake_up_state(p, TASK_INTERRUPTIBLE); | ||
130 | } | ||
131 | } else { | ||
83 | rmb(); | 132 | rmb(); |
84 | if (!frozen(p)) { | 133 | if (frozen(p)) { |
85 | set_freeze_flag(p); | 134 | ret = 0; |
86 | if (p->state == TASK_STOPPED) | 135 | } else { |
87 | force_sig_specific(SIGSTOP, p); | 136 | if (has_mm(p)) { |
88 | spin_lock_irqsave(&p->sighand->siglock, flags); | 137 | set_freeze_flag(p); |
89 | signal_wake_up(p, p->state == TASK_STOPPED); | 138 | send_fake_signal(p); |
90 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 139 | } else { |
140 | if (with_mm_only) { | ||
141 | ret = 0; | ||
142 | } else { | ||
143 | set_freeze_flag(p); | ||
144 | wake_up_state(p, TASK_INTERRUPTIBLE); | ||
145 | } | ||
146 | } | ||
91 | } | 147 | } |
92 | } | 148 | } |
149 | task_unlock(p); | ||
150 | return ret; | ||
93 | } | 151 | } |
94 | 152 | ||
95 | static void cancel_freezing(struct task_struct *p) | 153 | static void cancel_freezing(struct task_struct *p) |
@@ -110,6 +168,11 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
110 | struct task_struct *g, *p; | 168 | struct task_struct *g, *p; |
111 | unsigned long end_time; | 169 | unsigned long end_time; |
112 | unsigned int todo; | 170 | unsigned int todo; |
171 | struct timeval start, end; | ||
172 | s64 elapsed_csecs64; | ||
173 | unsigned int elapsed_csecs; | ||
174 | |||
175 | do_gettimeofday(&start); | ||
113 | 176 | ||
114 | end_time = jiffies + TIMEOUT; | 177 | end_time = jiffies + TIMEOUT; |
115 | do { | 178 | do { |
@@ -119,31 +182,14 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
119 | if (frozen(p) || !freezeable(p)) | 182 | if (frozen(p) || !freezeable(p)) |
120 | continue; | 183 | continue; |
121 | 184 | ||
122 | if (freeze_user_space) { | 185 | if (p->state == TASK_TRACED && frozen(p->parent)) { |
123 | if (p->state == TASK_TRACED && | 186 | cancel_freezing(p); |
124 | frozen(p->parent)) { | 187 | continue; |
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); | ||
146 | } | 188 | } |
189 | |||
190 | if (!freeze_task(p, freeze_user_space)) | ||
191 | continue; | ||
192 | |||
147 | if (!freezer_should_skip(p)) | 193 | if (!freezer_should_skip(p)) |
148 | todo++; | 194 | todo++; |
149 | } while_each_thread(g, p); | 195 | } while_each_thread(g, p); |
@@ -153,6 +199,11 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
153 | break; | 199 | break; |
154 | } while (todo); | 200 | } while (todo); |
155 | 201 | ||
202 | do_gettimeofday(&end); | ||
203 | elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); | ||
204 | do_div(elapsed_csecs64, NSEC_PER_SEC / 100); | ||
205 | elapsed_csecs = elapsed_csecs64; | ||
206 | |||
156 | if (todo) { | 207 | if (todo) { |
157 | /* This does not unfreeze processes that are already frozen | 208 | /* This does not unfreeze processes that are already frozen |
158 | * (we have slightly ugly calling convention in that respect, | 209 | * (we have slightly ugly calling convention in that respect, |
@@ -160,10 +211,9 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
160 | * but it cleans up leftover PF_FREEZE requests. | 211 | * but it cleans up leftover PF_FREEZE requests. |
161 | */ | 212 | */ |
162 | printk("\n"); | 213 | printk("\n"); |
163 | printk(KERN_ERR "Freezing of %s timed out after %d seconds " | 214 | printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds " |
164 | "(%d tasks refusing to freeze):\n", | 215 | "(%d tasks refusing to freeze):\n", |
165 | freeze_user_space ? "user space " : "tasks ", | 216 | elapsed_csecs / 100, elapsed_csecs % 100, todo); |
166 | TIMEOUT / HZ, todo); | ||
167 | show_state(); | 217 | show_state(); |
168 | read_lock(&tasklist_lock); | 218 | read_lock(&tasklist_lock); |
169 | do_each_thread(g, p) { | 219 | do_each_thread(g, p) { |
@@ -174,6 +224,9 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
174 | task_unlock(p); | 224 | task_unlock(p); |
175 | } while_each_thread(g, p); | 225 | } while_each_thread(g, p); |
176 | read_unlock(&tasklist_lock); | 226 | read_unlock(&tasklist_lock); |
227 | } else { | ||
228 | printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100, | ||
229 | elapsed_csecs % 100); | ||
177 | } | 230 | } |
178 | 231 | ||
179 | return todo ? -EBUSY : 0; | 232 | return todo ? -EBUSY : 0; |
@@ -186,19 +239,21 @@ int freeze_processes(void) | |||
186 | { | 239 | { |
187 | int error; | 240 | int error; |
188 | 241 | ||
189 | printk("Stopping tasks ... "); | 242 | printk("Freezing user space processes ... "); |
190 | error = try_to_freeze_tasks(FREEZER_USER_SPACE); | 243 | error = try_to_freeze_tasks(FREEZER_USER_SPACE); |
191 | if (error) | 244 | if (error) |
192 | return error; | 245 | goto Exit; |
246 | printk("done.\n"); | ||
193 | 247 | ||
194 | sys_sync(); | 248 | printk("Freezing remaining freezable tasks ... "); |
195 | error = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); | 249 | error = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); |
196 | if (error) | 250 | if (error) |
197 | return error; | 251 | goto Exit; |
198 | 252 | printk("done."); | |
199 | printk("done.\n"); | 253 | Exit: |
200 | BUG_ON(in_atomic()); | 254 | BUG_ON(in_atomic()); |
201 | return 0; | 255 | printk("\n"); |
256 | return error; | ||
202 | } | 257 | } |
203 | 258 | ||
204 | static void thaw_tasks(int thaw_user_space) | 259 | static void thaw_tasks(int thaw_user_space) |