18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef _S390_TLBFLUSH_H 38c2ecf20Sopenharmony_ci#define _S390_TLBFLUSH_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/mm.h> 68c2ecf20Sopenharmony_ci#include <linux/sched.h> 78c2ecf20Sopenharmony_ci#include <asm/processor.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Flush all TLB entries on the local CPU. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_cistatic inline void __tlb_flush_local(void) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci asm volatile("ptlb" : : : "memory"); 158c2ecf20Sopenharmony_ci} 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Flush TLB entries for a specific ASCE on all CPUs 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_cistatic inline void __tlb_flush_idte(unsigned long asce) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci unsigned long opt; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci opt = IDTE_PTOA; 258c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) 268c2ecf20Sopenharmony_ci opt |= IDTE_GUEST_ASCE; 278c2ecf20Sopenharmony_ci /* Global TLB flush for the mm */ 288c2ecf20Sopenharmony_ci asm volatile( 298c2ecf20Sopenharmony_ci " .insn rrf,0xb98e0000,0,%0,%1,0" 308c2ecf20Sopenharmony_ci : : "a" (opt), "a" (asce) : "cc"); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Flush all TLB entries on all CPUs. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic inline void __tlb_flush_global(void) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci unsigned int dummy = 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci csp(&dummy, 0, 0); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * Flush TLB entries for a specific mm on all CPUs (in case gmap is used 458c2ecf20Sopenharmony_ci * this implicates multiple ASCEs!). 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic inline void __tlb_flush_mm(struct mm_struct *mm) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long gmap_asce; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * If the machine has IDTE we prefer to do a per mm flush 538c2ecf20Sopenharmony_ci * on all cpus instead of doing a local flush if the mm 548c2ecf20Sopenharmony_ci * only ran on the local cpu. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci preempt_disable(); 578c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 588c2ecf20Sopenharmony_ci /* Reset TLB flush mask */ 598c2ecf20Sopenharmony_ci cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask); 608c2ecf20Sopenharmony_ci barrier(); 618c2ecf20Sopenharmony_ci gmap_asce = READ_ONCE(mm->context.gmap_asce); 628c2ecf20Sopenharmony_ci if (MACHINE_HAS_IDTE && gmap_asce != -1UL) { 638c2ecf20Sopenharmony_ci if (gmap_asce) 648c2ecf20Sopenharmony_ci __tlb_flush_idte(gmap_asce); 658c2ecf20Sopenharmony_ci __tlb_flush_idte(mm->context.asce); 668c2ecf20Sopenharmony_ci } else { 678c2ecf20Sopenharmony_ci /* Global TLB flush */ 688c2ecf20Sopenharmony_ci __tlb_flush_global(); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 718c2ecf20Sopenharmony_ci preempt_enable(); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline void __tlb_flush_kernel(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if (MACHINE_HAS_IDTE) 778c2ecf20Sopenharmony_ci __tlb_flush_idte(init_mm.context.asce); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci __tlb_flush_global(); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline void __tlb_flush_mm_lazy(struct mm_struct * mm) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci spin_lock(&mm->context.lock); 858c2ecf20Sopenharmony_ci if (mm->context.flush_mm) { 868c2ecf20Sopenharmony_ci mm->context.flush_mm = 0; 878c2ecf20Sopenharmony_ci __tlb_flush_mm(mm); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci spin_unlock(&mm->context.lock); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * TLB flushing: 948c2ecf20Sopenharmony_ci * flush_tlb() - flushes the current mm struct TLBs 958c2ecf20Sopenharmony_ci * flush_tlb_all() - flushes all processes TLBs 968c2ecf20Sopenharmony_ci * flush_tlb_mm(mm) - flushes the specified mm context TLB's 978c2ecf20Sopenharmony_ci * flush_tlb_page(vma, vmaddr) - flushes one page 988c2ecf20Sopenharmony_ci * flush_tlb_range(vma, start, end) - flushes a range of pages 998c2ecf20Sopenharmony_ci * flush_tlb_kernel_range(start, end) - flushes a range of kernel pages 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * flush_tlb_mm goes together with ptep_set_wrprotect for the 1048c2ecf20Sopenharmony_ci * copy_page_range operation and flush_tlb_range is related to 1058c2ecf20Sopenharmony_ci * ptep_get_and_clear for change_protection. ptep_set_wrprotect and 1068c2ecf20Sopenharmony_ci * ptep_get_and_clear do not flush the TLBs directly if the mm has 1078c2ecf20Sopenharmony_ci * only one user. At the end of the update the flush_tlb_mm and 1088c2ecf20Sopenharmony_ci * flush_tlb_range functions need to do the flush. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci#define flush_tlb() do { } while (0) 1118c2ecf20Sopenharmony_ci#define flush_tlb_all() do { } while (0) 1128c2ecf20Sopenharmony_ci#define flush_tlb_page(vma, addr) do { } while (0) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void flush_tlb_mm(struct mm_struct *mm) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci __tlb_flush_mm_lazy(mm); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline void flush_tlb_range(struct vm_area_struct *vma, 1208c2ecf20Sopenharmony_ci unsigned long start, unsigned long end) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci __tlb_flush_mm_lazy(vma->vm_mm); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline void flush_tlb_kernel_range(unsigned long start, 1268c2ecf20Sopenharmony_ci unsigned long end) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci __tlb_flush_kernel(); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#endif /* _S390_TLBFLUSH_H */ 132