Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=ba96a0c88098697a63e80157718b7440414ed24d
Commit:     ba96a0c88098697a63e80157718b7440414ed24d
Parent:     33e1c288da62a6a5aa9077a6b7bfa690b1b02cf4
Author:     Rafael J. Wysocki <[EMAIL PROTECTED]>
AuthorDate: Wed May 23 13:57:25 2007 -0700
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Wed May 23 20:14:11 2007 -0700

    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 <[EMAIL PROTECTED]>
    Cc: Gautham R Shenoy <[EMAIL PROTECTED]>
    Cc: Oleg Nesterov <[EMAIL PROTECTED]>
    Cc: Pavel Machek <[EMAIL PROTECTED]>
    Cc: "Eric W. Biederman" <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 include/linux/freezer.h |   48 +++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/sched.h   |    1 +
 kernel/fork.c           |    3 ++
 kernel/power/process.c  |   29 ++++++++-------------------
 4 files changed, 59 insertions(+), 22 deletions(-)

diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index db5423e..c943525 100644
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -81,7 +81,49 @@ static inline int try_to_freeze(void)
                return 0;
 }
 
-extern void thaw_some_processes(int all);
+/*
+ * The PF_FREEZER_SKIP flag should be set by a vfork parent right before it
+ * calls wait_for_completion(&vfork) and reset right after it returns from this
+ * function.  Next, the parent should call try_to_freeze() to freeze itself
+ * appropriately in case the child has exited before the freezing of tasks is
+ * complete.  However, we don't want kernel threads to be frozen in unexpected
+ * places, so we allow them to block freeze_processes() instead or to set
+ * PF_NOFREEZE if needed and PF_FREEZER_SKIP is only set for userland vfork
+ * parents.  Fortunately, in the ____call_usermodehelper() case the parent 
won't
+ * really block freeze_processes(), since ____call_usermodehelper() (the child)
+ * does a little before exec/exit and it can't be frozen before waking up the
+ * parent.
+ */
+
+/*
+ * If the current task is a user space one, tell the freezer not to count it as
+ * freezable.
+ */
+static inline void freezer_do_not_count(void)
+{
+       if (current->mm)
+               current->flags |= PF_FREEZER_SKIP;
+}
+
+/*
+ * If the current task is a user space one, tell the freezer to count it as
+ * freezable again and try to freeze it.
+ */
+static inline void freezer_count(void)
+{
+       if (current->mm) {
+               current->flags &= ~PF_FREEZER_SKIP;
+               try_to_freeze();
+       }
+}
+
+/*
+ * Check if the task should be counted as freezeable by the freezer
+ */
+static inline int freezer_should_skip(struct task_struct *p)
+{
+       return !!(p->flags & PF_FREEZER_SKIP);
+}
 
 #else
 static inline int frozen(struct task_struct *p) { return 0; }
@@ -96,5 +138,7 @@ static inline void thaw_processes(void) {}
 
 static inline int try_to_freeze(void) { return 0; }
 
-
+static inline void freezer_do_not_count(void) {}
+static inline void freezer_count(void) {}
+static inline int freezer_should_skip(struct task_struct *p) { return 0; }
 #endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a81897e..870b75e 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)
 #define PF_SPREAD_SLAB 0x02000000      /* Spread some slab caches over cpuset 
*/
 #define PF_MEMPOLICY   0x10000000      /* Non-default NUMA mempolicy */
 #define PF_MUTEX_TESTER        0x20000000      /* Thread belongs to the rt 
mutex tester */
+#define PF_FREEZER_SKIP        0x40000000      /* Freezer should not count it 
as freezeable */
 
 /*
  * Only the _current_ task can read/write to tsk->flags, but other
diff --git a/kernel/fork.c b/kernel/fork.c
index 87069cf..73ad5cd 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -45,6 +45,7 @@
 #include <linux/acct.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
+#include <linux/freezer.h>
 #include <linux/delayacct.h>
 #include <linux/taskstats_kern.h>
 #include <linux/random.h>
@@ -1405,7 +1406,9 @@ long do_fork(unsigned long clone_flags,
                }
 
                if (clone_flags & CLONE_VFORK) {
+                       freezer_do_not_count();
                        wait_for_completion(&vfork);
+                       freezer_count();
                        if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) {
                                current->ptrace_message = nr;
                                ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | 
SIGTRAP);
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 02e490e..eefca85 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)
                                cancel_freezing(p);
                                continue;
                        }
-                       if (is_user_space(p)) {
-                               if (!freeze_user_space)
-                                       continue;
-
-                               /* Freeze the task unless there is a vfork
-                                * completion pending
-                                */
-                               if (!p->vfork_done)
-                                       freeze_process(p);
-                       } else {
-                               if (freeze_user_space)
-                                       continue;
-
-                               freeze_process(p);
-                       }
-                       todo++;
+                       if (is_user_space(p) == !freeze_user_space)
+                               continue;
+
+                       freeze_process(p);
+                       if (!freezer_should_skip(p))
+                               todo++;
                } while_each_thread(g, p);
                read_unlock(&tasklist_lock);
                yield();                        /* Yield is okay here */
@@ -161,7 +151,8 @@ static unsigned int try_to_freeze_tasks(int 
freeze_user_space)
                                continue;
 
                        task_lock(p);
-                       if (freezeable(p) && !frozen(p))
+                       if (freezeable(p) && !frozen(p) &&
+                           !freezer_should_skip(p))
                                printk(KERN_ERR " %s\n", p->comm);
 
                        cancel_freezing(p);
@@ -210,9 +201,7 @@ static void thaw_tasks(int thaw_user_space)
                if (is_user_space(p) == !thaw_user_space)
                        continue;
 
-               if (!thaw_process(p))
-                       printk(KERN_WARNING " Strange, %s not stopped\n",
-                               p->comm );
+               thaw_process(p);
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 }
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to