Merge branch 'for-2.6.39/core' of git://git.kernel.dk/linux-2.6-block
[pandora-kernel.git] / kernel / fork.c
index 027c80e..e7548de 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/tracehook.h>
 #include <linux/futex.h>
 #include <linux/compat.h>
+#include <linux/kthread.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/rcupdate.h>
 #include <linux/ptrace.h>
@@ -109,20 +110,25 @@ int nr_processes(void)
 }
 
 #ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
-# define alloc_task_struct()   kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)
-# define free_task_struct(tsk) kmem_cache_free(task_struct_cachep, (tsk))
+# define alloc_task_struct_node(node)          \
+               kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node)
+# define free_task_struct(tsk)                 \
+               kmem_cache_free(task_struct_cachep, (tsk))
 static struct kmem_cache *task_struct_cachep;
 #endif
 
 #ifndef __HAVE_ARCH_THREAD_INFO_ALLOCATOR
-static inline struct thread_info *alloc_thread_info(struct task_struct *tsk)
+static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
+                                                 int node)
 {
 #ifdef CONFIG_DEBUG_STACK_USAGE
        gfp_t mask = GFP_KERNEL | __GFP_ZERO;
 #else
        gfp_t mask = GFP_KERNEL;
 #endif
-       return (struct thread_info *)__get_free_pages(mask, THREAD_SIZE_ORDER);
+       struct page *page = alloc_pages_node(node, mask, THREAD_SIZE_ORDER);
+
+       return page ? page_address(page) : NULL;
 }
 
 static inline void free_thread_info(struct thread_info *ti)
@@ -193,6 +199,7 @@ void __put_task_struct(struct task_struct *tsk)
        if (!profile_handoff_task(tsk))
                free_task(tsk);
 }
+EXPORT_SYMBOL_GPL(__put_task_struct);
 
 /*
  * macro override instead of weak attribute alias, to workaround
@@ -248,16 +255,16 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
        struct task_struct *tsk;
        struct thread_info *ti;
        unsigned long *stackend;
-
+       int node = tsk_fork_get_node(orig);
        int err;
 
        prepare_to_copy(orig);
 
-       tsk = alloc_task_struct();
+       tsk = alloc_task_struct_node(node);
        if (!tsk)
                return NULL;
 
-       ti = alloc_thread_info(tsk);
+       ti = alloc_thread_info_node(tsk, node);
        if (!ti) {
                free_task_struct(tsk);
                return NULL;
@@ -1180,12 +1187,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                pid = alloc_pid(p->nsproxy->pid_ns);
                if (!pid)
                        goto bad_fork_cleanup_io;
-
-               if (clone_flags & CLONE_NEWPID) {
-                       retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);
-                       if (retval < 0)
-                               goto bad_fork_free_pid;
-               }
        }
 
        p->pid = pid_nr(pid);
@@ -1292,7 +1293,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                tracehook_finish_clone(p, clone_flags, trace);
 
                if (thread_group_leader(p)) {
-                       if (clone_flags & CLONE_NEWPID)
+                       if (is_child_reaper(pid))
                                p->nsproxy->pid_ns->child_reaper = p;
 
                        p->signal->leader_pid = pid;
@@ -1515,38 +1516,24 @@ void __init proc_caches_init(void)
 }
 
 /*
- * Check constraints on flags passed to the unshare system call and
- * force unsharing of additional process context as appropriate.
+ * Check constraints on flags passed to the unshare system call.
  */
-static void check_unshare_flags(unsigned long *flags_ptr)
+static int check_unshare_flags(unsigned long unshare_flags)
 {
+       if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
+                               CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
+                               CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
+               return -EINVAL;
        /*
-        * If unsharing a thread from a thread group, must also
-        * unshare vm.
-        */
-       if (*flags_ptr & CLONE_THREAD)
-               *flags_ptr |= CLONE_VM;
-
-       /*
-        * If unsharing vm, must also unshare signal handlers.
-        */
-       if (*flags_ptr & CLONE_VM)
-               *flags_ptr |= CLONE_SIGHAND;
-
-       /*
-        * If unsharing namespace, must also unshare filesystem information.
+        * Not implemented, but pretend it works if there is nothing to
+        * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND
+        * needs to unshare vm.
         */
-       if (*flags_ptr & CLONE_NEWNS)
-               *flags_ptr |= CLONE_FS;
-}
-
-/*
- * Unsharing of tasks created with CLONE_THREAD is not supported yet
- */
-static int unshare_thread(unsigned long unshare_flags)
-{
-       if (unshare_flags & CLONE_THREAD)
-               return -EINVAL;
+       if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) {
+               /* FIXME: get_task_mm() increments ->mm_users */
+               if (atomic_read(&current->mm->mm_users) > 1)
+                       return -EINVAL;
+       }
 
        return 0;
 }
