Matthew Wilcox (Oracle)mm: remove mk_huge_pte()7b7aa8a1 year agoHistory
1392 lines · 39.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * This kernel test validates architecture page table helpers and * accessors and helps in verifying their continued compliance with * expected generic MM semantics. * * Copyright (C) 2019 ARM Ltd. * * Author: Anshuman Khandual <anshuman.khandual@arm.com> */#define pr_fmt(fmt) "debug_vm_pgtable: [%-25s]: " fmt, __func__ #include <linux/gfp.h>#include <linux/highmem.h>#include <linux/hugetlb.h>#include <linux/kernel.h>#include <linux/kconfig.h>#include <linux/memblock.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/mm_types.h>#include <linux/module.h>#include <linux/pfn_t.h>#include <linux/printk.h>#include <linux/pgtable.h>#include <linux/random.h>#include <linux/spinlock.h>#include <linux/swap.h>#include <linux/swapops.h>#include <linux/start_kernel.h>#include <linux/sched/mm.h>#include <linux/io.h>#include <linux/vmalloc.h> #include <asm/cacheflush.h>#include <asm/pgalloc.h>#include <asm/tlbflush.h> /* * Please refer Documentation/mm/arch_pgtable_helpers.rst for the semantics * expectations that are being validated here. All future changes in here * or the documentation need to be in sync. */#define RANDOM_NZVALUE	GENMASK(7, 0) struct pgtable_debug_args {	struct mm_struct	*mm;	struct vm_area_struct	*vma; 	pgd_t			*pgdp;	p4d_t			*p4dp;	pud_t			*pudp;	pmd_t			*pmdp;	pte_t			*ptep; 	p4d_t			*start_p4dp;	pud_t			*start_pudp;	pmd_t			*start_pmdp;	pgtable_t		start_ptep; 	unsigned long		vaddr;	pgprot_t		page_prot;	pgprot_t		page_prot_none; 	bool			is_contiguous_page;	unsigned long		pud_pfn;	unsigned long		pmd_pfn;	unsigned long		pte_pfn; 	unsigned long		fixed_alignment;	unsigned long		fixed_pgd_pfn;	unsigned long		fixed_p4d_pfn;	unsigned long		fixed_pud_pfn;	unsigned long		fixed_pmd_pfn;	unsigned long		fixed_pte_pfn;}; static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx){	pgprot_t prot = vm_get_page_prot(idx);	pte_t pte = pfn_pte(args->fixed_pte_pfn, prot);	unsigned long val = idx, *ptr = &val; 	pr_debug("Validating PTE basic (%pGv)\n", ptr); 	/*	 * This test needs to be executed after the given page table entry	 * is created with pfn_pte() to make sure that vm_get_page_prot(idx)	 * does not have the dirty bit enabled from the beginning. This is	 * important for platforms like arm64 where (!PTE_RDONLY) indicate	 * dirty bit being set.	 */	WARN_ON(pte_dirty(pte_wrprotect(pte))); 	WARN_ON(!pte_same(pte, pte));	WARN_ON(!pte_young(pte_mkyoung(pte_mkold(pte))));	WARN_ON(!pte_dirty(pte_mkdirty(pte_mkclean(pte))));	WARN_ON(!pte_write(pte_mkwrite(pte_wrprotect(pte), args->vma)));	WARN_ON(pte_young(pte_mkold(pte_mkyoung(pte))));	WARN_ON(pte_dirty(pte_mkclean(pte_mkdirty(pte))));	WARN_ON(pte_write(pte_wrprotect(pte_mkwrite(pte, args->vma))));	WARN_ON(pte_dirty(pte_wrprotect(pte_mkclean(pte))));	WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte))));} static void __init pte_advanced_tests(struct pgtable_debug_args *args){	struct page *page;	pte_t pte; 	/*	 * Architectures optimize set_pte_at by avoiding TLB flush.	 * This requires set_pte_at to be not used to update an	 * existing pte entry. Clear pte before we do set_pte_at	 *	 * flush_dcache_page() is called after set_pte_at() to clear	 * PG_arch_1 for the page on ARM64. The page flag isn't cleared	 * when it's released and page allocation check will fail when	 * the page is allocated again. For architectures other than ARM64,	 * the unexpected overhead of cache flushing is acceptable.	 */	page = (args->pte_pfn != ULONG_MAX) ? pfn_to_page(args->pte_pfn) : NULL;	if (!page)		return; 	pr_debug("Validating PTE advanced\n");	if (WARN_ON(!args->ptep))		return; 	pte = pfn_pte(args->pte_pfn, args->page_prot);	set_pte_at(args->mm, args->vaddr, args->ptep, pte);	flush_dcache_page(page);	ptep_set_wrprotect(args->mm, args->vaddr, args->ptep);	pte = ptep_get(args->ptep);	WARN_ON(pte_write(pte));	ptep_get_and_clear(args->mm, args->vaddr, args->ptep);	pte = ptep_get(args->ptep);	WARN_ON(!pte_none(pte)); 	pte = pfn_pte(args->pte_pfn, args->page_prot);	pte = pte_wrprotect(pte);	pte = pte_mkclean(pte);	set_pte_at(args->mm, args->vaddr, args->ptep, pte);	flush_dcache_page(page);	pte = pte_mkwrite(pte, args->vma);	pte = pte_mkdirty(pte);	ptep_set_access_flags(args->vma, args->vaddr, args->ptep, pte, 1);	pte = ptep_get(args->ptep);	WARN_ON(!(pte_write(pte) && pte_dirty(pte)));	ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1);	pte = ptep_get(args->ptep);	WARN_ON(!pte_none(pte)); 	pte = pfn_pte(args->pte_pfn, args->page_prot);	pte = pte_mkyoung(pte);	set_pte_at(args->mm, args->vaddr, args->ptep, pte);	flush_dcache_page(page);	ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep);	pte = ptep_get(args->ptep);	WARN_ON(pte_young(pte)); 	ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1);} #ifdef CONFIG_TRANSPARENT_HUGEPAGEstatic void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx){	pgprot_t prot = vm_get_page_prot(idx);	unsigned long val = idx, *ptr = &val;	pmd_t pmd; 	if (!has_transparent_hugepage())		return; 	pr_debug("Validating PMD basic (%pGv)\n", ptr);	pmd = pfn_pmd(args->fixed_pmd_pfn, prot); 	/*	 * This test needs to be executed after the given page table entry	 * is created with pfn_pmd() to make sure that vm_get_page_prot(idx)	 * does not have the dirty bit enabled from the beginning. This is	 * important for platforms like arm64 where (!PTE_RDONLY) indicate	 * dirty bit being set.	 */	WARN_ON(pmd_dirty(pmd_wrprotect(pmd)));  	WARN_ON(!pmd_same(pmd, pmd));	WARN_ON(!pmd_young(pmd_mkyoung(pmd_mkold(pmd))));	WARN_ON(!pmd_dirty(pmd_mkdirty(pmd_mkclean(pmd))));	WARN_ON(!pmd_write(pmd_mkwrite(pmd_wrprotect(pmd), args->vma)));	WARN_ON(pmd_young(pmd_mkold(pmd_mkyoung(pmd))));	WARN_ON(pmd_dirty(pmd_mkclean(pmd_mkdirty(pmd))));	WARN_ON(pmd_write(pmd_wrprotect(pmd_mkwrite(pmd, args->vma))));	WARN_ON(pmd_dirty(pmd_wrprotect(pmd_mkclean(pmd))));	WARN_ON(!pmd_dirty(pmd_wrprotect(pmd_mkdirty(pmd))));	/*	 * A huge page does not point to next level page table	 * entry. Hence this must qualify as pmd_bad().	 */	WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));} static void __init pmd_advanced_tests(struct pgtable_debug_args *args){	struct page *page;	pmd_t pmd;	unsigned long vaddr = args->vaddr; 	if (!has_transparent_hugepage())		return; 	page = (args->pmd_pfn != ULONG_MAX) ? pfn_to_page(args->pmd_pfn) : NULL;	if (!page)		return; 	/*	 * flush_dcache_page() is called after set_pmd_at() to clear	 * PG_arch_1 for the page on ARM64. The page flag isn't cleared	 * when it's released and page allocation check will fail when	 * the page is allocated again. For architectures other than ARM64,	 * the unexpected overhead of cache flushing is acceptable.	 */	pr_debug("Validating PMD advanced\n");	/* Align the address wrt HPAGE_PMD_SIZE */	vaddr &= HPAGE_PMD_MASK; 	pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep); 	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);	flush_dcache_page(page);	pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);	pmd = pmdp_get(args->pmdp);	WARN_ON(pmd_write(pmd));	pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);	pmd = pmdp_get(args->pmdp);	WARN_ON(!pmd_none(pmd)); 	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);	pmd = pmd_wrprotect(pmd);	pmd = pmd_mkclean(pmd);	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);	flush_dcache_page(page);	pmd = pmd_mkwrite(pmd, args->vma);	pmd = pmd_mkdirty(pmd);	pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);	pmd = pmdp_get(args->pmdp);	WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));	pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);	pmd = pmdp_get(args->pmdp);	WARN_ON(!pmd_none(pmd)); 	pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));	pmd = pmd_mkyoung(pmd);	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);	flush_dcache_page(page);	pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);	pmd = pmdp_get(args->pmdp);	WARN_ON(pmd_young(pmd)); 	/*  Clear the pte entries  */	pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);	pgtable_trans_huge_withdraw(args->mm, args->pmdp);} static void __init pmd_leaf_tests(struct pgtable_debug_args *args){	pmd_t pmd; 	if (!has_transparent_hugepage())		return; 	pr_debug("Validating PMD leaf\n");	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot); 	/*	 * PMD based THP is a leaf entry.	 */	pmd = pmd_mkhuge(pmd);	WARN_ON(!pmd_leaf(pmd));} #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUDstatic void __init pud_basic_tests(struct pgtable_debug_args *args, int idx){	pgprot_t prot = vm_get_page_prot(idx);	unsigned long val = idx, *ptr = &val;	pud_t pud; 	if (!has_transparent_pud_hugepage())		return; 	pr_debug("Validating PUD basic (%pGv)\n", ptr);	pud = pfn_pud(args->fixed_pud_pfn, prot); 	/*	 * This test needs to be executed after the given page table entry	 * is created with pfn_pud() to make sure that vm_get_page_prot(idx)	 * does not have the dirty bit enabled from the beginning. This is	 * important for platforms like arm64 where (!PTE_RDONLY) indicate	 * dirty bit being set.	 */	WARN_ON(pud_dirty(pud_wrprotect(pud))); 	WARN_ON(!pud_same(pud, pud));	WARN_ON(!pud_young(pud_mkyoung(pud_mkold(pud))));	WARN_ON(!pud_dirty(pud_mkdirty(pud_mkclean(pud))));	WARN_ON(pud_dirty(pud_mkclean(pud_mkdirty(pud))));	WARN_ON(!pud_write(pud_mkwrite(pud_wrprotect(pud))));	WARN_ON(pud_write(pud_wrprotect(pud_mkwrite(pud))));	WARN_ON(pud_young(pud_mkold(pud_mkyoung(pud))));	WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud))));	WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud)))); 	if (mm_pmd_folded(args->mm))		return; 	/*	 * A huge page does not point to next level page table	 * entry. Hence this must qualify as pud_bad().	 */	WARN_ON(!pud_bad(pud_mkhuge(pud)));} static void __init pud_advanced_tests(struct pgtable_debug_args *args){	struct page *page;	unsigned long vaddr = args->vaddr;	pud_t pud; 	if (!has_transparent_pud_hugepage())		return; 	page = (args->pud_pfn != ULONG_MAX) ? pfn_to_page(args->pud_pfn) : NULL;	if (!page)		return; 	/*	 * flush_dcache_page() is called after set_pud_at() to clear	 * PG_arch_1 for the page on ARM64. The page flag isn't cleared	 * when it's released and page allocation check will fail when	 * the page is allocated again. For architectures other than ARM64,	 * the unexpected overhead of cache flushing is acceptable.	 */	pr_debug("Validating PUD advanced\n");	/* Align the address wrt HPAGE_PUD_SIZE */	vaddr &= HPAGE_PUD_MASK; 	pud = pfn_pud(args->pud_pfn, args->page_prot);	/*	 * Some architectures have debug checks to make sure	 * huge pud mapping are only found with devmap entries	 * For now test with only devmap entries.	 */	pud = pud_mkdevmap(pud);	set_pud_at(args->mm, vaddr, args->pudp, pud);	flush_dcache_page(page);	pudp_set_wrprotect(args->mm, vaddr, args->pudp);	pud = pudp_get(args->pudp);	WARN_ON(pud_write(pud)); #ifndef __PAGETABLE_PMD_FOLDED	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);	pud = pudp_get(args->pudp);	WARN_ON(!pud_none(pud));#endif /* __PAGETABLE_PMD_FOLDED */	pud = pfn_pud(args->pud_pfn, args->page_prot);	pud = pud_mkdevmap(pud);	pud = pud_wrprotect(pud);	pud = pud_mkclean(pud);	set_pud_at(args->mm, vaddr, args->pudp, pud);	flush_dcache_page(page);	pud = pud_mkwrite(pud);	pud = pud_mkdirty(pud);	pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1);	pud = pudp_get(args->pudp);	WARN_ON(!(pud_write(pud) && pud_dirty(pud))); #ifndef __PAGETABLE_PMD_FOLDED	pudp_huge_get_and_clear_full(args->vma, vaddr, args->pudp, 1);	pud = pudp_get(args->pudp);	WARN_ON(!pud_none(pud));#endif /* __PAGETABLE_PMD_FOLDED */ 	pud = pfn_pud(args->pud_pfn, args->page_prot);	pud = pud_mkdevmap(pud);	pud = pud_mkyoung(pud);	set_pud_at(args->mm, vaddr, args->pudp, pud);	flush_dcache_page(page);	pudp_test_and_clear_young(args->vma, vaddr, args->pudp);	pud = pudp_get(args->pudp);	WARN_ON(pud_young(pud)); 	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);} static void __init pud_leaf_tests(struct pgtable_debug_args *args){	pud_t pud; 	if (!has_transparent_pud_hugepage())		return; 	pr_debug("Validating PUD leaf\n");	pud = pfn_pud(args->fixed_pud_pfn, args->page_prot);	/*	 * PUD based THP is a leaf entry.	 */	pud = pud_mkhuge(pud);	WARN_ON(!pud_leaf(pud));}#else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pud_advanced_tests(struct pgtable_debug_args *args) { }static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */#else  /* !CONFIG_TRANSPARENT_HUGEPAGE */static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }static void __init pud_advanced_tests(struct pgtable_debug_args *args) { }static void __init pmd_leaf_tests(struct pgtable_debug_args *args) { }static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMAPstatic void __init pmd_huge_tests(struct pgtable_debug_args *args){	pmd_t pmd; 	if (!arch_vmap_pmd_supported(args->page_prot) ||	    args->fixed_alignment < PMD_SIZE)		return; 	pr_debug("Validating PMD huge\n");	/*	 * X86 defined pmd_set_huge() verifies that the given	 * PMD is not a populated non-leaf entry.	 */	WRITE_ONCE(*args->pmdp, __pmd(0));	WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn), args->page_prot));	WARN_ON(!pmd_clear_huge(args->pmdp));	pmd = pmdp_get(args->pmdp);	WARN_ON(!pmd_none(pmd));} static void __init pud_huge_tests(struct pgtable_debug_args *args){	pud_t pud; 	if (!arch_vmap_pud_supported(args->page_prot) ||	    args->fixed_alignment < PUD_SIZE)		return; 	pr_debug("Validating PUD huge\n");	/*	 * X86 defined pud_set_huge() verifies that the given	 * PUD is not a populated non-leaf entry.	 */	WRITE_ONCE(*args->pudp, __pud(0));	WARN_ON(!pud_set_huge(args->pudp, __pfn_to_phys(args->fixed_pud_pfn), args->page_prot));	WARN_ON(!pud_clear_huge(args->pudp));	pud = pudp_get(args->pudp);	WARN_ON(!pud_none(pud));}#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }static void __init pud_huge_tests(struct pgtable_debug_args *args) { }#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */ static void __init p4d_basic_tests(struct pgtable_debug_args *args){	p4d_t p4d; 	pr_debug("Validating P4D basic\n");	memset(&p4d, RANDOM_NZVALUE, sizeof(p4d_t));	WARN_ON(!p4d_same(p4d, p4d));} static void __init pgd_basic_tests(struct pgtable_debug_args *args){	pgd_t pgd; 	pr_debug("Validating PGD basic\n");	memset(&pgd, RANDOM_NZVALUE, sizeof(pgd_t));	WARN_ON(!pgd_same(pgd, pgd));} #ifndef __PAGETABLE_PUD_FOLDEDstatic void __init pud_clear_tests(struct pgtable_debug_args *args){	pud_t pud = pudp_get(args->pudp); 	if (mm_pmd_folded(args->mm))		return; 	pr_debug("Validating PUD clear\n");	WARN_ON(pud_none(pud));	pud_clear(args->pudp);

Lines 1–500 of 1392.