fork.c 6.6 KB
Newer Older
1 2 3
/*
 *  linux/kernel/fork.c
 *
4
 *  Copyright (C) 1991, 1992  Linus Torvalds
5 6
 */

7 8
/*
 *  'fork.c' contains the help-routines for the 'fork' system call
9
 * (see also system_call.s).
10 11 12 13
 * 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()'
 */

14
#include <linux/errno.h>
15 16
#include <linux/sched.h>
#include <linux/kernel.h>
17 18
#include <linux/mm.h>
#include <linux/stddef.h>
19
#include <linux/unistd.h>
20
#include <linux/segment.h>
21
#include <linux/ptrace.h>
Linus Torvalds's avatar
Linus Torvalds committed
22
#include <linux/malloc.h>
Linus Torvalds's avatar
Linus Torvalds committed
23
#include <linux/ldt.h>
24

25 26 27
#include <asm/segment.h>
#include <asm/system.h>

28 29 30
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");

/* These should maybe be in <linux/tasks.h> */
31

32
#define MAX_TASKS_PER_USER (NR_TASKS/2)
33
#define MIN_TASKS_LEFT_FOR_ROOT 4
34

35
extern int shm_fork(struct task_struct *, struct task_struct *);
36 37
long last_pid=0;

38 39
static int find_empty_process(void)
{
Linus Torvalds's avatar
Linus Torvalds committed
40 41
	int free_task;
	int i, tasks_free;
42
	int this_user_tasks;
43

44
repeat:
45
	if ((++last_pid) & 0xffff8000)
46
		last_pid=1;
47
	this_user_tasks = 0;
Linus Torvalds's avatar
Linus Torvalds committed
48 49 50 51 52 53 54
	tasks_free = 0;
	free_task = -EAGAIN;
	i = NR_TASKS;
	while (--i > 0) {
		if (!task[i]) {
			free_task = i;
			tasks_free++;
55
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
56
		}
57 58
		if (task[i]->uid == current->uid)
			this_user_tasks++;
Linus Torvalds's avatar
Linus Torvalds committed
59 60
		if (task[i]->pid == last_pid || task[i]->pgrp == last_pid ||
		    task[i]->session == last_pid)
61 62
			goto repeat;
	}
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66 67
	if (tasks_free <= MIN_TASKS_LEFT_FOR_ROOT ||
	    this_user_tasks > MAX_TASKS_PER_USER)
		if (current->uid)
			return -EAGAIN;
	return free_task;
68 69
}

70
static struct file * copy_fd(struct file * old_file)
71
{
72
	struct file * new_file = get_empty_filp();
73 74
	int error;

75 76 77 78 79 80 81
	if (new_file) {
		memcpy(new_file,old_file,sizeof(struct file));
		new_file->f_count = 1;
		if (new_file->f_inode)
			new_file->f_inode->i_count++;
		if (new_file->f_op && new_file->f_op->open) {
			error = new_file->f_op->open(new_file->f_inode,new_file);
82
			if (error) {
83 84 85
				iput(new_file->f_inode);
				new_file->f_count = 0;
				new_file = NULL;
86 87 88
			}
		}
	}
89
	return new_file;
90 91
}

Linus Torvalds's avatar
Linus Torvalds committed
92
static int dup_mmap(struct task_struct * tsk)
93 94 95
{
	struct vm_area_struct * mpnt, **p, *tmp;

Linus Torvalds's avatar
Linus Torvalds committed
96 97 98
	tsk->mm->mmap = NULL;
	p = &tsk->mm->mmap;
	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112
		tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
		if (!tmp)
			return -ENOMEM;
		*tmp = *mpnt;
		tmp->vm_task = tsk;
		tmp->vm_next = NULL;
		if (tmp->vm_inode)
			tmp->vm_inode->i_count++;
		*p = tmp;
		p = &tmp->vm_next;
	}
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/*
 * SHAREFD not yet implemented..
 */
static void copy_files(unsigned long clone_flags, struct task_struct * p)
{
	int i;
	struct file * f;

	if (clone_flags & COPYFD) {
		for (i=0; i<NR_OPEN;i++)
			if ((f = p->files->fd[i]) != NULL)
				p->files->fd[i] = copy_fd(f);
	} else {
		for (i=0; i<NR_OPEN;i++)
			if ((f = p->files->fd[i]) != NULL)
				f->f_count++;
	}
}

/*
 * CLONEVM not yet correctly implemented: needs to clone the mmap
 * instead of duplicating it..
 */
static int copy_mm(unsigned long clone_flags, struct task_struct * p)
{
	if (clone_flags & COPYVM) {
		p->mm->swappable = 1;
		p->mm->min_flt = p->mm->maj_flt = 0;
		p->mm->cmin_flt = p->mm->cmaj_flt = 0;
		if (copy_page_tables(p))
			return 1;
		dup_mmap(p);
	} else {
		if (clone_page_tables(p))
			return 1;
		dup_mmap(p);		/* wrong.. */
	}
Linus Torvalds's avatar
Linus Torvalds committed
150
	return shm_fork(current, p);
Linus Torvalds's avatar
Linus Torvalds committed
151 152 153 154 155 156 157 158 159 160 161 162
}

