summaryrefslogtreecommitdiffstats
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-02-15 02:15:32 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-02-15 02:15:32 +0000
commit86464aed71025541805e7b1515541aee89879e33 (patch)
treee01a457a4912a8553bc65524aa3125d51f29f810 /kernel/fork.c
parent88f99939ecc6a95a79614574cb7d95ffccfc3466 (diff)
Merge with Linux 2.2.1.
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c45
1 files changed, 39 insertions, 6 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index a625aaba3..5c714fe73 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -16,6 +16,7 @@
#include <linux/unistd.h>
#include <linux/smp_lock.h>
#include <linux/module.h>
+#include <linux/vmalloc.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
@@ -230,16 +231,16 @@ static inline int dup_mmap(struct mm_struct * mm)
* Link in the new vma even if an error occurred,
* so that exit_mmap() can clean up the mess.
*/
- if((tmp->vm_next = *pprev) != NULL)
- (*pprev)->vm_pprev = &tmp->vm_next;
+ tmp->vm_next = *pprev;
*pprev = tmp;
- tmp->vm_pprev = pprev;
pprev = &tmp->vm_next;
if (retval)
goto fail_nomem;
}
retval = 0;
+ if (mm->map_count >= AVL_MIN_MAP_COUNT)
+ build_mmap_avl(mm);
fail_nomem:
flush_tlb_mm(current->mm);
@@ -268,7 +269,7 @@ struct mm_struct * mm_alloc(void)
* Leave mm->pgd set to the parent's pgd
* so that pgd_offset() is always valid.
*/
- mm->mmap = mm->mmap_cache = NULL;
+ mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL;
/* It has not run yet, so cannot be present in anyone's
* cache or tlb.
@@ -278,6 +279,30 @@ struct mm_struct * mm_alloc(void)
return mm;
}
+/* Please note the differences between mmput and mm_release.
+ * mmput is called whenever we stop holding onto a mm_struct,
+ * error success whatever.
+ *
+ * mm_release is called after a mm_struct has been removed
+ * from the current process.
+ *
+ * This difference is important for error handling, when we
+ * only half set up a mm_struct for a new process and need to restore
+ * the old one. Because we mmput the new mm_struct before
+ * restoring the old one. . .
+ * Eric Biederman 10 January 1998
+ */
+void mm_release(void)
+{
+ struct task_struct *tsk = current;
+ forget_segments();
+ /* notify parent sleeping on vfork() */
+ if (tsk->flags & PF_VFORK) {
+ tsk->flags &= ~PF_VFORK;
+ up(tsk->p_opptr->vfork_sem);
+ }
+}
+
/*
* Decrement the use count and release all resources for an mm.
*/
@@ -453,10 +478,12 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
{
unsigned long new_flags = p->flags;
- new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU);
+ new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK);
new_flags |= PF_FORKNOEXEC;
if (!(clone_flags & CLONE_PTRACE))
new_flags &= ~(PF_PTRACED|PF_TRACESYS);
+ if (clone_flags & CLONE_VFORK)
+ new_flags |= PF_VFORK;
p->flags = new_flags;
}
@@ -470,6 +497,9 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
int nr;
int retval = -ENOMEM;
struct task_struct *p;
+ struct semaphore sem = MUTEX_LOCKED;
+
+ current->vfork_sem = &sem;
p = alloc_task_struct();
if (!p)
@@ -521,6 +551,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
p->p_pptr = p->p_opptr = current;
p->p_cptr = NULL;
init_waitqueue(&p->wait_chldexit);
+ p->vfork_sem = NULL;
p->sigpending = 0;
sigemptyset(&p->signal);
@@ -602,9 +633,11 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
}
++total_forks;
bad_fork:
- up(&current->mm->mmap_sem);
unlock_kernel();
+ up(&current->mm->mmap_sem);
fork_out:
+ if ((clone_flags & CLONE_VFORK) && (retval > 0))
+ down(&sem);
return retval;
bad_fork_cleanup_sighand: