18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* arch/sparc64/mm/tlb.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 David S. Miller <davem@redhat.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/percpu.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/swap.h> 118c2ecf20Sopenharmony_ci#include <linux/preempt.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 148c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 158c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 168c2ecf20Sopenharmony_ci#include <asm/tlb.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Heavily inspired by the ppc64 code. */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct tlb_batch, tlb_batch); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_civoid flush_tlb_pending(void) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct tlb_batch *tb = &get_cpu_var(tlb_batch); 258c2ecf20Sopenharmony_ci struct mm_struct *mm = tb->mm; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (!tb->tlb_nr) 288c2ecf20Sopenharmony_ci goto out; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci flush_tsb_user(tb); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (CTX_VALID(mm->context)) { 338c2ecf20Sopenharmony_ci if (tb->tlb_nr == 1) { 348c2ecf20Sopenharmony_ci global_flush_tlb_page(mm, tb->vaddrs[0]); 358c2ecf20Sopenharmony_ci } else { 368c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 378c2ecf20Sopenharmony_ci smp_flush_tlb_pending(tb->mm, tb->tlb_nr, 388c2ecf20Sopenharmony_ci &tb->vaddrs[0]); 398c2ecf20Sopenharmony_ci#else 408c2ecf20Sopenharmony_ci __flush_tlb_pending(CTX_HWBITS(tb->mm->context), 418c2ecf20Sopenharmony_ci tb->tlb_nr, &tb->vaddrs[0]); 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci tb->tlb_nr = 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciout: 498c2ecf20Sopenharmony_ci put_cpu_var(tlb_batch); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_civoid arch_enter_lazy_mmu_mode(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct tlb_batch *tb = this_cpu_ptr(&tlb_batch); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci tb->active = 1; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid arch_leave_lazy_mmu_mode(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct tlb_batch *tb = this_cpu_ptr(&tlb_batch); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (tb->tlb_nr) 648c2ecf20Sopenharmony_ci flush_tlb_pending(); 658c2ecf20Sopenharmony_ci tb->active = 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr, 698c2ecf20Sopenharmony_ci bool exec, unsigned int hugepage_shift) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct tlb_batch *tb = &get_cpu_var(tlb_batch); 728c2ecf20Sopenharmony_ci unsigned long nr; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci vaddr &= PAGE_MASK; 758c2ecf20Sopenharmony_ci if (exec) 768c2ecf20Sopenharmony_ci vaddr |= 0x1UL; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci nr = tb->tlb_nr; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (unlikely(nr != 0 && mm != tb->mm)) { 818c2ecf20Sopenharmony_ci flush_tlb_pending(); 828c2ecf20Sopenharmony_ci nr = 0; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!tb->active) { 868c2ecf20Sopenharmony_ci flush_tsb_user_page(mm, vaddr, hugepage_shift); 878c2ecf20Sopenharmony_ci global_flush_tlb_page(mm, vaddr); 888c2ecf20Sopenharmony_ci goto out; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (nr == 0) { 928c2ecf20Sopenharmony_ci tb->mm = mm; 938c2ecf20Sopenharmony_ci tb->hugepage_shift = hugepage_shift; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (tb->hugepage_shift != hugepage_shift) { 978c2ecf20Sopenharmony_ci flush_tlb_pending(); 988c2ecf20Sopenharmony_ci tb->hugepage_shift = hugepage_shift; 998c2ecf20Sopenharmony_ci nr = 0; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci tb->vaddrs[nr] = vaddr; 1038c2ecf20Sopenharmony_ci tb->tlb_nr = ++nr; 1048c2ecf20Sopenharmony_ci if (nr >= TLB_BATCH_NR) 1058c2ecf20Sopenharmony_ci flush_tlb_pending(); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciout: 1088c2ecf20Sopenharmony_ci put_cpu_var(tlb_batch); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_civoid tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, 1128c2ecf20Sopenharmony_ci pte_t *ptep, pte_t orig, int fullmm, 1138c2ecf20Sopenharmony_ci unsigned int hugepage_shift) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (tlb_type != hypervisor && 1168c2ecf20Sopenharmony_ci pte_dirty(orig)) { 1178c2ecf20Sopenharmony_ci unsigned long paddr, pfn = pte_pfn(orig); 1188c2ecf20Sopenharmony_ci struct address_space *mapping; 1198c2ecf20Sopenharmony_ci struct page *page; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!pfn_valid(pfn)) 1228c2ecf20Sopenharmony_ci goto no_cache_flush; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci page = pfn_to_page(pfn); 1258c2ecf20Sopenharmony_ci if (PageReserved(page)) 1268c2ecf20Sopenharmony_ci goto no_cache_flush; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* A real file page? */ 1298c2ecf20Sopenharmony_ci mapping = page_mapping_file(page); 1308c2ecf20Sopenharmony_ci if (!mapping) 1318c2ecf20Sopenharmony_ci goto no_cache_flush; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci paddr = (unsigned long) page_address(page); 1348c2ecf20Sopenharmony_ci if ((paddr ^ vaddr) & (1 << 13)) 1358c2ecf20Sopenharmony_ci flush_dcache_page_all(mm, page); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cino_cache_flush: 1398c2ecf20Sopenharmony_ci if (!fullmm) 1408c2ecf20Sopenharmony_ci tlb_batch_add_one(mm, vaddr, pte_exec(orig), hugepage_shift); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 1448c2ecf20Sopenharmony_cistatic void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, 1458c2ecf20Sopenharmony_ci pmd_t pmd) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned long end; 1488c2ecf20Sopenharmony_ci pte_t *pte; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci pte = pte_offset_map(&pmd, vaddr); 1518c2ecf20Sopenharmony_ci end = vaddr + HPAGE_SIZE; 1528c2ecf20Sopenharmony_ci while (vaddr < end) { 1538c2ecf20Sopenharmony_ci if (pte_val(*pte) & _PAGE_VALID) { 1548c2ecf20Sopenharmony_ci bool exec = pte_exec(*pte); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci tlb_batch_add_one(mm, vaddr, exec, PAGE_SHIFT); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci pte++; 1598c2ecf20Sopenharmony_ci vaddr += PAGE_SIZE; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci pte_unmap(pte); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void __set_pmd_acct(struct mm_struct *mm, unsigned long addr, 1668c2ecf20Sopenharmony_ci pmd_t orig, pmd_t pmd) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci if (mm == &init_mm) 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if ((pmd_val(pmd) ^ pmd_val(orig)) & _PAGE_PMD_HUGE) { 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Note that this routine only sets pmds for THP pages. 1748c2ecf20Sopenharmony_ci * Hugetlb pages are handled elsewhere. We need to check 1758c2ecf20Sopenharmony_ci * for huge zero page. Huge zero pages are like hugetlb 1768c2ecf20Sopenharmony_ci * pages in that there is no RSS, but there is the need 1778c2ecf20Sopenharmony_ci * for TSB entries. So, huge zero page counts go into 1788c2ecf20Sopenharmony_ci * hugetlb_pte_count. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (pmd_val(pmd) & _PAGE_PMD_HUGE) { 1818c2ecf20Sopenharmony_ci if (is_huge_zero_page(pmd_page(pmd))) 1828c2ecf20Sopenharmony_ci mm->context.hugetlb_pte_count++; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci mm->context.thp_pte_count++; 1858c2ecf20Sopenharmony_ci } else { 1868c2ecf20Sopenharmony_ci if (is_huge_zero_page(pmd_page(orig))) 1878c2ecf20Sopenharmony_ci mm->context.hugetlb_pte_count--; 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci mm->context.thp_pte_count--; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Do not try to allocate the TSB hash table if we 1938c2ecf20Sopenharmony_ci * don't have one already. We have various locks held 1948c2ecf20Sopenharmony_ci * and thus we'll end up doing a GFP_KERNEL allocation 1958c2ecf20Sopenharmony_ci * in an atomic context. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Instead, we let the first TLB miss on a hugepage 1988c2ecf20Sopenharmony_ci * take care of this. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!pmd_none(orig)) { 2038c2ecf20Sopenharmony_ci addr &= HPAGE_MASK; 2048c2ecf20Sopenharmony_ci if (pmd_trans_huge(orig)) { 2058c2ecf20Sopenharmony_ci pte_t orig_pte = __pte(pmd_val(orig)); 2068c2ecf20Sopenharmony_ci bool exec = pte_exec(orig_pte); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci tlb_batch_add_one(mm, addr, exec, REAL_HPAGE_SHIFT); 2098c2ecf20Sopenharmony_ci tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec, 2108c2ecf20Sopenharmony_ci REAL_HPAGE_SHIFT); 2118c2ecf20Sopenharmony_ci } else { 2128c2ecf20Sopenharmony_ci tlb_batch_pmd_scan(mm, addr, orig); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_civoid set_pmd_at(struct mm_struct *mm, unsigned long addr, 2188c2ecf20Sopenharmony_ci pmd_t *pmdp, pmd_t pmd) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci pmd_t orig = *pmdp; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci *pmdp = pmd; 2238c2ecf20Sopenharmony_ci __set_pmd_acct(mm, addr, orig, pmd); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic inline pmd_t pmdp_establish(struct vm_area_struct *vma, 2278c2ecf20Sopenharmony_ci unsigned long address, pmd_t *pmdp, pmd_t pmd) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci pmd_t old; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci do { 2328c2ecf20Sopenharmony_ci old = *pmdp; 2338c2ecf20Sopenharmony_ci } while (cmpxchg64(&pmdp->pmd, old.pmd, pmd.pmd) != old.pmd); 2348c2ecf20Sopenharmony_ci __set_pmd_acct(vma->vm_mm, address, old, pmd); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return old; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * This routine is only called when splitting a THP 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cipmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, 2438c2ecf20Sopenharmony_ci pmd_t *pmdp) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci pmd_t old, entry; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci entry = __pmd(pmd_val(*pmdp) & ~_PAGE_VALID); 2488c2ecf20Sopenharmony_ci old = pmdp_establish(vma, address, pmdp, entry); 2498c2ecf20Sopenharmony_ci flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * set_pmd_at() will not be called in a way to decrement 2538c2ecf20Sopenharmony_ci * thp_pte_count when splitting a THP, so do it now. 2548c2ecf20Sopenharmony_ci * Sanity check pmd before doing the actual decrement. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci if ((pmd_val(entry) & _PAGE_PMD_HUGE) && 2578c2ecf20Sopenharmony_ci !is_huge_zero_page(pmd_page(entry))) 2588c2ecf20Sopenharmony_ci (vma->vm_mm)->context.thp_pte_count--; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return old; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_civoid pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, 2648c2ecf20Sopenharmony_ci pgtable_t pgtable) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct list_head *lh = (struct list_head *) pgtable; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci assert_spin_locked(&mm->page_table_lock); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* FIFO */ 2718c2ecf20Sopenharmony_ci if (!pmd_huge_pte(mm, pmdp)) 2728c2ecf20Sopenharmony_ci INIT_LIST_HEAD(lh); 2738c2ecf20Sopenharmony_ci else 2748c2ecf20Sopenharmony_ci list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp)); 2758c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = pgtable; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cipgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct list_head *lh; 2818c2ecf20Sopenharmony_ci pgtable_t pgtable; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci assert_spin_locked(&mm->page_table_lock); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* FIFO */ 2868c2ecf20Sopenharmony_ci pgtable = pmd_huge_pte(mm, pmdp); 2878c2ecf20Sopenharmony_ci lh = (struct list_head *) pgtable; 2888c2ecf20Sopenharmony_ci if (list_empty(lh)) 2898c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = NULL; 2908c2ecf20Sopenharmony_ci else { 2918c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next; 2928c2ecf20Sopenharmony_ci list_del(lh); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci pte_val(pgtable[0]) = 0; 2958c2ecf20Sopenharmony_ci pte_val(pgtable[1]) = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return pgtable; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 300