162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __ASM_GENERIC_PGALLOC_H 362306a36Sopenharmony_ci#define __ASM_GENERIC_PGALLOC_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#ifdef CONFIG_MMU 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define GFP_PGTABLE_KERNEL (GFP_KERNEL | __GFP_ZERO) 862306a36Sopenharmony_ci#define GFP_PGTABLE_USER (GFP_PGTABLE_KERNEL | __GFP_ACCOUNT) 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/** 1162306a36Sopenharmony_ci * __pte_alloc_one_kernel - allocate memory for a PTE-level kernel page table 1262306a36Sopenharmony_ci * @mm: the mm_struct of the current context 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This function is intended for architectures that need 1562306a36Sopenharmony_ci * anything beyond simple page allocation. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Return: pointer to the allocated memory or %NULL on error 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic inline pte_t *__pte_alloc_one_kernel(struct mm_struct *mm) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct ptdesc *ptdesc = pagetable_alloc(GFP_PGTABLE_KERNEL & 2262306a36Sopenharmony_ci ~__GFP_HIGHMEM, 0); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (!ptdesc) 2562306a36Sopenharmony_ci return NULL; 2662306a36Sopenharmony_ci return ptdesc_address(ptdesc); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * pte_alloc_one_kernel - allocate memory for a PTE-level kernel page table 3262306a36Sopenharmony_ci * @mm: the mm_struct of the current context 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Return: pointer to the allocated memory or %NULL on error 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci return __pte_alloc_one_kernel(mm); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * pte_free_kernel - free PTE-level kernel page table memory 4462306a36Sopenharmony_ci * @mm: the mm_struct of the current context 4562306a36Sopenharmony_ci * @pte: pointer to the memory containing the page table 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci pagetable_free(virt_to_ptdesc(pte)); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * __pte_alloc_one - allocate memory for a PTE-level user page table 5462306a36Sopenharmony_ci * @mm: the mm_struct of the current context 5562306a36Sopenharmony_ci * @gfp: GFP flags to use for the allocation 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Allocate memory for a page table and ptdesc and runs pagetable_pte_ctor(). 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * This function is intended for architectures that need 6062306a36Sopenharmony_ci * anything beyond simple page allocation or must have custom GFP flags. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Return: `struct page` referencing the ptdesc or %NULL on error 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic inline pgtable_t __pte_alloc_one(struct mm_struct *mm, gfp_t gfp) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct ptdesc *ptdesc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ptdesc = pagetable_alloc(gfp, 0); 6962306a36Sopenharmony_ci if (!ptdesc) 7062306a36Sopenharmony_ci return NULL; 7162306a36Sopenharmony_ci if (!pagetable_pte_ctor(ptdesc)) { 7262306a36Sopenharmony_ci pagetable_free(ptdesc); 7362306a36Sopenharmony_ci return NULL; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return ptdesc_page(ptdesc); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PTE_ALLOC_ONE 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * pte_alloc_one - allocate a page for PTE-level user page table 8262306a36Sopenharmony_ci * @mm: the mm_struct of the current context 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Allocate memory for a page table and ptdesc and runs pagetable_pte_ctor(). 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Return: `struct page` referencing the ptdesc or %NULL on error 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic inline pgtable_t pte_alloc_one(struct mm_struct *mm) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return __pte_alloc_one(mm, GFP_PGTABLE_USER); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci#endif 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Should really implement gc for free page table pages. This could be 9662306a36Sopenharmony_ci * done with a reference count in struct page. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/** 10062306a36Sopenharmony_ci * pte_free - free PTE-level user page table memory 10162306a36Sopenharmony_ci * @mm: the mm_struct of the current context 10262306a36Sopenharmony_ci * @pte_page: the `struct page` referencing the ptdesc 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic inline void pte_free(struct mm_struct *mm, struct page *pte_page) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct ptdesc *ptdesc = page_ptdesc(pte_page); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci pagetable_pte_dtor(ptdesc); 10962306a36Sopenharmony_ci pagetable_free(ptdesc); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 2 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PMD_ALLOC_ONE 11662306a36Sopenharmony_ci/** 11762306a36Sopenharmony_ci * pmd_alloc_one - allocate memory for a PMD-level page table 11862306a36Sopenharmony_ci * @mm: the mm_struct of the current context 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Allocate memory for a page table and ptdesc and runs pagetable_pmd_ctor(). 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * Allocations use %GFP_PGTABLE_USER in user context and 12362306a36Sopenharmony_ci * %GFP_PGTABLE_KERNEL in kernel context. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Return: pointer to the allocated memory or %NULL on error 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct ptdesc *ptdesc; 13062306a36Sopenharmony_ci gfp_t gfp = GFP_PGTABLE_USER; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (mm == &init_mm) 13362306a36Sopenharmony_ci gfp = GFP_PGTABLE_KERNEL; 13462306a36Sopenharmony_ci ptdesc = pagetable_alloc(gfp, 0); 13562306a36Sopenharmony_ci if (!ptdesc) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci if (!pagetable_pmd_ctor(ptdesc)) { 13862306a36Sopenharmony_ci pagetable_free(ptdesc); 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return ptdesc_address(ptdesc); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci#endif 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PMD_FREE 14662306a36Sopenharmony_cistatic inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct ptdesc *ptdesc = virt_to_ptdesc(pmd); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); 15162306a36Sopenharmony_ci pagetable_pmd_dtor(ptdesc); 15262306a36Sopenharmony_ci pagetable_free(ptdesc); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#endif /* CONFIG_PGTABLE_LEVELS > 2 */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 3 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline pud_t *__pud_alloc_one(struct mm_struct *mm, unsigned long addr) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci gfp_t gfp = GFP_PGTABLE_USER; 16362306a36Sopenharmony_ci struct ptdesc *ptdesc; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (mm == &init_mm) 16662306a36Sopenharmony_ci gfp = GFP_PGTABLE_KERNEL; 16762306a36Sopenharmony_ci gfp &= ~__GFP_HIGHMEM; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ptdesc = pagetable_alloc(gfp, 0); 17062306a36Sopenharmony_ci if (!ptdesc) 17162306a36Sopenharmony_ci return NULL; 17262306a36Sopenharmony_ci return ptdesc_address(ptdesc); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PUD_ALLOC_ONE 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * pud_alloc_one - allocate memory for a PUD-level page table 17862306a36Sopenharmony_ci * @mm: the mm_struct of the current context 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Allocate memory for a page table using %GFP_PGTABLE_USER for user context 18162306a36Sopenharmony_ci * and %GFP_PGTABLE_KERNEL for kernel context. 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Return: pointer to the allocated memory or %NULL on error 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci return __pud_alloc_one(mm, addr); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic inline void __pud_free(struct mm_struct *mm, pud_t *pud) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci BUG_ON((unsigned long)pud & (PAGE_SIZE-1)); 19462306a36Sopenharmony_ci pagetable_free(virt_to_ptdesc(pud)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PUD_FREE 19862306a36Sopenharmony_cistatic inline void pud_free(struct mm_struct *mm, pud_t *pud) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci __pud_free(mm, pud); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci#endif 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#endif /* CONFIG_PGTABLE_LEVELS > 3 */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci#ifndef __HAVE_ARCH_PGD_FREE 20762306a36Sopenharmony_cistatic inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci pagetable_free(virt_to_ptdesc(pgd)); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci#endif 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci#endif /* CONFIG_MMU */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#endif /* __ASM_GENERIC_PGALLOC_H */ 216