@@ -1572,34 +1559,6 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
        return 0;
 }
 
-/*
- * Unsharing of sighand is not supported yet
- */
-static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp)
-{
-       struct sighand_struct *sigh = current->sighand;
-
-       if ((unshare_flags & CLONE_SIGHAND) && atomic_read(&sigh->count) > 1)
-               return -EINVAL;
-       else
-               return 0;
-}
-
-/*
- * Unshare vm if it is being shared
- */
-static int unshare_vm(unsigned long unshare_flags, struct mm_struct **new_mmp)
-{
-       struct mm_struct *mm = current->mm;
-
-       if ((unshare_flags & CLONE_VM) &&
-           (mm && atomic_read(&mm->mm_users) > 1)) {
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 /*
  * Unshare file descriptor table if it is being shared
  */
@@ -1628,23 +1587,21 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp
  */
 SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
 {
-       int err = 0;
        struct fs_struct *fs, *new_fs = NULL;
-       struct sighand_struct *new_sigh = NULL;
-       struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL;
        struct files_struct *fd, *new_fd = NULL;
        struct nsproxy *new_nsproxy = NULL;
        int do_sysvsem = 0;
+       int err;
 
-       check_unshare_flags(&unshare_flags);
-
-       /* Return -EINVAL for all unsupported flags */
-       err = -EINVAL;
-       if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
-                               CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
-                               CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
+       err = check_unshare_flags(unshare_flags);
+       if (err)
                goto bad_unshare_out;
 
+       /*
+        * If unsharing namespace, must also unshare filesystem information.
+        */
+       if (unshare_flags & CLONE_NEWNS)
+               unshare_flags |= CLONE_FS;
        /*
         * CLONE_NEWIPC must also detach from the undolist: after switching
         * to a new ipc namespace, the semaphore arrays from the old
@@ -1652,21 +1609,15 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
         */
        if (unshare_flags & (CLONE_NEWIPC|CLONE_SYSVSEM))
                do_sysvsem = 1;
-       if ((err = unshare_thread(unshare_flags)))
-               goto bad_unshare_out;
        if ((err = unshare_fs(unshare_flags, &new_fs)))
-               goto bad_unshare_cleanup_thread;
-       if ((err = unshare_sighand(unshare_flags, &new_sigh)))
-               goto bad_unshare_cleanup_fs;
-       if ((err = unshare_vm(unshare_flags, &new_mm)))
-               goto bad_unshare_cleanup_sigh;
+               goto bad_unshare_out;
        if ((err = unshare_fd(unshare_flags, &new_fd)))
-               goto bad_unshare_cleanup_vm;
+               goto bad_unshare_cleanup_fs;
        if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy,
                        new_fs)))
                goto bad_unshare_cleanup_fd;
 
-       if (new_fs ||  new_mm || new_fd || do_sysvsem || new_nsproxy) {
+       if (new_fs || new_fd || do_sysvsem || new_nsproxy) {
                if (do_sysvsem) {
                        /*
                         * CLONE_SYSVSEM is equivalent to sys_exit().
@@ -1692,19 +1643,6 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
                        spin_unlock(&fs->lock);
                }
 
-               if (new_mm) {
-                       mm = current->mm;
-                       active_mm = current->active_mm;
-                       current->mm = new_mm;
-                       current->active_mm = new_mm;
-                       if (current->signal->oom_score_adj == OOM_SCORE_ADJ_MIN) {
-                               atomic_dec(&mm->oom_disable_count);
-                               atomic_inc(&new_mm->oom_disable_count);
-                       }
-                       activate_mm(active_mm, new_mm);
-                       new_mm = mm;
-               }
-
                if (new_fd) {
                        fd = current->files;
                        current->files = new_fd;
@@ -1721,20 +1659,10 @@ bad_unshare_cleanup_fd:
        if (new_fd)
                put_files_struct(new_fd);
 
-bad_unshare_cleanup_vm:
-       if (new_mm)
-               mmput(new_mm);
-
-bad_unshare_cleanup_sigh:
-       if (new_sigh)
-               if (atomic_dec_and_test(&new_sigh->count))
-                       kmem_cache_free(sighand_cachep, new_sigh);
-
 bad_unshare_cleanup_fs:
        if (new_fs)
                free_fs_struct(new_fs);
 
-bad_unshare_cleanup_thread:
 bad_unshare_out:
        return err;
 }