/*
|
/*
|
* linux/kernel/fork.c
|
* linux/kernel/fork.c
|
*
|
*
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*/
|
*/
|
|
|
/*
|
/*
|
* 'fork.c' contains the help-routines for the 'fork' system call
|
* 'fork.c' contains the help-routines for the 'fork' system call
|
* (see also system_call.s).
|
* (see also system_call.s).
|
* Fork is rather simple, once you get the hang of it, but the memory
|
* Fork is rather simple, once you get the hang of it, but the memory
|
* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
|
* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
|
*/
|
*/
|
|
|
/*
|
/*
|
* uClinux revisions for NO_MM
|
* uClinux revisions for NO_MM
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* The Silver Hammer Group, Ltd.
|
* The Silver Hammer Group, Ltd.
|
*/
|
*/
|
|
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/stddef.h>
|
#include <linux/stddef.h>
|
#include <linux/unistd.h>
|
#include <linux/unistd.h>
|
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/ldt.h>
|
#include <linux/ldt.h>
|
#include <linux/smp.h>
|
#include <linux/smp.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
#include <asm/system.h>
|
#include <asm/system.h>
|
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
|
|
int nr_tasks=1;
|
int nr_tasks=1;
|
int nr_running=1;
|
int nr_running=1;
|
unsigned long int total_forks=0; /* Handle normal Linux uptimes. */
|
unsigned long int total_forks=0; /* Handle normal Linux uptimes. */
|
int last_pid=0;
|
int last_pid=0;
|
|
|
static inline int find_empty_process(void)
|
static inline int find_empty_process(void)
|
{
|
{
|
int i;
|
int i;
|
|
|
if (nr_tasks >= NR_TASKS - MIN_TASKS_LEFT_FOR_ROOT) {
|
if (nr_tasks >= NR_TASKS - MIN_TASKS_LEFT_FOR_ROOT) {
|
if (current->uid)
|
if (current->uid)
|
return -EAGAIN;
|
return -EAGAIN;
|
}
|
}
|
if (current->uid) {
|
if (current->uid) {
|
long max_tasks = current->rlim[RLIMIT_NPROC].rlim_cur;
|
long max_tasks = current->rlim[RLIMIT_NPROC].rlim_cur;
|
|
|
max_tasks--; /* count the new process.. */
|
max_tasks--; /* count the new process.. */
|
if (max_tasks < nr_tasks) {
|
if (max_tasks < nr_tasks) {
|
struct task_struct *p;
|
struct task_struct *p;
|
for_each_task (p) {
|
for_each_task (p) {
|
if (p->uid == current->uid)
|
if (p->uid == current->uid)
|
if (--max_tasks < 0)
|
if (--max_tasks < 0)
|
return -EAGAIN;
|
return -EAGAIN;
|
}
|
}
|
}
|
}
|
}
|
}
|
for (i = 0 ; i < NR_TASKS ; i++) {
|
for (i = 0 ; i < NR_TASKS ; i++) {
|
if (!task[i])
|
if (!task[i])
|
return i;
|
return i;
|
}
|
}
|
return -EAGAIN;
|
return -EAGAIN;
|
}
|
}
|
|
|
static int get_pid(unsigned long flags)
|
static int get_pid(unsigned long flags)
|
{
|
{
|
struct task_struct *p;
|
struct task_struct *p;
|
|
|
if (flags & CLONE_PID)
|
if (flags & CLONE_PID)
|
return current->pid;
|
return current->pid;
|
repeat:
|
repeat:
|
if ((++last_pid) & 0xffff8000)
|
if ((++last_pid) & 0xffff8000)
|
last_pid=1;
|
last_pid=1;
|
for_each_task (p) {
|
for_each_task (p) {
|
if (p->pid == last_pid ||
|
if (p->pid == last_pid ||
|
p->pgrp == last_pid ||
|
p->pgrp == last_pid ||
|
p->session == last_pid)
|
p->session == last_pid)
|
goto repeat;
|
goto repeat;
|
}
|
}
|
return last_pid;
|
return last_pid;
|
}
|
}
|
|
|
#ifndef NO_MM
|
#ifndef NO_MM
|
|
|
static inline int dup_mmap(struct mm_struct * mm)
|
static inline int dup_mmap(struct mm_struct * mm)
|
{
|
{
|
struct vm_area_struct * mpnt, **p, *tmp;
|
struct vm_area_struct * mpnt, **p, *tmp;
|
|
|
mm->mmap = NULL;
|
mm->mmap = NULL;
|
p = &mm->mmap;
|
p = &mm->mmap;
|
for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
|
for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
|
tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
|
tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
|
if (!tmp) {
|
if (!tmp) {
|
/* exit_mmap is called by the caller */
|
/* exit_mmap is called by the caller */
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
*tmp = *mpnt;
|
*tmp = *mpnt;
|
tmp->vm_flags &= ~VM_LOCKED;
|
tmp->vm_flags &= ~VM_LOCKED;
|
tmp->vm_mm = mm;
|
tmp->vm_mm = mm;
|
tmp->vm_next = NULL;
|
tmp->vm_next = NULL;
|
if (tmp->vm_inode) {
|
if (tmp->vm_inode) {
|
tmp->vm_inode->i_count++;
|
tmp->vm_inode->i_count++;
|
/* insert tmp into the share list, just after mpnt */
|
/* insert tmp into the share list, just after mpnt */
|
tmp->vm_next_share->vm_prev_share = tmp;
|
tmp->vm_next_share->vm_prev_share = tmp;
|
mpnt->vm_next_share = tmp;
|
mpnt->vm_next_share = tmp;
|
tmp->vm_prev_share = mpnt;
|
tmp->vm_prev_share = mpnt;
|
}
|
}
|
if (copy_page_range(mm, current->mm, tmp)) {
|
if (copy_page_range(mm, current->mm, tmp)) {
|
/* link into the linked list for exit_mmap */
|
/* link into the linked list for exit_mmap */
|
*p = tmp;
|
*p = tmp;
|
p = &tmp->vm_next;
|
p = &tmp->vm_next;
|
/* exit_mmap is called by the caller */
|
/* exit_mmap is called by the caller */
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
if (tmp->vm_ops && tmp->vm_ops->open)
|
if (tmp->vm_ops && tmp->vm_ops->open)
|
tmp->vm_ops->open(tmp);
|
tmp->vm_ops->open(tmp);
|
*p = tmp;
|
*p = tmp;
|
p = &tmp->vm_next;
|
p = &tmp->vm_next;
|
}
|
}
|
build_mmap_avl(mm);
|
build_mmap_avl(mm);
|
flush_tlb_mm(current->mm);
|
flush_tlb_mm(current->mm);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
|
{
|
{
|
if (!(clone_flags & CLONE_VM)) {
|
if (!(clone_flags & CLONE_VM)) {
|
struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL);
|
struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL);
|
if (!mm)
|
if (!mm)
|
return -ENOMEM;
|
return -ENOMEM;
|
*mm = *current->mm;
|
*mm = *current->mm;
|
mm->count = 1;
|
mm->count = 1;
|
mm->def_flags = 0;
|
mm->def_flags = 0;
|
mm->mmap_sem = MUTEX;
|
mm->mmap_sem = MUTEX;
|
tsk->mm = mm;
|
tsk->mm = mm;
|
tsk->min_flt = tsk->maj_flt = 0;
|
tsk->min_flt = tsk->maj_flt = 0;
|
tsk->cmin_flt = tsk->cmaj_flt = 0;
|
tsk->cmin_flt = tsk->cmaj_flt = 0;
|
tsk->nswap = tsk->cnswap = 0;
|
tsk->nswap = tsk->cnswap = 0;
|
if (new_page_tables(tsk)) {
|
if (new_page_tables(tsk)) {
|
tsk->mm = NULL;
|
tsk->mm = NULL;
|
exit_mmap(mm);
|
exit_mmap(mm);
|
goto free_mm;
|
goto free_mm;
|
}
|
}
|
down(&mm->mmap_sem);
|
down(&mm->mmap_sem);
|
if (dup_mmap(mm)) {
|
if (dup_mmap(mm)) {
|
up(&mm->mmap_sem);
|
up(&mm->mmap_sem);
|
tsk->mm = NULL;
|
tsk->mm = NULL;
|
exit_mmap(mm);
|
exit_mmap(mm);
|
free_page_tables(mm);
|
free_page_tables(mm);
|
free_mm:
|
free_mm:
|
kfree(mm);
|
kfree(mm);
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
up(&mm->mmap_sem);
|
up(&mm->mmap_sem);
|
return 0;
|
return 0;
|
}
|
}
|
SET_PAGE_DIR(tsk, current->mm->pgd);
|
SET_PAGE_DIR(tsk, current->mm->pgd);
|
current->mm->count++;
|
current->mm->count++;
|
return 0;
|
return 0;
|
}
|
}
|
#else /* NO_MM */
|
#else /* NO_MM */
|
|
|
static inline int dup_mmap(struct mm_struct * mm)
|
static inline int dup_mmap(struct mm_struct * mm)
|
{
|
{
|
struct mm_tblock_struct * tmp = ¤t->mm->tblock;
|
struct mm_tblock_struct * tmp = ¤t->mm->tblock;
|
struct mm_tblock_struct * newtmp = &mm->tblock;
|
struct mm_tblock_struct * newtmp = &mm->tblock;
|
/*unsigned long flags;*/
|
/*unsigned long flags;*/
|
extern long realalloc, askedalloc;
|
extern long realalloc, askedalloc;
|
|
|
if (!mm)
|
if (!mm)
|
return -1;
|
return -1;
|
|
|
mm->tblock.rblock = 0;
|
mm->tblock.rblock = 0;
|
mm->tblock.next = 0;
|
mm->tblock.next = 0;
|
|
|
/*save_flags(flags); cli();*/
|
/*save_flags(flags); cli();*/
|
|
|
while((tmp = tmp->next)) {
|
while((tmp = tmp->next)) {
|
newtmp->next = kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL);
|
newtmp->next = kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL);
|
if (!newtmp->next) {
|
if (!newtmp->next) {
|
/*restore_flags(flags);*/
|
/*restore_flags(flags);*/
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
realalloc += ksize(newtmp->next);
|
realalloc += ksize(newtmp->next);
|
askedalloc += sizeof(struct mm_tblock_struct);
|
askedalloc += sizeof(struct mm_tblock_struct);
|
newtmp->next->rblock = tmp->rblock;
|
newtmp->next->rblock = tmp->rblock;
|
if (tmp->rblock)
|
if (tmp->rblock)
|
tmp->rblock->refcount++;
|
tmp->rblock->refcount++;
|
newtmp->next->next = 0;
|
newtmp->next->next = 0;
|
newtmp = newtmp->next;
|
newtmp = newtmp->next;
|
}
|
}
|
/*restore_flags(flags);*/
|
/*restore_flags(flags);*/
|
|
|
return 0;
|
return 0;
|
|
|
}
|
}
|
|
|
static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
|
{
|
{
|
if (!(clone_flags & CLONE_VM)) {
|
if (!(clone_flags & CLONE_VM)) {
|
struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL);
|
struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL);
|
if (!mm)
|
if (!mm)
|
return -ENOMEM;
|
return -ENOMEM;
|
*mm = *current->mm;
|
*mm = *current->mm;
|
mm->count = 1;
|
mm->count = 1;
|
mm->def_flags = 0;
|
mm->def_flags = 0;
|
mm->vforkwait = 0;
|
mm->vforkwait = 0;
|
tsk->mm = mm;
|
tsk->mm = mm;
|
if (tsk->mm->executable)
|
if (tsk->mm->executable)
|
tsk->mm->executable->i_count++;
|
tsk->mm->executable->i_count++;
|
tsk->min_flt = tsk->maj_flt = 0;
|
tsk->min_flt = tsk->maj_flt = 0;
|
tsk->cmin_flt = tsk->cmaj_flt = 0;
|
tsk->cmin_flt = tsk->cmaj_flt = 0;
|
tsk->nswap = tsk->cnswap = 0;
|
tsk->nswap = tsk->cnswap = 0;
|
if (dup_mmap(mm)) {
|
if (dup_mmap(mm)) {
|
tsk->mm = NULL;
|
tsk->mm = NULL;
|
exit_mmap(mm);
|
exit_mmap(mm);
|
kfree(mm);
|
kfree(mm);
|
return -ENOMEM;
|
return -ENOMEM;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
current->mm->count++;
|
current->mm->count++;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#endif /* NO_MM */
|
#endif /* NO_MM */
|
|
|
static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
|
{
|
{
|
if (clone_flags & CLONE_FS) {
|
if (clone_flags & CLONE_FS) {
|
current->fs->count++;
|
current->fs->count++;
|
return 0;
|
return 0;
|
}
|
}
|
tsk->fs = kmalloc(sizeof(*tsk->fs), GFP_KERNEL);
|
tsk->fs = kmalloc(sizeof(*tsk->fs), GFP_KERNEL);
|
if (!tsk->fs)
|
if (!tsk->fs)
|
return -1;
|
return -1;
|
tsk->fs->count = 1;
|
tsk->fs->count = 1;
|
tsk->fs->umask = current->fs->umask;
|
tsk->fs->umask = current->fs->umask;
|
if ((tsk->fs->root = current->fs->root))
|
if ((tsk->fs->root = current->fs->root))
|
tsk->fs->root->i_count++;
|
tsk->fs->root->i_count++;
|
if ((tsk->fs->pwd = current->fs->pwd))
|
if ((tsk->fs->pwd = current->fs->pwd))
|
tsk->fs->pwd->i_count++;
|
tsk->fs->pwd->i_count++;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk)
|
{
|
{
|
int i;
|
int i;
|
struct files_struct *oldf, *newf;
|
struct files_struct *oldf, *newf;
|
struct file **old_fds, **new_fds;
|
struct file **old_fds, **new_fds;
|
|
|
oldf = current->files;
|
oldf = current->files;
|
if (clone_flags & CLONE_FILES) {
|
if (clone_flags & CLONE_FILES) {
|
oldf->count++;
|
oldf->count++;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
newf = kmalloc(sizeof(*newf), GFP_KERNEL);
|
newf = kmalloc(sizeof(*newf), GFP_KERNEL);
|
tsk->files = newf;
|
tsk->files = newf;
|
if (!newf)
|
if (!newf)
|
return -1;
|
return -1;
|
|
|
newf->count = 1;
|
newf->count = 1;
|
newf->close_on_exec = oldf->close_on_exec;
|
newf->close_on_exec = oldf->close_on_exec;
|
newf->open_fds = oldf->open_fds;
|
newf->open_fds = oldf->open_fds;
|
|
|
old_fds = oldf->fd;
|
old_fds = oldf->fd;
|
new_fds = newf->fd;
|
new_fds = newf->fd;
|
for (i = NR_OPEN; i != 0; i--) {
|
for (i = NR_OPEN; i != 0; i--) {
|
struct file * f = *old_fds;
|
struct file * f = *old_fds;
|
old_fds++;
|
old_fds++;
|
*new_fds = f;
|
*new_fds = f;
|
new_fds++;
|
new_fds++;
|
if (f)
|
if (f)
|
f->f_count++;
|
f->f_count++;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
|
{
|
{
|
if (clone_flags & CLONE_SIGHAND) {
|
if (clone_flags & CLONE_SIGHAND) {
|
current->sig->count++;
|
current->sig->count++;
|
return 0;
|
return 0;
|
}
|
}
|
tsk->sig = kmalloc(sizeof(*tsk->sig), GFP_KERNEL);
|
tsk->sig = kmalloc(sizeof(*tsk->sig), GFP_KERNEL);
|
if (!tsk->sig)
|
if (!tsk->sig)
|
return -1;
|
return -1;
|
tsk->sig->count = 1;
|
tsk->sig->count = 1;
|
memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action));
|
memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action));
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Ok, this is the main fork-routine. It copies the system process
|
* Ok, this is the main fork-routine. It copies the system process
|
* information (task[nr]) and sets up the necessary registers. It
|
* information (task[nr]) and sets up the necessary registers. It
|
* also copies the data segment in its entirety.
|
* also copies the data segment in its entirety.
|
*/
|
*/
|
int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
|
int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
|
{
|
{
|
int nr;
|
int nr;
|
int i;
|
int i;
|
int error = -ENOMEM;
|
int error = -ENOMEM;
|
unsigned long new_stack;
|
unsigned long new_stack;
|
struct task_struct *p;
|
struct task_struct *p;
|
|
|
p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL);
|
p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL);
|
if (!p)
|
if (!p)
|
goto bad_fork;
|
goto bad_fork;
|
|
|
new_stack = alloc_kernel_stack();
|
new_stack = alloc_kernel_stack();
|
if (!new_stack)
|
if (!new_stack)
|
goto bad_fork_free_p;
|
goto bad_fork_free_p;
|
|
|
error = -EAGAIN;
|
error = -EAGAIN;
|
nr = find_empty_process();
|
nr = find_empty_process();
|
if (nr < 0)
|
if (nr < 0)
|
goto bad_fork_free_stack;
|
goto bad_fork_free_stack;
|
|
|
*p = *current;
|
*p = *current;
|
|
|
if (p->exec_domain && p->exec_domain->use_count)
|
if (p->exec_domain && p->exec_domain->use_count)
|
(*p->exec_domain->use_count)++;
|
(*p->exec_domain->use_count)++;
|
if (p->binfmt && p->binfmt->use_count)
|
if (p->binfmt && p->binfmt->use_count)
|
(*p->binfmt->use_count)++;
|
(*p->binfmt->use_count)++;
|
|
|
p->did_exec = 0;
|
p->did_exec = 0;
|
p->swappable = 0;
|
p->swappable = 0;
|
p->kernel_stack_page = new_stack;
|
p->kernel_stack_page = new_stack;
|
*(unsigned long *) p->kernel_stack_page = STACK_MAGIC;
|
*(unsigned long *) p->kernel_stack_page = STACK_MAGIC;
|
for(i=1;i<(PAGE_SIZE/sizeof(long));i++)
|
for(i=1;i<(PAGE_SIZE/sizeof(long));i++)
|
((unsigned long*)p->kernel_stack_page)[i] = STACK_UNTOUCHED_MAGIC;
|
((unsigned long*)p->kernel_stack_page)[i] = STACK_UNTOUCHED_MAGIC;
|
p->state = TASK_UNINTERRUPTIBLE;
|
p->state = TASK_UNINTERRUPTIBLE;
|
p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV);
|
p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV);
|
p->flags |= PF_FORKNOEXEC;
|
p->flags |= PF_FORKNOEXEC;
|
p->pid = get_pid(clone_flags);
|
p->pid = get_pid(clone_flags);
|
p->next_run = NULL;
|
p->next_run = NULL;
|
p->prev_run = NULL;
|
p->prev_run = NULL;
|
p->p_pptr = p->p_opptr = current;
|
p->p_pptr = p->p_opptr = current;
|
p->p_cptr = NULL;
|
p->p_cptr = NULL;
|
init_waitqueue(&p->wait_chldexit);
|
init_waitqueue(&p->wait_chldexit);
|
p->signal = 0;
|
p->signal = 0;
|
p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
|
p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
|
p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;
|
p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;
|
init_timer(&p->real_timer);
|
init_timer(&p->real_timer);
|
p->real_timer.data = (unsigned long) p;
|
p->real_timer.data = (unsigned long) p;
|
p->leader = 0; /* session leadership doesn't inherit */
|
p->leader = 0; /* session leadership doesn't inherit */
|
p->tty_old_pgrp = 0;
|
p->tty_old_pgrp = 0;
|
p->utime = p->stime = 0;
|
p->utime = p->stime = 0;
|
p->cutime = p->cstime = 0;
|
p->cutime = p->cstime = 0;
|
#ifdef __SMP__
|
#ifdef __SMP__
|
p->processor = NO_PROC_ID;
|
p->processor = NO_PROC_ID;
|
p->lock_depth = 1;
|
p->lock_depth = 1;
|
#endif
|
#endif
|
p->start_time = jiffies;
|
p->start_time = jiffies;
|
task[nr] = p;
|
task[nr] = p;
|
SET_LINKS(p);
|
SET_LINKS(p);
|
nr_tasks++;
|
nr_tasks++;
|
|
|
error = -ENOMEM;
|
error = -ENOMEM;
|
/* copy all the process information */
|
/* copy all the process information */
|
if (copy_files(clone_flags, p))
|
if (copy_files(clone_flags, p))
|
goto bad_fork_cleanup;
|
goto bad_fork_cleanup;
|
|
|
if (copy_fs(clone_flags, p))
|
if (copy_fs(clone_flags, p))
|
goto bad_fork_cleanup_files;
|
goto bad_fork_cleanup_files;
|
|
|
if (copy_sighand(clone_flags, p))
|
if (copy_sighand(clone_flags, p))
|
goto bad_fork_cleanup_fs;
|
goto bad_fork_cleanup_fs;
|
|
|
if (copy_mm(clone_flags, p))
|
if (copy_mm(clone_flags, p))
|
goto bad_fork_cleanup_sighand;
|
goto bad_fork_cleanup_sighand;
|
|
|
copy_thread(nr, clone_flags, usp, p, regs);
|
copy_thread(nr, clone_flags, usp, p, regs);
|
p->semundo = NULL;
|
p->semundo = NULL;
|
|
|
/* ok, now we should be set up.. */
|
/* ok, now we should be set up.. */
|
p->swappable = 1;
|
p->swappable = 1;
|
p->exit_signal = clone_flags & CSIGNAL;
|
p->exit_signal = clone_flags & CSIGNAL;
|
p->counter = (current->counter >>= 1);
|
p->counter = (current->counter >>= 1);
|
wake_up_process(p); /* do this last, just in case */
|
wake_up_process(p); /* do this last, just in case */
|
++total_forks;
|
++total_forks;
|
#ifdef NO_MM
|
#ifdef NO_MM
|
if (clone_flags & CLONE_WAIT) {
|
if (clone_flags & CLONE_WAIT) {
|
sleep_on(¤t->mm->vforkwait);
|
sleep_on(¤t->mm->vforkwait);
|
}
|
}
|
#endif /*NO_MM*/
|
#endif /*NO_MM*/
|
|
|
return p->pid;
|
return p->pid;
|
|
|
bad_fork_cleanup_sighand:
|
bad_fork_cleanup_sighand:
|
exit_sighand(p);
|
exit_sighand(p);
|
bad_fork_cleanup_fs:
|
bad_fork_cleanup_fs:
|
exit_fs(p);
|
exit_fs(p);
|
bad_fork_cleanup_files:
|
bad_fork_cleanup_files:
|
exit_files(p);
|
exit_files(p);
|
bad_fork_cleanup:
|
bad_fork_cleanup:
|
if (p->exec_domain && p->exec_domain->use_count)
|
if (p->exec_domain && p->exec_domain->use_count)
|
(*p->exec_domain->use_count)--;
|
(*p->exec_domain->use_count)--;
|
if (p->binfmt && p->binfmt->use_count)
|
if (p->binfmt && p->binfmt->use_count)
|
(*p->binfmt->use_count)--;
|
(*p->binfmt->use_count)--;
|
task[nr] = NULL;
|
task[nr] = NULL;
|
REMOVE_LINKS(p);
|
REMOVE_LINKS(p);
|
nr_tasks--;
|
nr_tasks--;
|
bad_fork_free_stack:
|
bad_fork_free_stack:
|
free_kernel_stack(new_stack);
|
free_kernel_stack(new_stack);
|
bad_fork_free_p:
|
bad_fork_free_p:
|
kfree(p);
|
kfree(p);
|
bad_fork:
|
bad_fork:
|
return error;
|
return error;
|
}
|
}
|
|
|