162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IA-32 Huge TLB Page Support for Kernel. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/sched/mm.h> 1262306a36Sopenharmony_ci#include <linux/hugetlb.h> 1362306a36Sopenharmony_ci#include <linux/pagemap.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/sysctl.h> 1662306a36Sopenharmony_ci#include <linux/compat.h> 1762306a36Sopenharmony_ci#include <asm/mman.h> 1862306a36Sopenharmony_ci#include <asm/tlb.h> 1962306a36Sopenharmony_ci#include <asm/tlbflush.h> 2062306a36Sopenharmony_ci#include <asm/elf.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * pmd_huge() returns 1 if @pmd is hugetlb related entry, that is normal 2462306a36Sopenharmony_ci * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry. 2562306a36Sopenharmony_ci * Otherwise, returns 0. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ciint pmd_huge(pmd_t pmd) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return !pmd_none(pmd) && 3062306a36Sopenharmony_ci (pmd_val(pmd) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * pud_huge() returns 1 if @pud is hugetlb related entry, that is normal 3562306a36Sopenharmony_ci * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry. 3662306a36Sopenharmony_ci * Otherwise, returns 0. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ciint pud_huge(pud_t pud) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 2 4162306a36Sopenharmony_ci return !pud_none(pud) && 4262306a36Sopenharmony_ci (pud_val(pud) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT; 4362306a36Sopenharmony_ci#else 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 4962306a36Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file, 5062306a36Sopenharmony_ci unsigned long addr, unsigned long len, 5162306a36Sopenharmony_ci unsigned long pgoff, unsigned long flags) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct hstate *h = hstate_file(file); 5462306a36Sopenharmony_ci struct vm_unmapped_area_info info; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci info.flags = 0; 5762306a36Sopenharmony_ci info.length = len; 5862306a36Sopenharmony_ci info.low_limit = get_mmap_base(1); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * If hint address is above DEFAULT_MAP_WINDOW, look for unmapped area 6262306a36Sopenharmony_ci * in the full address space. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci info.high_limit = in_32bit_syscall() ? 6562306a36Sopenharmony_ci task_size_32bit() : task_size_64bit(addr > DEFAULT_MAP_WINDOW); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci info.align_mask = PAGE_MASK & ~huge_page_mask(h); 6862306a36Sopenharmony_ci info.align_offset = 0; 6962306a36Sopenharmony_ci return vm_unmapped_area(&info); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_topdown(struct file *file, 7362306a36Sopenharmony_ci unsigned long addr, unsigned long len, 7462306a36Sopenharmony_ci unsigned long pgoff, unsigned long flags) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct hstate *h = hstate_file(file); 7762306a36Sopenharmony_ci struct vm_unmapped_area_info info; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci info.flags = VM_UNMAPPED_AREA_TOPDOWN; 8062306a36Sopenharmony_ci info.length = len; 8162306a36Sopenharmony_ci info.low_limit = PAGE_SIZE; 8262306a36Sopenharmony_ci info.high_limit = get_mmap_base(0); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * If hint address is above DEFAULT_MAP_WINDOW, look for unmapped area 8662306a36Sopenharmony_ci * in the full address space. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci if (addr > DEFAULT_MAP_WINDOW && !in_32bit_syscall()) 8962306a36Sopenharmony_ci info.high_limit += TASK_SIZE_MAX - DEFAULT_MAP_WINDOW; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci info.align_mask = PAGE_MASK & ~huge_page_mask(h); 9262306a36Sopenharmony_ci info.align_offset = 0; 9362306a36Sopenharmony_ci addr = vm_unmapped_area(&info); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * A failed mmap() very likely causes application failure, 9762306a36Sopenharmony_ci * so fall back to the bottom-up function here. This scenario 9862306a36Sopenharmony_ci * can happen with large stack limits and large mmap() 9962306a36Sopenharmony_ci * allocations. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci if (addr & ~PAGE_MASK) { 10262306a36Sopenharmony_ci VM_BUG_ON(addr != -ENOMEM); 10362306a36Sopenharmony_ci info.flags = 0; 10462306a36Sopenharmony_ci info.low_limit = TASK_UNMAPPED_BASE; 10562306a36Sopenharmony_ci info.high_limit = TASK_SIZE_LOW; 10662306a36Sopenharmony_ci addr = vm_unmapped_area(&info); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return addr; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciunsigned long 11362306a36Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr, 11462306a36Sopenharmony_ci unsigned long len, unsigned long pgoff, unsigned long flags) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct hstate *h = hstate_file(file); 11762306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 11862306a36Sopenharmony_ci struct vm_area_struct *vma; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (len & ~huge_page_mask(h)) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (len > TASK_SIZE) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* No address checking. See comment at mmap_address_hint_valid() */ 12762306a36Sopenharmony_ci if (flags & MAP_FIXED) { 12862306a36Sopenharmony_ci if (prepare_hugepage_range(file, addr, len)) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci return addr; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (addr) { 13462306a36Sopenharmony_ci addr &= huge_page_mask(h); 13562306a36Sopenharmony_ci if (!mmap_address_hint_valid(addr, len)) 13662306a36Sopenharmony_ci goto get_unmapped_area; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci vma = find_vma(mm, addr); 13962306a36Sopenharmony_ci if (!vma || addr + len <= vm_start_gap(vma)) 14062306a36Sopenharmony_ci return addr; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciget_unmapped_area: 14462306a36Sopenharmony_ci if (mm->get_unmapped_area == arch_get_unmapped_area) 14562306a36Sopenharmony_ci return hugetlb_get_unmapped_area_bottomup(file, addr, len, 14662306a36Sopenharmony_ci pgoff, flags); 14762306a36Sopenharmony_ci else 14862306a36Sopenharmony_ci return hugetlb_get_unmapped_area_topdown(file, addr, len, 14962306a36Sopenharmony_ci pgoff, flags); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci#endif /* CONFIG_HUGETLB_PAGE */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#ifdef CONFIG_X86_64 15462306a36Sopenharmony_cibool __init arch_hugetlb_valid_size(unsigned long size) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci if (size == PMD_SIZE) 15762306a36Sopenharmony_ci return true; 15862306a36Sopenharmony_ci else if (size == PUD_SIZE && boot_cpu_has(X86_FEATURE_GBPAGES)) 15962306a36Sopenharmony_ci return true; 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci return false; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#ifdef CONFIG_CONTIG_ALLOC 16562306a36Sopenharmony_cistatic __init int gigantic_pages_init(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci /* With compaction or CMA we can allocate gigantic pages at runtime */ 16862306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_GBPAGES)) 16962306a36Sopenharmony_ci hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciarch_initcall(gigantic_pages_init); 17362306a36Sopenharmony_ci#endif 17462306a36Sopenharmony_ci#endif 175