aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-05-23 16:57:25 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-23 23:14:11 -0400
commitba96a0c88098697a63e80157718b7440414ed24d (patch)
treebdd999761eed452cc162f5b63166d1014aaf2e3e
parent33e1c288da62a6a5aa9077a6b7bfa690b1b02cf4 (diff)
freezer: fix vfork problem
Currently try_to_freeze_tasks() has to wait until all of the vforked processes exit and for this reason every user can make it fail. To fix this problem we can introduce the additional process flag PF_FREEZER_SKIP to be used by tasks that do not want to be counted as freezable by the freezer and want to have TIF_FREEZE set nevertheless. Then, this flag can be set by tasks using sys_vfork() before they call wait_for_completion(&vfork) and cleared after they have woken up. After clearing it, the tasks should call try_to_freeze() as soon as possible. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Cc: Gautham R Shenoy <ego@in.ibm.com> Cc: Oleg Nesterov <oleg@tv-sign.ru> Cc: Pavel Machek <pavel@ucw.cz> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/freezer.h48
-rw-r--r--include/linux/sched.h1
-rw-r--r--kernel/fork.c3
-rw-r--r--kernel/power/process.c29
4 files changed, 59 insertions, 22 deletions
diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index db5423eae24d..c9435252e8e4 100644
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -81,7 +81,49 @@ static inline int try_to_freeze(void)
81 return 0; 81 return 0;
82} 82}
83 83
84extern void thaw_some_processes(int all); 84/*
85 * The PF_FREEZER_SKIP flag should be set by a vfork parent right before it
86 * calls wait_for_completion(&vfork) and reset right after it returns from this
87 * function. Next, the parent should call try_to_freeze() to freeze itself
88 * appropriately in case the child has exited before the freezing of tasks is
89 * complete. However, we don't want kernel threads to be frozen in unexpected
90 * places, so we allow them to block freeze_processes() instead or to set
91 * PF_NOFREEZE if needed and PF_FREEZER_SKIP is only set for userland vfork
92 * parents. Fortunately, in the ____call_usermodehelper() case the parent won't
93 * really block freeze_processes(), since ____call_usermodehelper() (the child)
94 * does a little before exec/exit and it can't be frozen before waking up the
95 * parent.
96 */
97
98/*
99 * If the current task is a user space one, tell the freezer not to count it as
100 * freezable.
101 */
102static inline void freezer_do_not_count(void)
103{
104 if (current->mm)
105 current->flags |= PF_FREEZER_SKIP;
106}
107
108/*
109 * If the current task is a user space one, tell the freezer to count it as
110 * freezable again and try to freeze it.
111 */
112static inline void freezer_count(void)
113{
114 if (current->mm) {
115 current->flags &= ~PF_FREEZER_SKIP;
116 try_to_freeze();
117 }
118}
119
120/*
121 * Check if the task should be counted as freezeable by the freezer
122 */
123static inline int freezer_should_skip(struct task_struct *p)
124{
125 return !!(p->flags & PF_FREEZER_SKIP);
126}
85 127
86#else 128#else
87static inline int frozen(struct task_struct *p) { return 0; } 129static inline int frozen(struct task_struct *p) { return 0; }
@@ -96,5 +138,7 @@ static inline void thaw_processes(void) {}
96 138
97static inline int try_to_freeze(void) { return 0; } 139static inline int try_to_freeze(void) { return 0; }
98 140
99 141static inline void freezer_do_not_count(void) {}
142static inline void freezer_count(void) {}
143static inline int freezer_should_skip(struct task_struct *p) { return 0; }
100#endif 144#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a81897e2a244..870b75e348ab 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1182,6 +1182,7 @@ static inline void put_task_struct(struct task_struct *t)
1182#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */ 1182#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */
1183#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ 1183#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
1184#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ 1184#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */
1185#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */
1185 1186
1186/* 1187/*
1187 * Only the _current_ task can read/write to tsk->flags, but other 1188 * Only the _current_ task can read/write to tsk->flags, but other
diff --git a/kernel/fork.c b/kernel/fork.c
index 87069cfc18a1..73ad5cda1bcd 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -45,6 +45,7 @@
45#include <linux/acct.h> 45#include <linux/acct.h>
46#include <linux/tsacct_kern.h> 46#include <linux/tsacct_kern.h>
47#include <linux/cn_proc.h> 47#include <linux/cn_proc.h>
48#include <linux/freezer.h>
48#include <linux/delayacct.h> 49#include <linux/delayacct.h>
49#include <linux/taskstats_kern.h> 50#include <linux/taskstats_kern.h>
50#include <linux/random.h> 51#include <linux/random.h>
@@ -1405,7 +1406,9 @@ long do_fork(unsigned long clone_flags,
1405 } 1406 }
1406 1407
1407 if (clone_flags & CLONE_VFORK) { 1408 if (clone_flags & CLONE_VFORK) {
1409 freezer_do_not_count();
1408 wait_for_completion(&vfork); 1410 wait_for_completion(&vfork);
1411 freezer_count();
1409 if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) { 1412 if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
1410 current->ptrace_message = nr; 1413 current->ptrace_message = nr;
1411 ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); 1414 ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 02e490e311eb..eefca8581fa0 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -120,22 +120,12 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space)
120 cancel_freezing(p); 120 cancel_freezing(p);
121 continue; 121 continue;
122 } 122 }
123 if (is_user_space(p)) { 123 if (is_user_space(p) == !freeze_user_space)
124 if (!freeze_user_space) 124 continue;
125 continue; 125
126 126 freeze_process(p);
127 /* Freeze the task unless there is a vfork 127 if (!freezer_should_skip(p))
128 * completion pending 128 todo++;
129 */
130 if (!p->vfork_done)
131 freeze_process(p);
132 } else {
133 if (freeze_user_space)
134 continue;
135
136 freeze_process(p);
137 }
138 todo++;
139 } while_each_thread(g, p); 129 } while_each_thread(g, p);
140 read_unlock(&tasklist_lock); 130 read_unlock(&tasklist_lock);
141 yield(); /* Yield is okay here */ 131 yield(); /* Yield is okay here */
@@ -161,7 +151,8 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space)
161 continue; 151 continue;
162 152
163 task_lock(p); 153 task_lock(p);
164 if (freezeable(p) && !frozen(p)) 154 if (freezeable(p) && !frozen(p) &&
155 !freezer_should_skip(p))
165 printk(KERN_ERR " %s\n", p->comm); 156 printk(KERN_ERR " %s\n", p->comm);
166 157
167 cancel_freezing(p); 158 cancel_freezing(p);
@@ -210,9 +201,7 @@ static void thaw_tasks(int thaw_user_space)
210 if (is_user_space(p) == !thaw_user_space) 201 if (is_user_space(p) == !thaw_user_space)
211 continue; 202 continue;
212 203
213 if (!thaw_process(p)) 204 thaw_process(p);
214 printk(KERN_WARNING " Strange, %s not stopped\n",
215 p->comm );
216 } while_each_thread(g, p); 205 } while_each_thread(g, p);
217 read_unlock(&tasklist_lock); 206 read_unlock(&tasklist_lock);
218} 207}