/*
 *  linux/kernel/fork.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */

/*
 * 680x0 support by Hamish Macdonald
 */

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

#include <asm/segment.h>
#include <asm/system.h>

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>

#define MAX_TASKS_PER_USER (NR_TASKS/2)

long last_pid=0;

static int find_empty_process(void)
{
	int i, task_nr;
	int this_user_tasks;

repeat:
	if ((++last_pid) & 0xffff8000)
		last_pid=1;
	this_user_tasks = 0;
	for(i=0 ; i < NR_TASKS ; i++) {
		if (!task[i])
			continue;
		if (task[i]->uid == current->uid)
			this_user_tasks++;
		if (task[i]->pid == last_pid || task[i]->pgrp == last_pid)
			goto repeat;
	}
	if (this_user_tasks > MAX_TASKS_PER_USER && !suser())
		return -EAGAIN;
/* Only the super-user can fill the last available slot */
	task_nr = 0;
	for(i=1 ; i<NR_TASKS ; i++)
		if (!task[i])
			if (task_nr)
				return task_nr;
			else
				task_nr = i;
	if (task_nr && suser())
		return task_nr;
	return -EAGAIN;
}

/*
 *  Ok, this is the main fork-routine. It copies the system process
 * information (task[nr]) and sets up the necessary registers. It
 * also copies the data segment in it's entirety.
 */
int sys_fork(void)
{
    struct task_struct *p;
    int i,nr;
    struct file *f;

    p = (struct task_struct *) get_free_page(GFP_KERNEL);

    if (!p)
	return -EAGAIN;

    nr = find_empty_process();
    if (nr < 0) {
	free_page((unsigned long) p);
	return nr;
    }

#ifdef DEBUG
    printk ("fork: task # is %d\n", nr);
#endif

    task[nr] = p;
    *p = *current;
    p->kernel_stack_page = 0;
    p->state = TASK_UNINTERRUPTIBLE;
    p->flags &= ~(PF_PTRACED|PF_TRACESYS);
    p->pid = last_pid;
    if (p->pid > 1)
	p->swappable = 1;
    p->p_pptr = p->p_opptr = current;
    p->p_cptr = NULL;
    SET_LINKS(p);
    p->signal = 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->leader = 0;		/* process leadership doesn't inherit */
    p->utime = p->stime = 0;
    p->cutime = p->cstime = 0;
    p->min_flt = p->maj_flt = 0;
    p->cmin_flt = p->cmaj_flt = 0;
    p->start_time = jiffies;

#if 0
    if (last_task_used_math == current)
	__asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));
#endif

    /* allocate and copy kernel stack */
    p->kernel_stack_page = get_free_page(GFP_KERNEL);

    if (!p->kernel_stack_page || copy_page_tables(p)) {
	task[nr] = NULL;
	REMOVE_LINKS(p);
	free_page(p->kernel_stack_page);
	free_page((long) p);
	return -EAGAIN;
    }

    memcpy ((void *)p->kernel_stack_page,
	   (void *)current->kernel_stack_page, PAGE_SIZE);

    for (i=0; i<NR_OPEN;i++)
	if ((f = p->filp[i]) != NULL)
	    f->f_count++;
    if (current->pwd)
	current->pwd->i_count++;
    if (current->root)
	current->root->i_count++;
    if (current->executable)
	current->executable->i_count++;
    for (i=0; i < current->numlibraries ; i++)
	if (current->libraries[i].library)
	    current->libraries[i].library->i_count++;

    /* setup new process context so that it can be loaded in schedule */
    setjmp(&p->swtch);
    p->pjmp = &p->jmpbuff;
    if (setjmp(p->pjmp))
    {
#ifdef DEBUG
	printk ("new task returning 0\n");
#endif
	return 0;
    }

#ifdef DEBUG
    printk ("new task is %#lx\n", p);
#endif

    p->state = TASK_RUNNING;	/* do this last, just in case */
    return p->pid;
}
