diff options
Diffstat (limited to 'kernel/exit.c')
| -rw-r--r-- | kernel/exit.c | 97 | 
1 files changed, 52 insertions, 45 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index f702a6a63686..b00a25bb4ab9 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -116,7 +116,7 @@ static void __exit_signal(struct task_struct *tsk)  		 * then notify it:  		 */  		if (sig->notify_count > 0 && !--sig->notify_count) -			wake_up_process(sig->group_exit_task); +			wake_up_process(sig->group_exec_task);  		if (tsk == sig->curr_target)  			sig->curr_target = next_thread(tsk); @@ -697,7 +697,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead)  	/* mt-exec, de_thread() is waiting for group leader */  	if (unlikely(tsk->signal->notify_count < 0)) -		wake_up_process(tsk->signal->group_exit_task); +		wake_up_process(tsk->signal->group_exec_task);  	write_unlock_irq(&tasklist_lock);  	list_for_each_entry_safe(p, n, &dead, ptrace_entry) { @@ -735,37 +735,22 @@ void __noreturn do_exit(long code)  	struct task_struct *tsk = current;  	int group_dead; -	/* -	 * We can get here from a kernel oops, sometimes with preemption off. -	 * Start by checking for critical errors. -	 * Then fix up important state like USER_DS and preemption. -	 * Then do everything else. -	 */ -  	WARN_ON(blk_needs_flush_plug(tsk)); -	if (unlikely(in_interrupt())) -		panic("Aiee, killing interrupt handler!"); -	if (unlikely(!tsk->pid)) -		panic("Attempted to kill the idle task!"); -  	/* -	 * If do_exit is called because this processes oopsed, it's possible +	 * If do_dead is called because this processes oopsed, it's possible  	 * that get_fs() was left as KERNEL_DS, so reset it to USER_DS before  	 * continuing. Amongst other possible reasons, this is to prevent  	 * mm_release()->clear_child_tid() from writing to a user-controlled  	 * kernel address. +	 * +	 * On uptodate architectures force_uaccess_begin is a noop.  On +	 * architectures that still have set_fs/get_fs in addition to handling +	 * oopses handles kernel threads that run as set_fs(KERNEL_DS) by +	 * default.  	 */  	force_uaccess_begin(); -	if (unlikely(in_atomic())) { -		pr_info("note: %s[%d] exited with preempt_count %d\n", -			current->comm, task_pid_nr(current), -			preempt_count()); -		preempt_count_set(PREEMPT_ENABLED); -	} - -	profile_task_exit(tsk);  	kcov_task_exit(tsk);  	coredump_task_exit(tsk); @@ -773,17 +758,6 @@ void __noreturn do_exit(long code)  	validate_creds_for_do_exit(tsk); -	/* -	 * We're taking recursive faults here in do_exit. Safest is to just -	 * leave this task alone and wait for reboot. -	 */ -	if (unlikely(tsk->flags & PF_EXITING)) { -		pr_alert("Fixing recursive fault but reboot is needed!\n"); -		futex_exit_recursive(tsk); -		set_current_state(TASK_UNINTERRUPTIBLE); -		schedule(); -	} -  	io_uring_files_cancel();  	exit_signals(tsk);  /* sets PF_EXITING */ @@ -882,16 +856,46 @@ void __noreturn do_exit(long code)  	lockdep_free_task(tsk);  	do_task_dead();  } -EXPORT_SYMBOL_GPL(do_exit); -void complete_and_exit(struct completion *comp, long code) +void __noreturn make_task_dead(int signr)  { -	if (comp) -		complete(comp); +	/* +	 * Take the task off the cpu after something catastrophic has +	 * happened. +	 * +	 * We can get here from a kernel oops, sometimes with preemption off. +	 * Start by checking for critical errors. +	 * Then fix up important state like USER_DS and preemption. +	 * Then do everything else. +	 */ +	struct task_struct *tsk = current; -	do_exit(code); +	if (unlikely(in_interrupt())) +		panic("Aiee, killing interrupt handler!"); +	if (unlikely(!tsk->pid)) +		panic("Attempted to kill the idle task!"); + +	if (unlikely(in_atomic())) { +		pr_info("note: %s[%d] exited with preempt_count %d\n", +			current->comm, task_pid_nr(current), +			preempt_count()); +		preempt_count_set(PREEMPT_ENABLED); +	} + +	/* +	 * We're taking recursive faults here in make_task_dead. Safest is to just +	 * leave this task alone and wait for reboot. +	 */ +	if (unlikely(tsk->flags & PF_EXITING)) { +		pr_alert("Fixing recursive fault but reboot is needed!\n"); +		futex_exit_recursive(tsk); +		tsk->exit_state = EXIT_DEAD; +		refcount_inc(&tsk->rcu_users); +		do_task_dead(); +	} + +	do_exit(signr);  } -EXPORT_SYMBOL(complete_and_exit);  SYSCALL_DEFINE1(exit, int, error_code)  { @@ -907,17 +911,19 @@ do_group_exit(int exit_code)  {  	struct signal_struct *sig = current->signal; -	BUG_ON(exit_code & 0x80); /* core dumps don't get here */ - -	if (signal_group_exit(sig)) +	if (sig->flags & SIGNAL_GROUP_EXIT)  		exit_code = sig->group_exit_code; +	else if (sig->group_exec_task) +		exit_code = 0;  	else if (!thread_group_empty(current)) {  		struct sighand_struct *const sighand = current->sighand;  		spin_lock_irq(&sighand->siglock); -		if (signal_group_exit(sig)) +		if (sig->flags & SIGNAL_GROUP_EXIT)  			/* Another thread got here before we took the lock.  */  			exit_code = sig->group_exit_code; +		else if (sig->group_exec_task) +			exit_code = 0;  		else {  			sig->group_exit_code = exit_code;  			sig->flags = SIGNAL_GROUP_EXIT; @@ -1012,7 +1018,8 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)  		return 0;  	if (unlikely(wo->wo_flags & WNOWAIT)) { -		status = p->exit_code; +		status = (p->signal->flags & SIGNAL_GROUP_EXIT) +			? p->signal->group_exit_code : p->exit_code;  		get_task_struct(p);  		read_unlock(&tasklist_lock);  		sched_annotate_sleep();  |