diff options
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 70 | 
1 files changed, 49 insertions, 21 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 1bfefc6f96a4..7d5f0f118a63 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -287,6 +287,11 @@ static void set_max_threads(unsigned int max_threads_suggested)  	max_threads = clamp_t(u64, threads, MIN_THREADS, MAX_THREADS);  } +#ifdef CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT +/* Initialized by the architecture: */ +int arch_task_struct_size __read_mostly; +#endif +  void __init fork_init(void)  {  #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR @@ -295,7 +300,7 @@ void __init fork_init(void)  #endif  	/* create a slab on which task_structs can be allocated */  	task_struct_cachep = -		kmem_cache_create("task_struct", sizeof(struct task_struct), +		kmem_cache_create("task_struct", arch_task_struct_size,  			ARCH_MIN_TASKALIGN, SLAB_PANIC | SLAB_NOTRACK, NULL);  #endif @@ -449,8 +454,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)  		tmp->vm_mm = mm;  		if (anon_vma_fork(tmp, mpnt))  			goto fail_nomem_anon_vma_fork; -		tmp->vm_flags &= ~VM_LOCKED; +		tmp->vm_flags &= ~(VM_LOCKED|VM_UFFD_MISSING|VM_UFFD_WP);  		tmp->vm_next = tmp->vm_prev = NULL; +		tmp->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;  		file = tmp->vm_file;  		if (file) {  			struct inode *inode = file_inode(file); @@ -1067,6 +1073,7 @@ static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)  	rcu_assign_pointer(tsk->sighand, sig);  	if (!sig)  		return -ENOMEM; +  	atomic_set(&sig->count, 1);  	memcpy(sig->action, current->sighand->action, sizeof(sig->action));  	return 0; @@ -1128,6 +1135,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)  	init_sigpending(&sig->shared_pending);  	INIT_LIST_HEAD(&sig->posix_timers);  	seqlock_init(&sig->stats_lock); +	prev_cputime_init(&sig->prev_cputime);  	hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  	sig->real_timer.function = it_real_fn; @@ -1239,6 +1247,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,  {  	int retval;  	struct task_struct *p; +	void *cgrp_ss_priv[CGROUP_CANFORK_COUNT] = {};  	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))  		return ERR_PTR(-EINVAL); @@ -1273,10 +1282,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	/*  	 * If the new process will be in a different pid or user namespace -	 * do not allow it to share a thread group or signal handlers or -	 * parent with the forking task. +	 * do not allow it to share a thread group with the forking task.  	 */ -	if (clone_flags & CLONE_SIGHAND) { +	if (clone_flags & CLONE_THREAD) {  		if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||  		    (task_active_pid_ns(current) !=  				current->nsproxy->pid_ns_for_children)) @@ -1335,9 +1343,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	p->utime = p->stime = p->gtime = 0;  	p->utimescaled = p->stimescaled = 0; -#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE -	p->prev_cputime.utime = p->prev_cputime.stime = 0; -#endif +	prev_cputime_init(&p->prev_cputime); +  #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN  	seqlock_init(&p->vtime_seqlock);  	p->vtime_snap = 0; @@ -1513,6 +1520,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	p->task_works = NULL;  	/* +	 * Ensure that the cgroup subsystem policies allow the new process to be +	 * forked. It should be noted the the new process's css_set can be changed +	 * between here and cgroup_post_fork() if an organisation operation is in +	 * progress. +	 */ +	retval = cgroup_can_fork(p, cgrp_ss_priv); +	if (retval) +		goto bad_fork_free_pid; + +	/*  	 * Make it visible to the rest of the system, but dont wake it up yet.  	 * Need tasklist lock for parent etc handling!  	 */ @@ -1548,7 +1565,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,  		spin_unlock(¤t->sighand->siglock);  		write_unlock_irq(&tasklist_lock);  		retval = -ERESTARTNOINTR; -		goto bad_fork_free_pid; +		goto bad_fork_cancel_cgroup;  	}  	if (likely(p->pid)) { @@ -1590,7 +1607,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	write_unlock_irq(&tasklist_lock);  	proc_fork_connector(p); -	cgroup_post_fork(p); +	cgroup_post_fork(p, cgrp_ss_priv);  	if (clone_flags & CLONE_THREAD)  		threadgroup_change_end(current);  	perf_event_fork(p); @@ -1600,6 +1617,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,  	return p; +bad_fork_cancel_cgroup: +	cgroup_cancel_fork(p, cgrp_ss_priv);  bad_fork_free_pid:  	if (pid != &init_struct_pid)  		free_pid(pid); @@ -1866,13 +1885,21 @@ static int check_unshare_flags(unsigned long unshare_flags)  				CLONE_NEWUSER|CLONE_NEWPID))  		return -EINVAL;  	/* -	 * Not implemented, but pretend it works if there is nothing to -	 * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND -	 * needs to unshare vm. +	 * Not implemented, but pretend it works if there is nothing +	 * to unshare.  Note that unsharing the address space or the +	 * signal handlers also need to unshare the signal queues (aka +	 * CLONE_THREAD).  	 */  	if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { -		/* FIXME: get_task_mm() increments ->mm_users */ -		if (atomic_read(¤t->mm->mm_users) > 1) +		if (!thread_group_empty(current)) +			return -EINVAL; +	} +	if (unshare_flags & (CLONE_SIGHAND | CLONE_VM)) { +		if (atomic_read(¤t->sighand->count) > 1) +			return -EINVAL; +	} +	if (unshare_flags & CLONE_VM) { +		if (!current_is_single_threaded())  			return -EINVAL;  	} @@ -1936,21 +1963,22 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)  	int err;  	/* -	 * If unsharing a user namespace must also unshare the thread. +	 * If unsharing a user namespace must also unshare the thread group +	 * and unshare the filesystem root and working directories.  	 */  	if (unshare_flags & CLONE_NEWUSER)  		unshare_flags |= CLONE_THREAD | CLONE_FS;  	/* -	 * If unsharing a thread from a thread group, must also unshare vm. -	 */ -	if (unshare_flags & CLONE_THREAD) -		unshare_flags |= CLONE_VM; -	/*  	 * If unsharing vm, must also unshare signal handlers.  	 */  	if (unshare_flags & CLONE_VM)  		unshare_flags |= CLONE_SIGHAND;  	/* +	 * If unsharing a signal handlers, must also unshare the signal queues. +	 */ +	if (unshare_flags & CLONE_SIGHAND) +		unshare_flags |= CLONE_THREAD; +	/*  	 * If unsharing namespace, must also unshare filesystem information.  	 */  	if (unshare_flags & CLONE_NEWNS)  |