18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SPARC64 Huge TLB page support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/fs.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 118c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 128c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 138c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/mman.h> 168c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 178c2ecf20Sopenharmony_ci#include <asm/tlb.h> 188c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 208c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Slightly simplified from the non-hugepage variant because by 238c2ecf20Sopenharmony_ci * definition we don't have to worry about any page coloring stuff 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp, 278c2ecf20Sopenharmony_ci unsigned long addr, 288c2ecf20Sopenharmony_ci unsigned long len, 298c2ecf20Sopenharmony_ci unsigned long pgoff, 308c2ecf20Sopenharmony_ci unsigned long flags) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct hstate *h = hstate_file(filp); 338c2ecf20Sopenharmony_ci unsigned long task_size = TASK_SIZE; 348c2ecf20Sopenharmony_ci struct vm_unmapped_area_info info; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (test_thread_flag(TIF_32BIT)) 378c2ecf20Sopenharmony_ci task_size = STACK_TOP32; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci info.flags = 0; 408c2ecf20Sopenharmony_ci info.length = len; 418c2ecf20Sopenharmony_ci info.low_limit = TASK_UNMAPPED_BASE; 428c2ecf20Sopenharmony_ci info.high_limit = min(task_size, VA_EXCLUDE_START); 438c2ecf20Sopenharmony_ci info.align_mask = PAGE_MASK & ~huge_page_mask(h); 448c2ecf20Sopenharmony_ci info.align_offset = 0; 458c2ecf20Sopenharmony_ci addr = vm_unmapped_area(&info); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if ((addr & ~PAGE_MASK) && task_size > VA_EXCLUDE_END) { 488c2ecf20Sopenharmony_ci VM_BUG_ON(addr != -ENOMEM); 498c2ecf20Sopenharmony_ci info.low_limit = VA_EXCLUDE_END; 508c2ecf20Sopenharmony_ci info.high_limit = task_size; 518c2ecf20Sopenharmony_ci addr = vm_unmapped_area(&info); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return addr; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic unsigned long 588c2ecf20Sopenharmony_cihugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, 598c2ecf20Sopenharmony_ci const unsigned long len, 608c2ecf20Sopenharmony_ci const unsigned long pgoff, 618c2ecf20Sopenharmony_ci const unsigned long flags) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct hstate *h = hstate_file(filp); 648c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 658c2ecf20Sopenharmony_ci unsigned long addr = addr0; 668c2ecf20Sopenharmony_ci struct vm_unmapped_area_info info; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* This should only ever run for 32-bit processes. */ 698c2ecf20Sopenharmony_ci BUG_ON(!test_thread_flag(TIF_32BIT)); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci info.flags = VM_UNMAPPED_AREA_TOPDOWN; 728c2ecf20Sopenharmony_ci info.length = len; 738c2ecf20Sopenharmony_ci info.low_limit = PAGE_SIZE; 748c2ecf20Sopenharmony_ci info.high_limit = mm->mmap_base; 758c2ecf20Sopenharmony_ci info.align_mask = PAGE_MASK & ~huge_page_mask(h); 768c2ecf20Sopenharmony_ci info.align_offset = 0; 778c2ecf20Sopenharmony_ci addr = vm_unmapped_area(&info); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * A failed mmap() very likely causes application failure, 818c2ecf20Sopenharmony_ci * so fall back to the bottom-up function here. This scenario 828c2ecf20Sopenharmony_ci * can happen with large stack limits and large mmap() 838c2ecf20Sopenharmony_ci * allocations. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci if (addr & ~PAGE_MASK) { 868c2ecf20Sopenharmony_ci VM_BUG_ON(addr != -ENOMEM); 878c2ecf20Sopenharmony_ci info.flags = 0; 888c2ecf20Sopenharmony_ci info.low_limit = TASK_UNMAPPED_BASE; 898c2ecf20Sopenharmony_ci info.high_limit = STACK_TOP32; 908c2ecf20Sopenharmony_ci addr = vm_unmapped_area(&info); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return addr; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciunsigned long 978c2ecf20Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr, 988c2ecf20Sopenharmony_ci unsigned long len, unsigned long pgoff, unsigned long flags) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct hstate *h = hstate_file(file); 1018c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 1028c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 1038c2ecf20Sopenharmony_ci unsigned long task_size = TASK_SIZE; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (test_thread_flag(TIF_32BIT)) 1068c2ecf20Sopenharmony_ci task_size = STACK_TOP32; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (len & ~huge_page_mask(h)) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci if (len > task_size) 1118c2ecf20Sopenharmony_ci return -ENOMEM; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (flags & MAP_FIXED) { 1148c2ecf20Sopenharmony_ci if (prepare_hugepage_range(file, addr, len)) 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci return addr; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (addr) { 1208c2ecf20Sopenharmony_ci addr = ALIGN(addr, huge_page_size(h)); 1218c2ecf20Sopenharmony_ci vma = find_vma(mm, addr); 1228c2ecf20Sopenharmony_ci if (task_size - len >= addr && 1238c2ecf20Sopenharmony_ci (!vma || addr + len <= vm_start_gap(vma))) 1248c2ecf20Sopenharmony_ci return addr; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci if (mm->get_unmapped_area == arch_get_unmapped_area) 1278c2ecf20Sopenharmony_ci return hugetlb_get_unmapped_area_bottomup(file, addr, len, 1288c2ecf20Sopenharmony_ci pgoff, flags); 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci return hugetlb_get_unmapped_area_topdown(file, addr, len, 1318c2ecf20Sopenharmony_ci pgoff, flags); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic pte_t sun4u_hugepage_shift_to_tte(pte_t entry, unsigned int shift) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return entry; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic pte_t sun4v_hugepage_shift_to_tte(pte_t entry, unsigned int shift) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci unsigned long hugepage_size = _PAGE_SZ4MB_4V; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci pte_val(entry) = pte_val(entry) & ~_PAGE_SZALL_4V; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci switch (shift) { 1468c2ecf20Sopenharmony_ci case HPAGE_16GB_SHIFT: 1478c2ecf20Sopenharmony_ci hugepage_size = _PAGE_SZ16GB_4V; 1488c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_PUD_HUGE; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case HPAGE_2GB_SHIFT: 1518c2ecf20Sopenharmony_ci hugepage_size = _PAGE_SZ2GB_4V; 1528c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_PMD_HUGE; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci case HPAGE_256MB_SHIFT: 1558c2ecf20Sopenharmony_ci hugepage_size = _PAGE_SZ256MB_4V; 1568c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_PMD_HUGE; 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci case HPAGE_SHIFT: 1598c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_PMD_HUGE; 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci case HPAGE_64K_SHIFT: 1628c2ecf20Sopenharmony_ci hugepage_size = _PAGE_SZ64K_4V; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci default: 1658c2ecf20Sopenharmony_ci WARN_ONCE(1, "unsupported hugepage shift=%u\n", shift); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci pte_val(entry) = pte_val(entry) | hugepage_size; 1698c2ecf20Sopenharmony_ci return entry; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic pte_t hugepage_shift_to_tte(pte_t entry, unsigned int shift) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (tlb_type == hypervisor) 1758c2ecf20Sopenharmony_ci return sun4v_hugepage_shift_to_tte(entry, shift); 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci return sun4u_hugepage_shift_to_tte(entry, shift); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cipte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma, 1818c2ecf20Sopenharmony_ci struct page *page, int writeable) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci unsigned int shift = huge_page_shift(hstate_vma(vma)); 1848c2ecf20Sopenharmony_ci pte_t pte; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci pte = hugepage_shift_to_tte(entry, shift); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC64 1898c2ecf20Sopenharmony_ci /* If this vma has ADI enabled on it, turn on TTE.mcd 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SPARC_ADI) 1928c2ecf20Sopenharmony_ci return pte_mkmcd(pte); 1938c2ecf20Sopenharmony_ci else 1948c2ecf20Sopenharmony_ci return pte_mknotmcd(pte); 1958c2ecf20Sopenharmony_ci#else 1968c2ecf20Sopenharmony_ci return pte; 1978c2ecf20Sopenharmony_ci#endif 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic unsigned int sun4v_huge_tte_to_shift(pte_t entry) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci unsigned long tte_szbits = pte_val(entry) & _PAGE_SZALL_4V; 2038c2ecf20Sopenharmony_ci unsigned int shift; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci switch (tte_szbits) { 2068c2ecf20Sopenharmony_ci case _PAGE_SZ16GB_4V: 2078c2ecf20Sopenharmony_ci shift = HPAGE_16GB_SHIFT; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case _PAGE_SZ2GB_4V: 2108c2ecf20Sopenharmony_ci shift = HPAGE_2GB_SHIFT; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case _PAGE_SZ256MB_4V: 2138c2ecf20Sopenharmony_ci shift = HPAGE_256MB_SHIFT; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case _PAGE_SZ4MB_4V: 2168c2ecf20Sopenharmony_ci shift = REAL_HPAGE_SHIFT; 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case _PAGE_SZ64K_4V: 2198c2ecf20Sopenharmony_ci shift = HPAGE_64K_SHIFT; 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci default: 2228c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci return shift; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic unsigned int sun4u_huge_tte_to_shift(pte_t entry) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci unsigned long tte_szbits = pte_val(entry) & _PAGE_SZALL_4U; 2318c2ecf20Sopenharmony_ci unsigned int shift; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci switch (tte_szbits) { 2348c2ecf20Sopenharmony_ci case _PAGE_SZ256MB_4U: 2358c2ecf20Sopenharmony_ci shift = HPAGE_256MB_SHIFT; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case _PAGE_SZ4MB_4U: 2388c2ecf20Sopenharmony_ci shift = REAL_HPAGE_SHIFT; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case _PAGE_SZ64K_4U: 2418c2ecf20Sopenharmony_ci shift = HPAGE_64K_SHIFT; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci default: 2448c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci return shift; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic unsigned int huge_tte_to_shift(pte_t entry) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci unsigned long shift; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (tlb_type == hypervisor) 2558c2ecf20Sopenharmony_ci shift = sun4v_huge_tte_to_shift(entry); 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci shift = sun4u_huge_tte_to_shift(entry); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (shift == PAGE_SHIFT) 2608c2ecf20Sopenharmony_ci WARN_ONCE(1, "tto_to_shift: invalid hugepage tte=0x%lx\n", 2618c2ecf20Sopenharmony_ci pte_val(entry)); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return shift; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic unsigned long huge_tte_to_size(pte_t pte) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci unsigned long size = 1UL << huge_tte_to_shift(pte); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (size == REAL_HPAGE_SIZE) 2718c2ecf20Sopenharmony_ci size = HPAGE_SIZE; 2728c2ecf20Sopenharmony_ci return size; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm, 2768c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci pgd_t *pgd; 2798c2ecf20Sopenharmony_ci p4d_t *p4d; 2808c2ecf20Sopenharmony_ci pud_t *pud; 2818c2ecf20Sopenharmony_ci pmd_t *pmd; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 2848c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 2858c2ecf20Sopenharmony_ci pud = pud_alloc(mm, p4d, addr); 2868c2ecf20Sopenharmony_ci if (!pud) 2878c2ecf20Sopenharmony_ci return NULL; 2888c2ecf20Sopenharmony_ci if (sz >= PUD_SIZE) 2898c2ecf20Sopenharmony_ci return (pte_t *)pud; 2908c2ecf20Sopenharmony_ci pmd = pmd_alloc(mm, pud, addr); 2918c2ecf20Sopenharmony_ci if (!pmd) 2928c2ecf20Sopenharmony_ci return NULL; 2938c2ecf20Sopenharmony_ci if (sz >= PMD_SIZE) 2948c2ecf20Sopenharmony_ci return (pte_t *)pmd; 2958c2ecf20Sopenharmony_ci return pte_alloc_map(mm, pmd, addr); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm, 2998c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci pgd_t *pgd; 3028c2ecf20Sopenharmony_ci p4d_t *p4d; 3038c2ecf20Sopenharmony_ci pud_t *pud; 3048c2ecf20Sopenharmony_ci pmd_t *pmd; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 3078c2ecf20Sopenharmony_ci if (pgd_none(*pgd)) 3088c2ecf20Sopenharmony_ci return NULL; 3098c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 3108c2ecf20Sopenharmony_ci if (p4d_none(*p4d)) 3118c2ecf20Sopenharmony_ci return NULL; 3128c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 3138c2ecf20Sopenharmony_ci if (pud_none(*pud)) 3148c2ecf20Sopenharmony_ci return NULL; 3158c2ecf20Sopenharmony_ci if (is_hugetlb_pud(*pud)) 3168c2ecf20Sopenharmony_ci return (pte_t *)pud; 3178c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 3188c2ecf20Sopenharmony_ci if (pmd_none(*pmd)) 3198c2ecf20Sopenharmony_ci return NULL; 3208c2ecf20Sopenharmony_ci if (is_hugetlb_pmd(*pmd)) 3218c2ecf20Sopenharmony_ci return (pte_t *)pmd; 3228c2ecf20Sopenharmony_ci return pte_offset_map(pmd, addr); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 3268c2ecf20Sopenharmony_ci pte_t *ptep, pte_t entry) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci unsigned int nptes, orig_shift, shift; 3298c2ecf20Sopenharmony_ci unsigned long i, size; 3308c2ecf20Sopenharmony_ci pte_t orig; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci size = huge_tte_to_size(entry); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 3358c2ecf20Sopenharmony_ci if (size >= PUD_SIZE) 3368c2ecf20Sopenharmony_ci shift = PUD_SHIFT; 3378c2ecf20Sopenharmony_ci else if (size >= PMD_SIZE) 3388c2ecf20Sopenharmony_ci shift = PMD_SHIFT; 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci nptes = size >> shift; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!pte_present(*ptep) && pte_present(entry)) 3458c2ecf20Sopenharmony_ci mm->context.hugetlb_pte_count += nptes; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci addr &= ~(size - 1); 3488c2ecf20Sopenharmony_ci orig = *ptep; 3498c2ecf20Sopenharmony_ci orig_shift = pte_none(orig) ? PAGE_SHIFT : huge_tte_to_shift(orig); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < nptes; i++) 3528c2ecf20Sopenharmony_ci ptep[i] = __pte(pte_val(entry) + (i << shift)); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci maybe_tlb_batch_add(mm, addr, ptep, orig, 0, orig_shift); 3558c2ecf20Sopenharmony_ci /* An HPAGE_SIZE'ed page is composed of two REAL_HPAGE_SIZE'ed pages */ 3568c2ecf20Sopenharmony_ci if (size == HPAGE_SIZE) 3578c2ecf20Sopenharmony_ci maybe_tlb_batch_add(mm, addr + REAL_HPAGE_SIZE, ptep, orig, 0, 3588c2ecf20Sopenharmony_ci orig_shift); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, 3628c2ecf20Sopenharmony_ci pte_t *ptep) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci unsigned int i, nptes, orig_shift, shift; 3658c2ecf20Sopenharmony_ci unsigned long size; 3668c2ecf20Sopenharmony_ci pte_t entry; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci entry = *ptep; 3698c2ecf20Sopenharmony_ci size = huge_tte_to_size(entry); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 3728c2ecf20Sopenharmony_ci if (size >= PUD_SIZE) 3738c2ecf20Sopenharmony_ci shift = PUD_SHIFT; 3748c2ecf20Sopenharmony_ci else if (size >= PMD_SIZE) 3758c2ecf20Sopenharmony_ci shift = PMD_SHIFT; 3768c2ecf20Sopenharmony_ci else 3778c2ecf20Sopenharmony_ci shift = PAGE_SHIFT; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci nptes = size >> shift; 3808c2ecf20Sopenharmony_ci orig_shift = pte_none(entry) ? PAGE_SHIFT : huge_tte_to_shift(entry); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (pte_present(entry)) 3838c2ecf20Sopenharmony_ci mm->context.hugetlb_pte_count -= nptes; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci addr &= ~(size - 1); 3868c2ecf20Sopenharmony_ci for (i = 0; i < nptes; i++) 3878c2ecf20Sopenharmony_ci ptep[i] = __pte(0UL); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci maybe_tlb_batch_add(mm, addr, ptep, entry, 0, orig_shift); 3908c2ecf20Sopenharmony_ci /* An HPAGE_SIZE'ed page is composed of two REAL_HPAGE_SIZE'ed pages */ 3918c2ecf20Sopenharmony_ci if (size == HPAGE_SIZE) 3928c2ecf20Sopenharmony_ci maybe_tlb_batch_add(mm, addr + REAL_HPAGE_SIZE, ptep, entry, 0, 3938c2ecf20Sopenharmony_ci orig_shift); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return entry; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci return !pmd_none(pmd) && 4018c2ecf20Sopenharmony_ci (pmd_val(pmd) & (_PAGE_VALID|_PAGE_PMD_HUGE)) != _PAGE_VALID; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciint pud_huge(pud_t pud) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci return !pud_none(pud) && 4078c2ecf20Sopenharmony_ci (pud_val(pud) & (_PAGE_VALID|_PAGE_PUD_HUGE)) != _PAGE_VALID; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void hugetlb_free_pte_range(struct mmu_gather *tlb, pmd_t *pmd, 4118c2ecf20Sopenharmony_ci unsigned long addr) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci pgtable_t token = pmd_pgtable(*pmd); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pmd_clear(pmd); 4168c2ecf20Sopenharmony_ci pte_free_tlb(tlb, token, addr); 4178c2ecf20Sopenharmony_ci mm_dec_nr_ptes(tlb->mm); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud, 4218c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, 4228c2ecf20Sopenharmony_ci unsigned long floor, unsigned long ceiling) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci pmd_t *pmd; 4258c2ecf20Sopenharmony_ci unsigned long next; 4268c2ecf20Sopenharmony_ci unsigned long start; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci start = addr; 4298c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 4308c2ecf20Sopenharmony_ci do { 4318c2ecf20Sopenharmony_ci next = pmd_addr_end(addr, end); 4328c2ecf20Sopenharmony_ci if (pmd_none(*pmd)) 4338c2ecf20Sopenharmony_ci continue; 4348c2ecf20Sopenharmony_ci if (is_hugetlb_pmd(*pmd)) 4358c2ecf20Sopenharmony_ci pmd_clear(pmd); 4368c2ecf20Sopenharmony_ci else 4378c2ecf20Sopenharmony_ci hugetlb_free_pte_range(tlb, pmd, addr); 4388c2ecf20Sopenharmony_ci } while (pmd++, addr = next, addr != end); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci start &= PUD_MASK; 4418c2ecf20Sopenharmony_ci if (start < floor) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci if (ceiling) { 4448c2ecf20Sopenharmony_ci ceiling &= PUD_MASK; 4458c2ecf20Sopenharmony_ci if (!ceiling) 4468c2ecf20Sopenharmony_ci return; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci if (end - 1 > ceiling - 1) 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, start); 4528c2ecf20Sopenharmony_ci pud_clear(pud); 4538c2ecf20Sopenharmony_ci pmd_free_tlb(tlb, pmd, start); 4548c2ecf20Sopenharmony_ci mm_dec_nr_pmds(tlb->mm); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void hugetlb_free_pud_range(struct mmu_gather *tlb, p4d_t *p4d, 4588c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, 4598c2ecf20Sopenharmony_ci unsigned long floor, unsigned long ceiling) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci pud_t *pud; 4628c2ecf20Sopenharmony_ci unsigned long next; 4638c2ecf20Sopenharmony_ci unsigned long start; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci start = addr; 4668c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 4678c2ecf20Sopenharmony_ci do { 4688c2ecf20Sopenharmony_ci next = pud_addr_end(addr, end); 4698c2ecf20Sopenharmony_ci if (pud_none_or_clear_bad(pud)) 4708c2ecf20Sopenharmony_ci continue; 4718c2ecf20Sopenharmony_ci if (is_hugetlb_pud(*pud)) 4728c2ecf20Sopenharmony_ci pud_clear(pud); 4738c2ecf20Sopenharmony_ci else 4748c2ecf20Sopenharmony_ci hugetlb_free_pmd_range(tlb, pud, addr, next, floor, 4758c2ecf20Sopenharmony_ci ceiling); 4768c2ecf20Sopenharmony_ci } while (pud++, addr = next, addr != end); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci start &= PGDIR_MASK; 4798c2ecf20Sopenharmony_ci if (start < floor) 4808c2ecf20Sopenharmony_ci return; 4818c2ecf20Sopenharmony_ci if (ceiling) { 4828c2ecf20Sopenharmony_ci ceiling &= PGDIR_MASK; 4838c2ecf20Sopenharmony_ci if (!ceiling) 4848c2ecf20Sopenharmony_ci return; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci if (end - 1 > ceiling - 1) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci pud = pud_offset(p4d, start); 4908c2ecf20Sopenharmony_ci p4d_clear(p4d); 4918c2ecf20Sopenharmony_ci pud_free_tlb(tlb, pud, start); 4928c2ecf20Sopenharmony_ci mm_dec_nr_puds(tlb->mm); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_civoid hugetlb_free_pgd_range(struct mmu_gather *tlb, 4968c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, 4978c2ecf20Sopenharmony_ci unsigned long floor, unsigned long ceiling) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci pgd_t *pgd; 5008c2ecf20Sopenharmony_ci p4d_t *p4d; 5018c2ecf20Sopenharmony_ci unsigned long next; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci addr &= PMD_MASK; 5048c2ecf20Sopenharmony_ci if (addr < floor) { 5058c2ecf20Sopenharmony_ci addr += PMD_SIZE; 5068c2ecf20Sopenharmony_ci if (!addr) 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (ceiling) { 5108c2ecf20Sopenharmony_ci ceiling &= PMD_MASK; 5118c2ecf20Sopenharmony_ci if (!ceiling) 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci if (end - 1 > ceiling - 1) 5158c2ecf20Sopenharmony_ci end -= PMD_SIZE; 5168c2ecf20Sopenharmony_ci if (addr > end - 1) 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci pgd = pgd_offset(tlb->mm, addr); 5208c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 5218c2ecf20Sopenharmony_ci do { 5228c2ecf20Sopenharmony_ci next = p4d_addr_end(addr, end); 5238c2ecf20Sopenharmony_ci if (p4d_none_or_clear_bad(p4d)) 5248c2ecf20Sopenharmony_ci continue; 5258c2ecf20Sopenharmony_ci hugetlb_free_pud_range(tlb, p4d, addr, next, floor, ceiling); 5268c2ecf20Sopenharmony_ci } while (p4d++, addr = next, addr != end); 5278c2ecf20Sopenharmony_ci} 528