diff options
Diffstat (limited to 'kernel/exit.c')
| -rw-r--r-- | kernel/exit.c | 174 | 
1 files changed, 67 insertions, 107 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index a949819055d..e5c4668f179 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -74,6 +74,7 @@ static void __unhash_process(struct task_struct *p, bool group_dead)  		__this_cpu_dec(process_counts);  	}  	list_del_rcu(&p->thread_group); +	list_del_rcu(&p->thread_node);  }  /* @@ -312,46 +313,7 @@ kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)  	}  } -/* - * Let kernel threads use this to say that they allow a certain signal. - * Must not be used if kthread was cloned with CLONE_SIGHAND. - */ -int allow_signal(int sig) -{ -	if (!valid_signal(sig) || sig < 1) -		return -EINVAL; - -	spin_lock_irq(¤t->sighand->siglock); -	/* This is only needed for daemonize()'ed kthreads */ -	sigdelset(¤t->blocked, sig); -	/* -	 * Kernel threads handle their own signals. Let the signal code -	 * know it'll be handled, so that they don't get converted to -	 * SIGKILL or just silently dropped. -	 */ -	current->sighand->action[(sig)-1].sa.sa_handler = (void __user *)2; -	recalc_sigpending(); -	spin_unlock_irq(¤t->sighand->siglock); -	return 0; -} - -EXPORT_SYMBOL(allow_signal); - -int disallow_signal(int sig) -{ -	if (!valid_signal(sig) || sig < 1) -		return -EINVAL; - -	spin_lock_irq(¤t->sighand->siglock); -	current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN; -	recalc_sigpending(); -	spin_unlock_irq(¤t->sighand->siglock); -	return 0; -} - -EXPORT_SYMBOL(disallow_signal); - -#ifdef CONFIG_MM_OWNER +#ifdef CONFIG_MEMCG  /*   * A task is exiting.   If it owned this mm, find a new owner for the mm.   */ @@ -394,14 +356,18 @@ retry:  	}  	/* -	 * Search through everything else. We should not get -	 * here often +	 * Search through everything else, we should not get here often.  	 */ -	do_each_thread(g, c) { -		if (c->mm == mm) -			goto assign_new_owner; -	} while_each_thread(g, c); - +	for_each_process(g) { +		if (g->flags & PF_KTHREAD) +			continue; +		for_each_thread(g, c) { +			if (c->mm == mm) +				goto assign_new_owner; +			if (c->mm) +				break; +		} +	}  	read_unlock(&tasklist_lock);  	/*  	 * We found no owner yet mm_users > 1: this implies that we are @@ -433,7 +399,7 @@ assign_new_owner:  	task_unlock(c);  	put_task_struct(c);  } -#endif /* CONFIG_MM_OWNER */ +#endif /* CONFIG_MEMCG */  /*   * Turn us into a lazy TLB process if we @@ -569,7 +535,7 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p,  	if (same_thread_group(p->real_parent, father))  		return; -	/* We don't want people slaying init.  */ +	/* We don't want people slaying init. */  	p->exit_signal = SIGCHLD;  	/* If it has exited notify the new parent about this child's death. */ @@ -783,9 +749,10 @@ void do_exit(long code)  	exit_shm(tsk);  	exit_files(tsk);  	exit_fs(tsk); +	if (group_dead) +		disassociate_ctty(1);  	exit_task_namespaces(tsk);  	exit_task_work(tsk); -	check_stack_usage();  	exit_thread();  	/* @@ -796,21 +763,17 @@ void do_exit(long code)  	 */  	perf_event_exit_task(tsk); -	cgroup_exit(tsk, 1); - -	if (group_dead) -		disassociate_ctty(1); +	cgroup_exit(tsk);  	module_put(task_thread_info(tsk)->exec_domain->module); -	proc_exit_connector(tsk); -  	/*  	 * FIXME: do that only when needed, using sched_exit tracepoint  	 */  	flush_ptrace_hw_breakpoint(tsk);  	exit_notify(tsk, group_dead); +	proc_exit_connector(tsk);  #ifdef CONFIG_NUMA  	task_lock(tsk);  	mpol_put(tsk->mempolicy); @@ -843,6 +806,7 @@ void do_exit(long code)  	validate_creds_for_do_exit(tsk); +	check_stack_usage();  	preempt_disable();  	if (tsk->nr_dirtied)  		__this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied); @@ -1037,17 +1001,13 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)  		return wait_noreap_copyout(wo, p, pid, uid, why, status);  	} +	traced = ptrace_reparented(p);  	/* -	 * Try to move the task's state to DEAD -	 * only one thread is allowed to do this: +	 * Move the task's state to DEAD/TRACE, only one thread can do this.  	 */ -	state = xchg(&p->exit_state, EXIT_DEAD); -	if (state != EXIT_ZOMBIE) { -		BUG_ON(state != EXIT_DEAD); +	state = traced && thread_group_leader(p) ? EXIT_TRACE : EXIT_DEAD; +	if (cmpxchg(&p->exit_state, EXIT_ZOMBIE, state) != EXIT_ZOMBIE)  		return 0; -	} - -	traced = ptrace_reparented(p);  	/*  	 * It can be ptraced but not reparented, check  	 * thread_group_leader() to filter out sub-threads. @@ -1108,7 +1068,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)  	/*  	 * Now we are sure this task is interesting, and no other -	 * thread can reap it because we set its state to EXIT_DEAD. +	 * thread can reap it because we its state == DEAD/TRACE.  	 */  	read_unlock(&tasklist_lock); @@ -1145,22 +1105,19 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)  	if (!retval)  		retval = pid; -	if (traced) { +	if (state == EXIT_TRACE) {  		write_lock_irq(&tasklist_lock);  		/* We dropped tasklist, ptracer could die and untrace */  		ptrace_unlink(p); -		/* -		 * If this is not a sub-thread, notify the parent. -		 * If parent wants a zombie, don't release it now. -		 */ -		if (thread_group_leader(p) && -		    !do_notify_parent(p, p->exit_signal)) { -			p->exit_state = EXIT_ZOMBIE; -			p = NULL; -		} + +		/* If parent wants a zombie, don't release it now */ +		state = EXIT_ZOMBIE; +		if (do_notify_parent(p, p->exit_signal)) +			state = EXIT_DEAD; +		p->exit_state = state;  		write_unlock_irq(&tasklist_lock);  	} -	if (p != NULL) +	if (state == EXIT_DEAD)  		release_task(p);  	return retval; @@ -1337,7 +1294,12 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)  static int wait_consider_task(struct wait_opts *wo, int ptrace,  				struct task_struct *p)  { -	int ret = eligible_child(wo, p); +	int ret; + +	if (unlikely(p->exit_state == EXIT_DEAD)) +		return 0; + +	ret = eligible_child(wo, p);  	if (!ret)  		return ret; @@ -1355,33 +1317,44 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,  		return 0;  	} -	/* dead body doesn't have much to contribute */ -	if (unlikely(p->exit_state == EXIT_DEAD)) { +	if (unlikely(p->exit_state == EXIT_TRACE)) {  		/* -		 * But do not ignore this task until the tracer does -		 * wait_task_zombie()->do_notify_parent(). +		 * ptrace == 0 means we are the natural parent. In this case +		 * we should clear notask_error, debugger will notify us.  		 */ -		if (likely(!ptrace) && unlikely(ptrace_reparented(p))) +		if (likely(!ptrace))  			wo->notask_error = 0;  		return 0;  	} -	/* slay zombie? */ -	if (p->exit_state == EXIT_ZOMBIE) { +	if (likely(!ptrace) && unlikely(p->ptrace)) {  		/* -		 * A zombie ptracee is only visible to its ptracer. -		 * Notification and reaping will be cascaded to the real -		 * parent when the ptracer detaches. +		 * If it is traced by its real parent's group, just pretend +		 * the caller is ptrace_do_wait() and reap this child if it +		 * is zombie. +		 * +		 * This also hides group stop state from real parent; otherwise +		 * a single stop can be reported twice as group and ptrace stop. +		 * If a ptracer wants to distinguish these two events for its +		 * own children it should create a separate process which takes +		 * the role of real parent.  		 */ -		if (likely(!ptrace) && unlikely(p->ptrace)) { -			/* it will become visible, clear notask_error */ -			wo->notask_error = 0; -			return 0; -		} +		if (!ptrace_reparented(p)) +			ptrace = 1; +	} +	/* slay zombie? */ +	if (p->exit_state == EXIT_ZOMBIE) {  		/* we don't reap group leaders with subthreads */ -		if (!delay_group_leader(p)) -			return wait_task_zombie(wo, p); +		if (!delay_group_leader(p)) { +			/* +			 * A zombie ptracee is only visible to its ptracer. +			 * Notification and reaping will be cascaded to the +			 * real parent when the ptracer detaches. +			 */ +			if (unlikely(ptrace) || likely(!p->ptrace)) +				return wait_task_zombie(wo, p); +		}  		/*  		 * Allow access to stopped/continued state via zombie by @@ -1407,19 +1380,6 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,  			wo->notask_error = 0;  	} else {  		/* -		 * If @p is ptraced by a task in its real parent's group, -		 * hide group stop/continued state when looking at @p as -		 * the real parent; otherwise, a single stop can be -		 * reported twice as group and ptrace stops. -		 * -		 * If a ptracer wants to distinguish the two events for its -		 * own children, it should create a separate process which -		 * takes the role of real parent. -		 */ -		if (likely(!ptrace) && p->ptrace && !ptrace_reparented(p)) -			return 0; - -		/*  		 * @p is alive and it's gonna stop, continue or exit, so  		 * there always is something to wait for.  		 */  | 
