aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/power/process.c')
-rw-r--r--kernel/power/process.c141
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
78static void freeze_task(struct task_struct *p) 78static 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
87static 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
94static 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 */
116static 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
95static void cancel_freezing(struct task_struct *p) 153static 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
204static void thaw_tasks(int thaw_user_space) 259static void thaw_tasks(int thaw_user_space)