Skip to content
Snippets Groups Projects
fork.c 42.5 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 *  linux/kernel/fork.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 *  'fork.c' contains the help-routines for the 'fork' system call
 * (see also entry.S and others).
 * Fork is rather simple, once you get the hang of it, but the memory
 * management can be a bitch. See 'mm/memory.c': 'copy_page_range()'
 */

#include <linux/slab.h>
#include <linux/init.h>
#include <linux/unistd.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/completion.h>
#include <linux/personality.h>
#include <linux/mempolicy.h>
#include <linux/sem.h>
#include <linux/file.h>
Al Viro's avatar
Al Viro committed
#include <linux/fdtable.h>
#include <linux/iocontext.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/key.h>
#include <linux/binfmts.h>
#include <linux/mman.h>
Andrea Arcangeli's avatar
Andrea Arcangeli committed
#include <linux/mmu_notifier.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/fs.h>
#include <linux/nsproxy.h>
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/cpu.h>
#include <linux/cgroup.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/security.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/swap.h>
#include <linux/syscalls.h>
#include <linux/jiffies.h>
#include <linux/futex.h>
#include <linux/compat.h>
#include <linux/kthread.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/rcupdate.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/ptrace.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/memcontrol.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/profile.h>
#include <linux/rmap.h>
#include <linux/ksm.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/acct.h>
#include <linux/tsacct_kern.h>
#include <linux/cn_proc.h>
#include <linux/freezer.h>
#include <linux/delayacct.h>
#include <linux/taskstats_kern.h>
#include <linux/tty.h>
#include <linux/blkdev.h>
#include <linux/fs_struct.h>
#include <linux/perf_event.h>
#include <linux/posix-timers.h>
#include <linux/user-return-notifier.h>
#include <linux/oom.h>
Andrea Arcangeli's avatar
Andrea Arcangeli committed
#include <linux/khugepaged.h>
Linus Torvalds's avatar
Linus Torvalds committed

#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>

Linus Torvalds's avatar
Linus Torvalds committed
/*
 * Protected counters by write_lock_irq(&tasklist_lock)
 */
unsigned long total_forks;	/* Handle normal Linux uptimes. */
int nr_threads;			/* The idle threads do not count.. */
Linus Torvalds's avatar
Linus Torvalds committed

int max_threads;		/* tunable limit on nr_threads */

DEFINE_PER_CPU(unsigned long, process_counts) = 0;

__cacheline_aligned DEFINE_RWLOCK(tasklist_lock);  /* outer */

#ifdef CONFIG_PROVE_RCU
int lockdep_tasklist_lock_is_held(void)
{
	return lockdep_is_held(&tasklist_lock);
}
EXPORT_SYMBOL_GPL(lockdep_tasklist_lock_is_held);
#endif /* #ifdef CONFIG_PROVE_RCU */
Linus Torvalds's avatar
Linus Torvalds committed

int nr_processes(void)
{
	int cpu;
	int total = 0;

	for_each_possible_cpu(cpu)
Linus Torvalds's avatar
Linus Torvalds committed
		total += per_cpu(process_counts, cpu);

	return total;
}

#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
# 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;
Linus Torvalds's avatar
Linus Torvalds committed
#endif

#ifndef __HAVE_ARCH_THREAD_INFO_ALLOCATOR
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
	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)
{
	free_pages((unsigned long)ti, THREAD_SIZE_ORDER);
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
/* SLAB cache for signal_struct structures (tsk->signal) */
static struct kmem_cache *signal_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

/* SLAB cache for sighand_struct structures (tsk->sighand) */
struct kmem_cache *sighand_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

/* SLAB cache for files_struct structures (tsk->files) */
struct kmem_cache *files_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

/* SLAB cache for fs_struct structures (tsk->fs) */
struct kmem_cache *fs_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

/* SLAB cache for vm_area_struct structures */
struct kmem_cache *vm_area_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

/* SLAB cache for mm_struct structures (tsk->mm) */
static struct kmem_cache *mm_cachep;
Linus Torvalds's avatar
Linus Torvalds committed

static void account_kernel_stack(struct thread_info *ti, int account)
{
	struct zone *zone = page_zone(virt_to_page(ti));

	mod_zone_page_state(zone, NR_KERNEL_STACK, account);
}

Linus Torvalds's avatar
Linus Torvalds committed
void free_task(struct task_struct *tsk)
{
	prop_local_destroy_single(&tsk->dirties);
	free_thread_info(tsk->stack);
	rt_mutex_debug_task_free(tsk);
Linus Torvalds's avatar
Linus Torvalds committed
	free_task_struct(tsk);
}
EXPORT_SYMBOL(free_task);

static inline void free_signal_struct(struct signal_struct *sig)
{
	sched_autogroup_exit(sig);
	kmem_cache_free(signal_cachep, sig);
}

static inline void put_signal_struct(struct signal_struct *sig)
{
	if (atomic_dec_and_test(&sig->sigcnt))
void __put_task_struct(struct task_struct *tsk)
Linus Torvalds's avatar
Linus Torvalds committed
{
Eugene Teo's avatar
Eugene Teo committed
	WARN_ON(!tsk->exit_state);
Linus Torvalds's avatar
Linus Torvalds committed
	WARN_ON(atomic_read(&tsk->usage));
	WARN_ON(tsk == current);

	delayacct_tsk_free(tsk);
	put_signal_struct(tsk->signal);
Linus Torvalds's avatar
Linus Torvalds committed

	if (!profile_handoff_task(tsk))
		free_task(tsk);
}
EXPORT_SYMBOL_GPL(__put_task_struct);
Loading
Loading full blame...