18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Common implementation of switch_mm_irqs_off
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2017
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/mm.h>
98c2ecf20Sopenharmony_ci#include <linux/cpu.h>
108c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
138c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC32)
168c2ecf20Sopenharmony_cistatic inline void switch_mm_pgdir(struct task_struct *tsk,
178c2ecf20Sopenharmony_ci				   struct mm_struct *mm)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	/* 32-bit keeps track of the current PGDIR in the thread struct */
208c2ecf20Sopenharmony_ci	tsk->thread.pgdir = mm->pgd;
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci#elif defined(CONFIG_PPC_BOOK3E_64)
238c2ecf20Sopenharmony_cistatic inline void switch_mm_pgdir(struct task_struct *tsk,
248c2ecf20Sopenharmony_ci				   struct mm_struct *mm)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	/* 64-bit Book3E keeps track of current PGD in the PACA */
278c2ecf20Sopenharmony_ci	get_paca()->pgd = mm->pgd;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci#else
308c2ecf20Sopenharmony_cistatic inline void switch_mm_pgdir(struct task_struct *tsk,
318c2ecf20Sopenharmony_ci				   struct mm_struct *mm) { }
328c2ecf20Sopenharmony_ci#endif
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_civoid switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
358c2ecf20Sopenharmony_ci			struct task_struct *tsk)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	bool new_on_cpu = false;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* Mark this context has been used on the new CPU */
408c2ecf20Sopenharmony_ci	if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next))) {
418c2ecf20Sopenharmony_ci		cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
428c2ecf20Sopenharmony_ci		inc_mm_active_cpus(next);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		/*
458c2ecf20Sopenharmony_ci		 * This full barrier orders the store to the cpumask above vs
468c2ecf20Sopenharmony_ci		 * a subsequent operation which allows this CPU to begin loading
478c2ecf20Sopenharmony_ci		 * translations for next.
488c2ecf20Sopenharmony_ci		 *
498c2ecf20Sopenharmony_ci		 * When using the radix MMU that operation is the load of the
508c2ecf20Sopenharmony_ci		 * MMU context id, which is then moved to SPRN_PID.
518c2ecf20Sopenharmony_ci		 *
528c2ecf20Sopenharmony_ci		 * For the hash MMU it is either the first load from slb_cache
538c2ecf20Sopenharmony_ci		 * in switch_slb(), and/or the store of paca->mm_ctx_id in
548c2ecf20Sopenharmony_ci		 * copy_mm_to_paca().
558c2ecf20Sopenharmony_ci		 *
568c2ecf20Sopenharmony_ci		 * On the other side, the barrier is in mm/tlb-radix.c for
578c2ecf20Sopenharmony_ci		 * radix which orders earlier stores to clear the PTEs vs
588c2ecf20Sopenharmony_ci		 * the load of mm_cpumask. And pte_xchg which does the same
598c2ecf20Sopenharmony_ci		 * thing for hash.
608c2ecf20Sopenharmony_ci		 *
618c2ecf20Sopenharmony_ci		 * This full barrier is needed by membarrier when switching
628c2ecf20Sopenharmony_ci		 * between processes after store to rq->curr, before user-space
638c2ecf20Sopenharmony_ci		 * memory accesses.
648c2ecf20Sopenharmony_ci		 */
658c2ecf20Sopenharmony_ci		smp_mb();
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		new_on_cpu = true;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Some subarchs need to track the PGD elsewhere */
718c2ecf20Sopenharmony_ci	switch_mm_pgdir(tsk, next);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Nothing else to do if we aren't actually switching */
748c2ecf20Sopenharmony_ci	if (prev == next)
758c2ecf20Sopenharmony_ci		return;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * We must stop all altivec streams before changing the HW
798c2ecf20Sopenharmony_ci	 * context
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ALTIVEC))
828c2ecf20Sopenharmony_ci		asm volatile (PPC_DSSALL);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (new_on_cpu)
858c2ecf20Sopenharmony_ci		radix_kvm_prefetch_workaround(next);
868c2ecf20Sopenharmony_ci	else
878c2ecf20Sopenharmony_ci		membarrier_arch_switch_mm(prev, next, tsk);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/*
908c2ecf20Sopenharmony_ci	 * The actual HW switching method differs between the various
918c2ecf20Sopenharmony_ci	 * sub architectures. Out of line for now
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	switch_mmu_context(prev, next, tsk);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_BOOK3S_64
978c2ecf20Sopenharmony_civoid arch_exit_mmap(struct mm_struct *mm)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	void *frag = pte_frag_get(&mm->context);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (frag)
1028c2ecf20Sopenharmony_ci		pte_frag_destroy(frag);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci#endif
105