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