diff options
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 145 | 
1 files changed, 107 insertions, 38 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index cf65139615a0..03c1eaaa6ef5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -74,6 +74,7 @@  #include <linux/uprobes.h>  #include <linux/aio.h>  #include <linux/compiler.h> +#include <linux/sysctl.h>  #include <asm/pgtable.h>  #include <asm/pgalloc.h> @@ -88,6 +89,16 @@  #include <trace/events/task.h>  /* + * Minimum number of threads to boot the kernel + */ +#define MIN_THREADS 20 + +/* + * Maximum number of threads + */ +#define MAX_THREADS FUTEX_TID_MASK + +/*   * Protected counters by write_lock_irq(&tasklist_lock)   */  unsigned long total_forks;	/* Handle normal Linux uptimes. */ @@ -253,7 +264,30 @@ EXPORT_SYMBOL_GPL(__put_task_struct);  void __init __weak arch_task_cache_init(void) { } -void __init fork_init(unsigned long mempages) +/* + * set_max_threads + */ +static void set_max_threads(unsigned int max_threads_suggested) +{ +	u64 threads; + +	/* +	 * The number of threads shall be limited such that the thread +	 * structures may only consume a small part of the available memory. +	 */ +	if (fls64(totalram_pages) + fls64(PAGE_SIZE) > 64) +		threads = MAX_THREADS; +	else +		threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE, +				    (u64) THREAD_SIZE * 8UL); + +	if (threads > max_threads_suggested) +		threads = max_threads_suggested; + +	max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS); +} + +void __init fork_init(void)  {  #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR  #ifndef ARCH_MIN_TASKALIGN @@ -268,18 +302,7 @@ void __init fork_init(unsigned long mempages)  	/* do the arch specific task caches init */  	arch_task_cache_init(); -	/* -	 * The default maximum number of threads is set to a safe -	 * value: the thread structures can take up at most half -	 * of memory. -	 */ -	max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE); - -	/* -	 * we need to allow at least 20 threads to boot a system -	 */ -	if (max_threads < 20) -		max_threads = 20; +	set_max_threads(MAX_THREADS);  	init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;  	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2; @@ -380,6 +403,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)  	 */  	down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); +	/* No ordering required: file already has been exposed. */ +	RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); +  	mm->total_vm = oldmm->total_vm;  	mm->shared_vm = oldmm->shared_vm;  	mm->exec_vm = oldmm->exec_vm; @@ -505,7 +531,13 @@ static inline void mm_free_pgd(struct mm_struct *mm)  	pgd_free(mm, mm->pgd);  }  #else -#define dup_mmap(mm, oldmm)	(0) +static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) +{ +	down_write(&oldmm->mmap_sem); +	RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); +	up_write(&oldmm->mmap_sem); +	return 0; +}  #define mm_alloc_pgd(mm)	(0)  #define mm_free_pgd(mm)  #endif /* CONFIG_MMU */ @@ -674,34 +706,53 @@ void mmput(struct mm_struct *mm)  }  EXPORT_SYMBOL_GPL(mmput); +/** + * set_mm_exe_file - change a reference to the mm's executable file + * + * This changes mm's executable file (shown as symlink /proc/[pid]/exe). + * + * Main users are mmput() and sys_execve(). Callers prevent concurrent + * invocations: in mmput() nobody alive left, in execve task is single + * threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the + * mm->exe_file, but does so without using set_mm_exe_file() in order + * to do avoid the need for any locks. + */  void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)  { +	struct file *old_exe_file; + +	/* +	 * It is safe to dereference the exe_file without RCU as +	 * this function is only called if nobody else can access +	 * this mm -- see comment above for justification. +	 */ +	old_exe_file = rcu_dereference_raw(mm->exe_file); +  	if (new_exe_file)  		get_file(new_exe_file); -	if (mm->exe_file) -		fput(mm->exe_file); -	mm->exe_file = new_exe_file; +	rcu_assign_pointer(mm->exe_file, new_exe_file); +	if (old_exe_file) +		fput(old_exe_file);  } +/** + * get_mm_exe_file - acquire a reference to the mm's executable file + * + * Returns %NULL if mm has no associated executable file. + * User must release file via fput(). + */  struct file *get_mm_exe_file(struct mm_struct *mm)  {  	struct file *exe_file; -	/* We need mmap_sem to protect against races with removal of exe_file */ -	down_read(&mm->mmap_sem); -	exe_file = mm->exe_file; -	if (exe_file) -		get_file(exe_file); -	up_read(&mm->mmap_sem); +	rcu_read_lock(); +	exe_file = rcu_dereference(mm->exe_file); +	if (exe_file && !get_file_rcu(exe_file)) +		exe_file = NULL; +	rcu_read_unlock();  	return exe_file;  } - -static void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm) -{ -	/* It's safe to write the exe_file pointer without exe_file_lock because -	 * this is called during fork when the task is not yet in /proc */ -	newmm->exe_file = get_mm_exe_file(oldmm); -} +EXPORT_SYMBOL(get_mm_exe_file);  /**   * get_task_mm - acquire a reference to the task's mm @@ -864,8 +915,6 @@ static struct mm_struct *dup_mm(struct task_struct *tsk)  	if (!mm_init(mm, tsk))  		goto fail_nomem; -	dup_mm_exe_file(oldmm, mm); -  	err = dup_mmap(mm, oldmm);  	if (err)  		goto free_pt; @@ -1279,9 +1328,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	if (nr_threads >= max_threads)  		goto bad_fork_cleanup_count; -	if (!try_module_get(task_thread_info(p)->exec_domain->module)) -		goto bad_fork_cleanup_count; -  	delayacct_tsk_init(p);	/* Must remain after dup_task_struct() */  	p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER);  	p->flags |= PF_FORKNOEXEC; @@ -1406,10 +1452,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,  		goto bad_fork_cleanup_io;  	if (pid != &init_struct_pid) { -		retval = -ENOMEM;  		pid = alloc_pid(p->nsproxy->pid_ns_for_children); -		if (!pid) +		if (IS_ERR(pid)) { +			retval = PTR_ERR(pid);  			goto bad_fork_cleanup_io; +		}  	}  	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; @@ -1590,7 +1637,6 @@ bad_fork_cleanup_threadgroup_lock:  	if (clone_flags & CLONE_THREAD)  		threadgroup_change_end(current);  	delayacct_tsk_free(p); -	module_put(task_thread_info(p)->exec_domain->module);  bad_fork_cleanup_count:  	atomic_dec(&p->cred->user->processes);  	exit_creds(p); @@ -2004,3 +2050,26 @@ int unshare_files(struct files_struct **displaced)  	task_unlock(task);  	return 0;  } + +int sysctl_max_threads(struct ctl_table *table, int write, +		       void __user *buffer, size_t *lenp, loff_t *ppos) +{ +	struct ctl_table t; +	int ret; +	int threads = max_threads; +	int min = MIN_THREADS; +	int max = MAX_THREADS; + +	t = *table; +	t.data = &threads; +	t.extra1 = &min; +	t.extra2 = &max; + +	ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); +	if (ret || !write) +		return ret; + +	set_max_threads(threads); + +	return 0; +}  |