static void copy_fs(unsigned long clone_flags, struct task_struct * p)
{
	if (current->fs->pwd)
		current->fs->pwd->i_count++;
	if (current->fs->root)
		current->fs->root->i_count++;
	if (current->executable)
		current->executable->i_count++;
}

163
#define IS_CLONE (regs.orig_eax == __NR_clone)
164

165 166 167
/*
 *  Ok, this is the main fork-routine. It copies the system process
 * information (task[nr]) and sets up the necessary registers. It
168
 * also copies the data segment in its entirety.
169
 */
170
asmlinkage int sys_fork(struct pt_regs regs)
171
{
172
	struct pt_regs * childregs;
173
	struct task_struct *p;
174
	int i,nr;
175
	unsigned long clone_flags = COPYVM | SIGCHLD;
176

177
	if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL)))
178
		goto bad_fork;
179
	nr = find_empty_process();
180 181
	if (nr < 0)
		goto bad_fork_free;
Linus Torvalds's avatar
Linus Torvalds committed
182
	task[nr] = p;
183
	*p = *current;
Linus Torvalds's avatar
Linus Torvalds committed
184 185 186 187 188 189

	if (p->exec_domain && p->exec_domain->use_count)
		(*p->exec_domain->use_count)++;
	if (p->binfmt && p->binfmt->use_count)
		(*p->binfmt->use_count)++;

Linus Torvalds's avatar
Linus Torvalds committed
190
	p->did_exec = 0;
191
	p->kernel_stack_page = 0;
Linus Torvalds's avatar
Linus Torvalds committed
192
	p->state = TASK_UNINTERRUPTIBLE;
193
	p->flags &= ~(PF_PTRACED|PF_TRACESYS);
194
	p->pid = last_pid;
195
	p->p_pptr = p->p_opptr = current;
196
	p->p_cptr = NULL;
197
	SET_LINKS(p);
198
	p->signal = 0;
199 200
	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;
201 202 203 204
	p->leader = 0;		/* process leadership doesn't inherit */
	p->utime = p->stime = 0;
	p->cutime = p->cstime = 0;
	p->start_time = jiffies;
205 206 207
/*
 * set up new TSS and kernel stack
 */
Linus Torvalds's avatar
Linus Torvalds committed
208
	if (!(p->kernel_stack_page = get_free_page(GFP_KERNEL)))
209
		goto bad_fork_cleanup;
Linus Torvalds's avatar
Linus Torvalds committed
210
	*(unsigned long *)p->kernel_stack_page = STACK_MAGIC;
211 212 213 214
	p->tss.es = KERNEL_DS;
	p->tss.cs = KERNEL_CS;
	p->tss.ss = KERNEL_DS;
	p->tss.ds = KERNEL_DS;
215
	p->tss.fs = USER_DS;
216
	p->tss.gs = KERNEL_DS;
217
	p->tss.ss0 = KERNEL_DS;
218 219 220 221 222 223 224 225 226
	p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
	p->tss.tr = _TSS(nr);
	childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
	p->tss.esp = (unsigned long) childregs;
	p->tss.eip = (unsigned long) ret_from_sys_call;
	*childregs = regs;
	childregs->eax = 0;
	p->tss.back_link = 0;
	p->tss.eflags = regs.eflags & 0xffffcfff;	/* iopl is always 0 for a new process */
227
	if (IS_CLONE) {
228 229 230 231
		if (regs.ebx)
			childregs->esp = regs.ebx;
		clone_flags = regs.ecx;
		if (childregs->esp == regs.esp)
232 233 234
			clone_flags |= COPYVM;
	}
	p->exit_signal = clone_flags & CSIGNAL;
235
	p->tss.ldt = _LDT(nr);
236
	if (p->ldt) {
Linus Torvalds's avatar
Linus Torvalds committed
237 238 239
		p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
		if (p->ldt != NULL)
			memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
240
	}
241 242
	p->tss.bitmap = offsetof(struct tss_struct,io_bitmap);
	for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */
243
		p->tss.io_bitmap[i] = ~0;
244
	if (last_task_used_math == current)
245
		__asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387));
Linus Torvalds's avatar
Linus Torvalds committed
246
	if (copy_mm(clone_flags, p))
247
		goto bad_fork_cleanup;
Linus Torvalds's avatar
Linus Torvalds committed
248 249
	copy_files(clone_flags, p);
	copy_fs(clone_flags, p);
250
	set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
251 252 253 254 255
	if (p->ldt)
		set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
	else
		set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);

256
	p->counter = current->counter >> 1;
Linus Torvalds's avatar
Linus Torvalds committed
257
	p->state = TASK_RUNNING;	/* do this last, just in case */
258
	return p->pid;
259 260 261 262 263 264 265 266
bad_fork_cleanup:
	task[nr] = NULL;
	REMOVE_LINKS(p);
	free_page(p->kernel_stack_page);
bad_fork_free:
	free_page((long) p);
bad_fork:
	return -EAGAIN;
267
}