On Thu, Jul 19, 2018 at 09:45:47AM -0700, Andy Lutomirski wrote: > After some grepping, there are very few users. The > only nontrivial ones are the ones in kernel/ and mm/mmu_context.c that > are involved in the rather complicated dance of refcounting active_mm.
Something like so should work I suppose. It keeps the field, but ensure that for all tasks tsk->active_mm == tsk->mm. The implication is that switch_mm(.prev == NULL) should work (it does for x86, we don't care about @prev). diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d84eafcf5e29..7bfd850c6bf7 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2780,12 +2780,8 @@ static __always_inline struct rq * context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next, struct rq_flags *rf) { - struct mm_struct *mm, *oldmm; - prepare_task_switch(rq, prev, next); - mm = next->mm; - oldmm = prev->active_mm; /* * For paravirt, this is coupled with an exit in switch_to to * combine the page table reload and the switch backend into @@ -2800,16 +2796,32 @@ context_switch(struct rq *rq, struct task_struct *prev, * membarrier after storing to rq->curr, before returning to * user-space. */ - if (!mm) { - next->active_mm = oldmm; - mmgrab(oldmm); - enter_lazy_tlb(oldmm, next); - } else - switch_mm_irqs_off(oldmm, mm, next); - - if (!prev->mm) { - prev->active_mm = NULL; - rq->prev_mm = oldmm; + + /* + * kernel -> kernel lazy + transfer active + * user -> kernel lazy + mmgrab() active + * + * kernel -> user switch + mmdrop() active + * user -> user switch + */ + if (!next->mm) { // to kernel + enter_lazy_tlb(prev->active_mm, next); + +#ifdef ARCH_NO_ACTIVE_MM + next->active_mm = prev->active_mm; + if (prev->mm) // from user + mmgrab(prev->active_mm); +#endif + } else { // to user + switch_mm_irqs_off(prev->active_mm, next->mm, next); + +#ifdef ARCH_NO_ACTIVE_MM + if (!prev->mm) { // from kernel + /* will mmdrop() in finish_task_switch(). */ + rq->prev_mm = prev->active_mm; + prev->active_mm = NULL; + } +#endif } rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP); diff --git a/mm/mmu_context.c b/mm/mmu_context.c index 3e612ae748e9..770bb615d115 100644 --- a/mm/mmu_context.c +++ b/mm/mmu_context.c @@ -12,6 +12,10 @@ #include <asm/mmu_context.h> +#ifndef finish_arch_post_lock_switch +# define finish_arch_post_lock_switch() do { } while (0) +#endif + /* * use_mm * Makes the calling kernel thread take on the specified @@ -33,12 +37,14 @@ void use_mm(struct mm_struct *mm) tsk->mm = mm; switch_mm(active_mm, mm, tsk); task_unlock(tsk); -#ifdef finish_arch_post_lock_switch finish_arch_post_lock_switch(); -#endif +#ifdef ARCH_NO_ACTIVE_MM + WARN_ON_ONCE(active_mm != NULL); +#else if (active_mm != mm) mmdrop(active_mm); +#endif } EXPORT_SYMBOL_GPL(use_mm); @@ -57,8 +63,22 @@ void unuse_mm(struct mm_struct *mm) task_lock(tsk); sync_mm_rss(mm); tsk->mm = NULL; + + WARN_ON_ONCE(tsk->active_mm != mm); + +#ifdef ARCH_NO_ACTIVE_MM + switch_mm(tsk->active_mm, &init_mm, tsk); + mmdrop(tsk->active_mm); + tsk->active_mm = NULL; +#else /* active_mm is still 'mm' */ enter_lazy_tlb(mm, tsk); +#endif + task_unlock(tsk); + +#ifdef ARCH_NO_ACTIVE_MM + finish_arch_post_lock_switch(); +#endif } EXPORT_SYMBOL_GPL(unuse_mm);