diff options
Diffstat (limited to 'kernel/exit.c')
| -rw-r--r-- | kernel/exit.c | 84 | 
1 files changed, 45 insertions, 39 deletions
| diff --git a/kernel/exit.c b/kernel/exit.c index 81fcee45d630..7430852a8571 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -277,7 +277,6 @@ repeat:  	}  	write_unlock_irq(&tasklist_lock); -	seccomp_filter_release(p);  	proc_flush_pid(thread_pid);  	put_pid(thread_pid);  	release_thread(p); @@ -439,14 +438,46 @@ static void coredump_task_exit(struct task_struct *tsk)  }  #ifdef CONFIG_MEMCG +/* drops tasklist_lock if succeeds */ +static bool __try_to_set_owner(struct task_struct *tsk, struct mm_struct *mm) +{ +	bool ret = false; + +	task_lock(tsk); +	if (likely(tsk->mm == mm)) { +		/* tsk can't pass exit_mm/exec_mmap and exit */ +		read_unlock(&tasklist_lock); +		WRITE_ONCE(mm->owner, tsk); +		lru_gen_migrate_mm(mm); +		ret = true; +	} +	task_unlock(tsk); +	return ret; +} + +static bool try_to_set_owner(struct task_struct *g, struct mm_struct *mm) +{ +	struct task_struct *t; + +	for_each_thread(g, t) { +		struct mm_struct *t_mm = READ_ONCE(t->mm); +		if (t_mm == mm) { +			if (__try_to_set_owner(t, mm)) +				return true; +		} else if (t_mm) +			break; +	} + +	return false; +} +  /*   * A task is exiting.   If it owned this mm, find a new owner for the mm.   */  void mm_update_next_owner(struct mm_struct *mm)  { -	struct task_struct *c, *g, *p = current; +	struct task_struct *g, *p = current; -retry:  	/*  	 * If the exiting or execing task is not the owner, it's  	 * someone else's problem. @@ -467,19 +498,17 @@ retry:  	/*  	 * Search in the children  	 */ -	list_for_each_entry(c, &p->children, sibling) { -		if (c->mm == mm) -			goto assign_new_owner; +	list_for_each_entry(g, &p->children, sibling) { +		if (try_to_set_owner(g, mm)) +			goto ret;  	} -  	/*  	 * Search in the siblings  	 */ -	list_for_each_entry(c, &p->real_parent->children, sibling) { -		if (c->mm == mm) -			goto assign_new_owner; +	list_for_each_entry(g, &p->real_parent->children, sibling) { +		if (try_to_set_owner(g, mm)) +			goto ret;  	} -  	/*  	 * Search through everything else, we should not get here often.  	 */ @@ -488,12 +517,8 @@ retry:  			break;  		if (g->flags & PF_KTHREAD)  			continue; -		for_each_thread(g, c) { -			if (c->mm == mm) -				goto assign_new_owner; -			if (c->mm) -				break; -		} +		if (try_to_set_owner(g, mm)) +			goto ret;  	}  	read_unlock(&tasklist_lock);  	/* @@ -502,30 +527,9 @@ retry:  	 * ptrace or page migration (get_task_mm()).  Mark owner as NULL.  	 */  	WRITE_ONCE(mm->owner, NULL); + ret:  	return; -assign_new_owner: -	BUG_ON(c == p); -	get_task_struct(c); -	/* -	 * The task_lock protects c->mm from changing. -	 * We always want mm->owner->mm == mm -	 */ -	task_lock(c); -	/* -	 * Delay read_unlock() till we have the task_lock() -	 * to ensure that c does not slip away underneath us -	 */ -	read_unlock(&tasklist_lock); -	if (c->mm != mm) { -		task_unlock(c); -		put_task_struct(c); -		goto retry; -	} -	WRITE_ONCE(mm->owner, c); -	lru_gen_migrate_mm(mm); -	task_unlock(c); -	put_task_struct(c);  }  #endif /* CONFIG_MEMCG */ @@ -834,6 +838,8 @@ void __noreturn do_exit(long code)  	io_uring_files_cancel();  	exit_signals(tsk);  /* sets PF_EXITING */ +	seccomp_filter_release(tsk); +  	acct_update_integrals(tsk);  	group_dead = atomic_dec_and_test(&tsk->signal->live);  	if (group_dead) { |