mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
Four architectures currently implement 5-level pgtables: arm64, riscv, x86 and s390. The first three have essentially the same implementation for p4d_alloc_one() and p4d_free(), so we've got an opportunity to reduce duplication like at the lower levels. Provide a generic version of p4d_alloc_one() and p4d_free(), and make use of it on those architectures. Their implementation is the same as at PUD level, except that p4d_free() performs a runtime check by calling mm_p4d_folded(). 5-level pgtables depend on a runtime-detected hardware feature on all supported architectures, so we might as well include this check in the generic implementation. No runtime check is required in p4d_alloc_one() as the top-level p4d_alloc() already does the required check. Link: https://lkml.kernel.org/r/26d69c74a29183ecc335b9b407040d8e4cd70c6a.1736317725.git.zhengqi.arch@bytedance.com Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com> Acked-by: Dave Hansen <dave.hansen@linux.intel.com> Acked-by: Arnd Bergmann <arnd@arndb.de> [asm-generic] Cc: Alexander Gordeev <agordeev@linux.ibm.com> Cc: Alexandre Ghiti <alex@ghiti.fr> Cc: Alexandre Ghiti <alexghiti@rivosinc.com> Cc: Andreas Larsson <andreas@gaisler.com> Cc: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org> Cc: David Hildenbrand <david@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Jann Horn <jannh@google.com> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Muchun Song <muchun.song@linux.dev> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vishal Moola (Oracle) <vishal.moola@gmail.com> Cc: Will Deacon <will@kernel.org> Cc: Yu Zhao <yuzhao@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
164 lines
4.8 KiB
C
164 lines
4.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_X86_PGALLOC_H
|
|
#define _ASM_X86_PGALLOC_H
|
|
|
|
#include <linux/threads.h>
|
|
#include <linux/mm.h> /* for struct page */
|
|
#include <linux/pagemap.h>
|
|
|
|
#define __HAVE_ARCH_PTE_ALLOC_ONE
|
|
#define __HAVE_ARCH_PGD_FREE
|
|
#include <asm-generic/pgalloc.h>
|
|
|
|
static inline int __paravirt_pgd_alloc(struct mm_struct *mm) { return 0; }
|
|
|
|
#ifdef CONFIG_PARAVIRT_XXL
|
|
#include <asm/paravirt.h>
|
|
#else
|
|
#define paravirt_pgd_alloc(mm) __paravirt_pgd_alloc(mm)
|
|
static inline void paravirt_pgd_free(struct mm_struct *mm, pgd_t *pgd) {}
|
|
static inline void paravirt_alloc_pte(struct mm_struct *mm, unsigned long pfn) {}
|
|
static inline void paravirt_alloc_pmd(struct mm_struct *mm, unsigned long pfn) {}
|
|
static inline void paravirt_alloc_pmd_clone(unsigned long pfn, unsigned long clonepfn,
|
|
unsigned long start, unsigned long count) {}
|
|
static inline void paravirt_alloc_pud(struct mm_struct *mm, unsigned long pfn) {}
|
|
static inline void paravirt_alloc_p4d(struct mm_struct *mm, unsigned long pfn) {}
|
|
static inline void paravirt_release_pte(unsigned long pfn) {}
|
|
static inline void paravirt_release_pmd(unsigned long pfn) {}
|
|
static inline void paravirt_release_pud(unsigned long pfn) {}
|
|
static inline void paravirt_release_p4d(unsigned long pfn) {}
|
|
#endif
|
|
|
|
/*
|
|
* Flags to use when allocating a user page table page.
|
|
*/
|
|
extern gfp_t __userpte_alloc_gfp;
|
|
|
|
#ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION
|
|
/*
|
|
* Instead of one PGD, we acquire two PGDs. Being order-1, it is
|
|
* both 8k in size and 8k-aligned. That lets us just flip bit 12
|
|
* in a pointer to swap between the two 4k halves.
|
|
*/
|
|
#define PGD_ALLOCATION_ORDER 1
|
|
#else
|
|
#define PGD_ALLOCATION_ORDER 0
|
|
#endif
|
|
|
|
/*
|
|
* Allocate and free page tables.
|
|
*/
|
|
extern pgd_t *pgd_alloc(struct mm_struct *);
|
|
extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
|
|
|
|
extern pgtable_t pte_alloc_one(struct mm_struct *);
|
|
|
|
extern void ___pte_free_tlb(struct mmu_gather *tlb, struct page *pte);
|
|
|
|
static inline void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte,
|
|
unsigned long address)
|
|
{
|
|
___pte_free_tlb(tlb, pte);
|
|
}
|
|
|
|
static inline void pmd_populate_kernel(struct mm_struct *mm,
|
|
pmd_t *pmd, pte_t *pte)
|
|
{
|
|
paravirt_alloc_pte(mm, __pa(pte) >> PAGE_SHIFT);
|
|
set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE));
|
|
}
|
|
|
|
static inline void pmd_populate_kernel_safe(struct mm_struct *mm,
|
|
pmd_t *pmd, pte_t *pte)
|
|
{
|
|
paravirt_alloc_pte(mm, __pa(pte) >> PAGE_SHIFT);
|
|
set_pmd_safe(pmd, __pmd(__pa(pte) | _PAGE_TABLE));
|
|
}
|
|
|
|
static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
|
|
struct page *pte)
|
|
{
|
|
unsigned long pfn = page_to_pfn(pte);
|
|
|
|
paravirt_alloc_pte(mm, pfn);
|
|
set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE));
|
|
}
|
|
|
|
#if CONFIG_PGTABLE_LEVELS > 2
|
|
extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd);
|
|
|
|
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
|
|
unsigned long address)
|
|
{
|
|
___pmd_free_tlb(tlb, pmd);
|
|
}
|
|
|
|
#ifdef CONFIG_X86_PAE
|
|
extern void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd);
|
|
#else /* !CONFIG_X86_PAE */
|
|
static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
|
|
{
|
|
paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT);
|
|
set_pud(pud, __pud(_PAGE_TABLE | __pa(pmd)));
|
|
}
|
|
|
|
static inline void pud_populate_safe(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
|
|
{
|
|
paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT);
|
|
set_pud_safe(pud, __pud(_PAGE_TABLE | __pa(pmd)));
|
|
}
|
|
#endif /* CONFIG_X86_PAE */
|
|
|
|
#if CONFIG_PGTABLE_LEVELS > 3
|
|
static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4d, pud_t *pud)
|
|
{
|
|
paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT);
|
|
set_p4d(p4d, __p4d(_PAGE_TABLE | __pa(pud)));
|
|
}
|
|
|
|
static inline void p4d_populate_safe(struct mm_struct *mm, p4d_t *p4d, pud_t *pud)
|
|
{
|
|
paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT);
|
|
set_p4d_safe(p4d, __p4d(_PAGE_TABLE | __pa(pud)));
|
|
}
|
|
|
|
extern void ___pud_free_tlb(struct mmu_gather *tlb, pud_t *pud);
|
|
|
|
static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
|
|
unsigned long address)
|
|
{
|
|
___pud_free_tlb(tlb, pud);
|
|
}
|
|
|
|
#if CONFIG_PGTABLE_LEVELS > 4
|
|
static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, p4d_t *p4d)
|
|
{
|
|
if (!pgtable_l5_enabled())
|
|
return;
|
|
paravirt_alloc_p4d(mm, __pa(p4d) >> PAGE_SHIFT);
|
|
set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(p4d)));
|
|
}
|
|
|
|
static inline void pgd_populate_safe(struct mm_struct *mm, pgd_t *pgd, p4d_t *p4d)
|
|
{
|
|
if (!pgtable_l5_enabled())
|
|
return;
|
|
paravirt_alloc_p4d(mm, __pa(p4d) >> PAGE_SHIFT);
|
|
set_pgd_safe(pgd, __pgd(_PAGE_TABLE | __pa(p4d)));
|
|
}
|
|
|
|
extern void ___p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4d);
|
|
|
|
static inline void __p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4d,
|
|
unsigned long address)
|
|
{
|
|
if (pgtable_l5_enabled())
|
|
___p4d_free_tlb(tlb, p4d);
|
|
}
|
|
|
|
#endif /* CONFIG_PGTABLE_LEVELS > 4 */
|
|
#endif /* CONFIG_PGTABLE_LEVELS > 3 */
|
|
#endif /* CONFIG_PGTABLE_LEVELS > 2 */
|
|
|
|
#endif /* _ASM_X86_PGALLOC_H */
|