linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements
@ 2021-07-19 13:06 Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args Gavin Shan
                   ` (11 more replies)
  0 siblings, 12 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

There are couple of issues with current implementations and this series
tries to resolve the issues:

  (a) All needed information are scattered in variables, passed to various
      test functions. The code is organized in pretty much relaxed fashion.

  (b) The page isn't allocated from buddy during page table entry modifying
      tests. The page can be invalid, conflicting to the implementations
      of set_xxx_at() on ARM64. The target page is accessed so that the iCache
      can be flushed when execution permission is given on ARM64. Besides,
      the target page can be unmapped and access to it causes kernel crash.

"struct pgtable_debug_args" is introduced to address issue (a). For issue
(b), the used page is allocated from buddy in page table entry modifying
tests. The corresponding tets will be skipped if we fail to allocate the
(huge) page. For other test cases, the original page around to kernel
symbol (@start_kernel) is still used.

The patches are organized as below. PATCH[2-10] could be combined to one
patch, but it will make the review harder:

  PATCH[1] introduces "struct pgtable_debug_args" as place holder of all
           needed information. With it, the old and new implementation
           can coexist.
  PATCH[2-10] uses "struct pgtable_debug_args" in various test functions.
  PATCH[11] removes the unused code for old implementation.
  PATCH[12] fixes the issue of corrupted page flag for ARM64

Changelog
=========
v3:
   * Fix the warning caused by allocating more pages than
     (1 << (MAX_ORDER - 1)) in init_args()                       (syzbot)
   * Fix build warning by dropping unused variables in separate
     patches                                                     (0-day)
   * Missed "WARN_ON(!pud_none(pud))" in pud_huge_tests() in
     PATCH[v2 09/12]                                             (0-day)
   * Fix the subjects for PATCH[05/12] and PATCH[09/12]          (Gavin)
v2:
   * Rename struct vm_pgtable_debug to struct pgtable_debug_args.
     The parameter name to various test functions are renamed
     to "@args"                                                  (Anshuman)
   * Code changes as suggested by Anshuman                       (Anshuman)

Gavin Shan (12):
  mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in leaf and
    savewrite tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and
    devmap tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in soft_dirty and
    swap tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and
    thp tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying
    tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying
    tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying
    tests
  mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D
    modifying tests
  mm/debug_vm_pgtable: Remove unused code
  mm/debug_vm_pgtable: Fix corrupted page flag

 mm/debug_vm_pgtable.c | 849 ++++++++++++++++++++++++------------------
 1 file changed, 480 insertions(+), 369 deletions(-)

-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-21  5:44   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests Gavin Shan
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

In debug_vm_pgtable(), there are many local variables introduced to
track the needed information and they are passed to the functions for
various test cases. It'd better to introduce a struct as place holder
for these information. With it, what the functions for various test
cases need is the struct, to simplify the code. It also makes code
easier to be maintained.

Besides, set_xxx_at() could access the data on the corresponding pages
in the page table modifying tests. So the accessed pages in the tests
should have been allocated from buddy. Otherwise, we're accessing pages
that aren't owned by us. This causes issues like page flag corruption.

This introduces "struct pgtable_debug_args". The struct is initialized
and destroyed, but the information in the struct isn't used yet. They
will be used in subsequent patches.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 196 insertions(+), 1 deletion(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 1c922691aa61..ea153ff40d23 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -58,6 +58,36 @@
 #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
 #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;
+
+	unsigned long		pud_pfn;
+	unsigned long		pmd_pfn;
+	unsigned long		pte_pfn;
+
+	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(unsigned long pfn, int idx)
 {
 	pgprot_t prot = protection_map[idx];
@@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
 	return random_vaddr;
 }
 
+static void __init destroy_args(struct pgtable_debug_args *args)
+{
+	struct page *page = NULL;
+
+	/* Free (huge) page */
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
+	    has_transparent_hugepage() &&
+	    args->pud_pfn != ULONG_MAX) {
+		page = pfn_to_page(args->pud_pfn);
+		__free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
+	} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+		   has_transparent_hugepage() &&
+		   args->pmd_pfn != ULONG_MAX) {
+		page = pfn_to_page(args->pmd_pfn);
+		__free_pages(page, HPAGE_PMD_ORDER);
+	} else if (args->pte_pfn != ULONG_MAX) {
+		page = pfn_to_page(args->pte_pfn);
+		__free_pages(page, 0);
+	}
+
+	/* Free page table */
+	if (args->start_ptep) {
+		pte_free(args->mm, args->start_ptep);
+		mm_dec_nr_ptes(args->mm);
+	}
+
+	if (args->start_pmdp) {
+		pmd_free(args->mm, args->start_pmdp);
+		mm_dec_nr_pmds(args->mm);
+	}
+
+	if (args->start_pudp) {
+		pud_free(args->mm, args->start_pudp);
+		mm_dec_nr_puds(args->mm);
+	}
+
+	if (args->start_p4dp)
+		p4d_free(args->mm, args->p4dp);
+
+	/* Free vma and mm struct */
+	if (args->vma)
+		vm_area_free(args->vma);
+	if (args->mm)
+		mmdrop(args->mm);
+}
+
+static int __init init_args(struct pgtable_debug_args *args)
+{
+	struct page *page = NULL;
+	phys_addr_t phys;
+	int ret = 0;
+
+	/* Initialize the debugging data */
+	memset(args, 0, sizeof(*args));
+	args->page_prot      = vm_get_page_prot(VMFLAGS);
+	args->page_prot_none = __P000;
+	args->pud_pfn        = ULONG_MAX;
+	args->pmd_pfn        = ULONG_MAX;
+	args->pte_pfn        = ULONG_MAX;
+	args->fixed_pgd_pfn  = ULONG_MAX;
+	args->fixed_p4d_pfn  = ULONG_MAX;
+	args->fixed_pud_pfn  = ULONG_MAX;
+	args->fixed_pmd_pfn  = ULONG_MAX;
+	args->fixed_pte_pfn  = ULONG_MAX;
+
+	/* Allocate mm and vma */
+	args->mm = mm_alloc();
+	if (!args->mm) {
+		pr_err("Failed to allocate mm struct\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	args->vma = vm_area_alloc(args->mm);
+	if (!args->vma) {
+		pr_err("Failed to allocate vma\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	/* Figure out the virtual address and allocate page table entries */
+	args->vaddr = get_random_vaddr();
+	args->pgdp = pgd_offset(args->mm, args->vaddr);
+	args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
+	args->pudp = args->p4dp ?
+		     pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
+	args->pmdp = args->pudp ?
+		     pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
+	args->ptep = args->pmdp ?
+		     pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
+	if (!args->ptep) {
+		pr_err("Failed to allocate page table\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	/*
+	 * The above page table entries will be modified. Lets save the
+	 * page table entries so that they can be released when the tests
+	 * are completed.
+	 */
+	args->start_p4dp = p4d_offset(args->pgdp, 0UL);
+	args->start_pudp = pud_offset(args->p4dp, 0UL);
+	args->start_pmdp = pmd_offset(args->pudp, 0UL);
+	args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
+
+	/*
+	 * Figure out the fixed addresses, which are all around the kernel
+	 * symbol (@start_kernel). The corresponding PFNs might be invalid,
+	 * but it's fine as the following tests won't access the pages.
+	 */
+	phys = __pa_symbol(&start_kernel);
+	args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
+	args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
+	args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
+	args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
+	args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
+
+	/*
+	 * Allocate (huge) pages because some of the tests need to access
+	 * the data in the pages. The corresponding tests will be skipped
+	 * if we fail to allocate (huge) pages.
+	 */
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
+	    has_transparent_hugepage()) {
+		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
+				   HPAGE_PUD_SHIFT - PAGE_SHIFT);
+		if (page) {
+			args->pud_pfn = page_to_pfn(page);
+			args->pmd_pfn = args->pud_pfn;
+			args->pte_pfn = args->pud_pfn;
+			return 0;
+		}
+	}
+
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    has_transparent_hugepage()) {
+		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN, HPAGE_PMD_ORDER);
+		if (page) {
+			args->pmd_pfn = page_to_pfn(page);
+			args->pte_pfn = args->pmd_pfn;
+			return 0;
+		}
+	}
+
+	page = alloc_pages(GFP_KERNEL, 0);
+	if (page)
+		args->pte_pfn = page_to_pfn(page);
+
+	return 0;
+
+error:
+	destroy_args(args);
+	return ret;
+}
+
 static int __init debug_vm_pgtable(void)
 {
+	struct pgtable_debug_args args;
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
 	pgd_t *pgdp;
@@ -970,9 +1159,13 @@ static int __init debug_vm_pgtable(void)
 	unsigned long vaddr, pte_aligned, pmd_aligned;
 	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
 	spinlock_t *ptl = NULL;
-	int idx;
+	int idx, ret;
 
 	pr_info("Validating architecture page table helpers\n");
+	ret = init_args(&args);
+	if (ret)
+		return ret;
+
 	prot = vm_get_page_prot(VMFLAGS);
 	vaddr = get_random_vaddr();
 	mm = mm_alloc();
@@ -1127,6 +1320,8 @@ static int __init debug_vm_pgtable(void)
 	mm_dec_nr_pmds(mm);
 	mm_dec_nr_ptes(mm);
 	mmdrop(mm);
+
+	destroy_args(&args);
 	return 0;
 }
 late_initcall(debug_vm_pgtable);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-21  5:57   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 03/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in leaf and savewrite tests Gavin Shan
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in the basic test functions. There
are more cleanups applied:

   * Move hugetlb_basic_tests() so that the basic tests are treated
     as a bundle.

   * Drop parameters to {pgd, p4d}_basic_tests() as they're never
     used.

   * Drop unused variables @{pgd, p4d}_aligned in debug_vm_pgtable().

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 51 ++++++++++++++++++++-----------------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index ea153ff40d23..5f79d725f8d8 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -88,10 +88,10 @@ struct pgtable_debug_args {
 	unsigned long		fixed_pte_pfn;
 };
 
-static void __init pte_basic_tests(unsigned long pfn, int idx)
+static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
 {
 	pgprot_t prot = protection_map[idx];
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, prot);
 	unsigned long val = idx, *ptr = &val;
 
 	pr_debug("Validating PTE basic (%pGv)\n", ptr);
@@ -173,7 +173,7 @@ static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot)
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static void __init pmd_basic_tests(unsigned long pfn, int idx)
+static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
 {
 	pgprot_t prot = protection_map[idx];
 	unsigned long val = idx, *ptr = &val;
@@ -183,7 +183,7 @@ static void __init pmd_basic_tests(unsigned long pfn, int idx)
 		return;
 
 	pr_debug("Validating PMD basic (%pGv)\n", ptr);
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, prot);
 
 	/*
 	 * This test needs to be executed after the given page table entry
@@ -295,7 +295,7 @@ static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot)
 }
 
 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
-static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx)
+static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
 {
 	pgprot_t prot = protection_map[idx];
 	unsigned long val = idx, *ptr = &val;
@@ -305,7 +305,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
 		return;
 
 	pr_debug("Validating PUD basic (%pGv)\n", ptr);
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->fixed_pud_pfn, prot);
 
 	/*
 	 * This test needs to be executed after the given page table entry
@@ -326,7 +326,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
 	WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud))));
 	WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud))));
 
-	if (mm_pmd_folded(mm))
+	if (mm_pmd_folded(args->mm))
 		return;
 
 	/*
@@ -403,7 +403,7 @@ static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot)
 	WARN_ON(!pud_leaf(pud));
 }
 #else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
-static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
+static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }
 static void __init pud_advanced_tests(struct mm_struct *mm,
 				      struct vm_area_struct *vma, pud_t *pudp,
 				      unsigned long pfn, unsigned long vaddr,
@@ -413,8 +413,8 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
 static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { }
 #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
 #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
-static void __init pmd_basic_tests(unsigned long pfn, int idx) { }
-static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
+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 mm_struct *mm,
 				      struct vm_area_struct *vma, pmd_t *pmdp,
 				      unsigned long pfn, unsigned long vaddr,
@@ -475,7 +475,7 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
 static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
 #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
 
-static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
+static void __init p4d_basic_tests(void)
 {
 	p4d_t p4d;
 
@@ -484,7 +484,7 @@ static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
 	WARN_ON(!p4d_same(p4d, p4d));
 }
 
-static void __init pgd_basic_tests(unsigned long pfn, pgprot_t prot)
+static void __init pgd_basic_tests(void)
 {
 	pgd_t pgd;
 
@@ -889,7 +889,7 @@ static void __init swap_migration_tests(void)
 }
 
 #ifdef CONFIG_HUGETLB_PAGE
-static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
+static void __init hugetlb_basic_tests(struct pgtable_debug_args *args)
 {
 	struct page *page;
 	pte_t pte;
@@ -899,21 +899,21 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
 	 * Accessing the page associated with the pfn is safe here,
 	 * as it was previously derived from a real kernel symbol.
 	 */
-	page = pfn_to_page(pfn);
-	pte = mk_huge_pte(page, prot);
+	page = pfn_to_page(args->fixed_pmd_pfn);
+	pte = mk_huge_pte(page, args->page_prot);
 
 	WARN_ON(!huge_pte_dirty(huge_pte_mkdirty(pte)));
 	WARN_ON(!huge_pte_write(huge_pte_mkwrite(huge_pte_wrprotect(pte))));
 	WARN_ON(huge_pte_write(huge_pte_wrprotect(huge_pte_mkwrite(pte))));
 
 #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
-	pte = pfn_pte(pfn, prot);
+	pte = pfn_pte(args->fixed_pmd_pfn, args->page_prot);
 
 	WARN_ON(!pte_huge(pte_mkhuge(pte)));
 #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
 }
 #else  /* !CONFIG_HUGETLB_PAGE */
-static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init hugetlb_basic_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_HUGETLB_PAGE */
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -1157,7 +1157,7 @@ static int __init debug_vm_pgtable(void)
 	pgprot_t prot, protnone;
 	phys_addr_t paddr;
 	unsigned long vaddr, pte_aligned, pmd_aligned;
-	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
+	unsigned long pud_aligned;
 	spinlock_t *ptl = NULL;
 	int idx, ret;
 
@@ -1200,8 +1200,6 @@ static int __init debug_vm_pgtable(void)
 	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;
 	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
 	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
-	p4d_aligned = (paddr & P4D_MASK) >> PAGE_SHIFT;
-	pgd_aligned = (paddr & PGDIR_MASK) >> PAGE_SHIFT;
 	WARN_ON(!pfn_valid(pte_aligned));
 
 	pgdp = pgd_offset(mm, vaddr);
@@ -1235,9 +1233,9 @@ static int __init debug_vm_pgtable(void)
 	 * given page table entry.
 	 */
 	for (idx = 0; idx < ARRAY_SIZE(protection_map); idx++) {
-		pte_basic_tests(pte_aligned, idx);
-		pmd_basic_tests(pmd_aligned, idx);
-		pud_basic_tests(mm, pud_aligned, idx);
+		pte_basic_tests(&args, idx);
+		pmd_basic_tests(&args, idx);
+		pud_basic_tests(&args, idx);
 	}
 
 	/*
@@ -1247,8 +1245,9 @@ static int __init debug_vm_pgtable(void)
 	 * the above iteration for now to save some test execution
 	 * time.
 	 */
-	p4d_basic_tests(p4d_aligned, prot);
-	pgd_basic_tests(pgd_aligned, prot);
+	p4d_basic_tests();
+	pgd_basic_tests();
+	hugetlb_basic_tests(&args);
 
 	pmd_leaf_tests(pmd_aligned, prot);
 	pud_leaf_tests(pud_aligned, prot);
@@ -1277,8 +1276,6 @@ static int __init debug_vm_pgtable(void)
 	pmd_thp_tests(pmd_aligned, prot);
 	pud_thp_tests(pud_aligned, prot);
 
-	hugetlb_basic_tests(pte_aligned, prot);
-
 	/*
 	 * Page table modifying tests. They need to hold
 	 * proper page table lock.
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 03/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in leaf and savewrite tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 04/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and devmap tests Gavin Shan
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in the leaf and savewrite test
functions.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 5f79d725f8d8..b4b33afae942 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -160,9 +160,9 @@ static void __init pte_advanced_tests(struct mm_struct *mm,
 	WARN_ON(pte_young(pte));
 }
 
-static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_savedwrite_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot_none);
 
 	if (!IS_ENABLED(CONFIG_NUMA_BALANCING))
 		return;
@@ -261,7 +261,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm,
 	pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
 }
 
-static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_leaf_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -269,7 +269,7 @@ static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD leaf\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 
 	/*
 	 * PMD based THP is a leaf entry.
@@ -278,7 +278,7 @@ static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot)
 	WARN_ON(!pmd_leaf(pmd));
 }
 
-static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -289,7 +289,7 @@ static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD saved write\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot_none);
 	WARN_ON(!pmd_savedwrite(pmd_mk_savedwrite(pmd_clear_savedwrite(pmd))));
 	WARN_ON(pmd_savedwrite(pmd_clear_savedwrite(pmd_mk_savedwrite(pmd))));
 }
@@ -387,7 +387,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
 	pudp_huge_get_and_clear(mm, vaddr, pudp);
 }
 
-static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot)
+static void __init pud_leaf_tests(struct pgtable_debug_args *args)
 {
 	pud_t pud;
 
@@ -395,7 +395,7 @@ static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PUD leaf\n");
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->fixed_pud_pfn, args->page_prot);
 	/*
 	 * PUD based THP is a leaf entry.
 	 */
@@ -410,7 +410,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
 				      pgprot_t prot)
 {
 }
-static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { }
+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) { }
@@ -427,9 +427,9 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
 				      pgprot_t prot)
 {
 }
-static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_leaf_tests(struct pgtable_debug_args *args) { }
+static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
+static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
@@ -1249,11 +1249,11 @@ static int __init debug_vm_pgtable(void)
 	pgd_basic_tests();
 	hugetlb_basic_tests(&args);
 
-	pmd_leaf_tests(pmd_aligned, prot);
-	pud_leaf_tests(pud_aligned, prot);
+	pmd_leaf_tests(&args);
+	pud_leaf_tests(&args);
 
-	pte_savedwrite_tests(pte_aligned, protnone);
-	pmd_savedwrite_tests(pmd_aligned, protnone);
+	pte_savedwrite_tests(&args);
+	pmd_savedwrite_tests(&args);
 
 	pte_special_tests(pte_aligned, prot);
 	pte_protnone_tests(pte_aligned, protnone);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 04/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and devmap tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (2 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 03/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in leaf and savewrite tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 05/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in soft_dirty and swap tests Gavin Shan
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in protnone and devmap test
functions. After that, the unused variable @protnone in debug_vm_pgtable()
is dropped.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 58 +++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 32 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index b4b33afae942..1ae204831484 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -661,9 +661,9 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
 	WARN_ON(pmd_bad(pmd));
 }
 
-static void __init pte_special_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_special_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot);
 
 	if (!IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL))
 		return;
@@ -672,9 +672,9 @@ static void __init pte_special_tests(unsigned long pfn, pgprot_t prot)
 	WARN_ON(!pte_special(pte_mkspecial(pte)));
 }
 
-static void __init pte_protnone_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_protnone_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot_none);
 
 	if (!IS_ENABLED(CONFIG_NUMA_BALANCING))
 		return;
@@ -685,7 +685,7 @@ static void __init pte_protnone_tests(unsigned long pfn, pgprot_t prot)
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_protnone_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -696,25 +696,25 @@ static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD protnone\n");
-	pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
+	pmd = pmd_mkhuge(pfn_pmd(args->fixed_pmd_pfn, args->page_prot_none));
 	WARN_ON(!pmd_protnone(pmd));
 	WARN_ON(!pmd_present(pmd));
 }
 #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
-static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_protnone_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #ifdef CONFIG_ARCH_HAS_PTE_DEVMAP
-static void __init pte_devmap_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_devmap_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot);
 
 	pr_debug("Validating PTE devmap\n");
 	WARN_ON(!pte_devmap(pte_mkdevmap(pte)));
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_devmap_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -722,12 +722,12 @@ static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD devmap\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 	WARN_ON(!pmd_devmap(pmd_mkdevmap(pmd)));
 }
 
 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
-static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot)
+static void __init pud_devmap_tests(struct pgtable_debug_args *args)
 {
 	pud_t pud;
 
@@ -735,20 +735,20 @@ static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PUD devmap\n");
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->fixed_pud_pfn, args->page_prot);
 	WARN_ON(!pud_devmap(pud_mkdevmap(pud)));
 }
 #else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
-static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pud_devmap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
 #else  /* CONFIG_TRANSPARENT_HUGEPAGE */
-static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_devmap_tests(struct pgtable_debug_args *args) { }
+static void __init pud_devmap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 #else
-static void __init pte_devmap_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pte_devmap_tests(struct pgtable_debug_args *args) { }
+static void __init pmd_devmap_tests(struct pgtable_debug_args *args) { }
+static void __init pud_devmap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_ARCH_HAS_PTE_DEVMAP */
 
 static void __init pte_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
@@ -1154,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
 	pmd_t *pmdp, *saved_pmdp, pmd;
 	pte_t *ptep;
 	pgtable_t saved_ptep;
-	pgprot_t prot, protnone;
+	pgprot_t prot;
 	phys_addr_t paddr;
 	unsigned long vaddr, pte_aligned, pmd_aligned;
 	unsigned long pud_aligned;
@@ -1174,12 +1174,6 @@ static int __init debug_vm_pgtable(void)
 		return 1;
 	}
 
-	/*
-	 * __P000 (or even __S000) will help create page table entries with
-	 * PROT_NONE permission as required for pxx_protnone_tests().
-	 */
-	protnone = __P000;
-
 	vma = vm_area_alloc(mm);
 	if (!vma) {
 		pr_err("vma allocation failed\n");
@@ -1255,13 +1249,13 @@ static int __init debug_vm_pgtable(void)
 	pte_savedwrite_tests(&args);
 	pmd_savedwrite_tests(&args);
 
-	pte_special_tests(pte_aligned, prot);
-	pte_protnone_tests(pte_aligned, protnone);
-	pmd_protnone_tests(pmd_aligned, protnone);
+	pte_special_tests(&args);
+	pte_protnone_tests(&args);
+	pmd_protnone_tests(&args);
 
-	pte_devmap_tests(pte_aligned, prot);
-	pmd_devmap_tests(pmd_aligned, prot);
-	pud_devmap_tests(pud_aligned, prot);
+	pte_devmap_tests(&args);
+	pmd_devmap_tests(&args);
+	pud_devmap_tests(&args);
 
 	pte_soft_dirty_tests(pte_aligned, prot);
 	pmd_soft_dirty_tests(pmd_aligned, prot);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 05/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in soft_dirty and swap tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (3 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 04/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and devmap tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 06/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and thp tests Gavin Shan
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in the soft_dirty and swap test
functions.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 48 +++++++++++++++++++++----------------------
 1 file changed, 23 insertions(+), 25 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 1ae204831484..a20ed77bf05f 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -751,9 +751,9 @@ static void __init pmd_devmap_tests(struct pgtable_debug_args *args) { }
 static void __init pud_devmap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_ARCH_HAS_PTE_DEVMAP */
 
-static void __init pte_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_soft_dirty_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot);
 
 	if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
 		return;
@@ -763,9 +763,9 @@ static void __init pte_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
 	WARN_ON(pte_soft_dirty(pte_clear_soft_dirty(pte)));
 }
 
-static void __init pte_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_swap_soft_dirty_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot);
 
 	if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY))
 		return;
@@ -776,7 +776,7 @@ static void __init pte_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_soft_dirty_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -787,12 +787,12 @@ static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD soft dirty\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 	WARN_ON(!pmd_soft_dirty(pmd_mksoft_dirty(pmd)));
 	WARN_ON(pmd_soft_dirty(pmd_clear_soft_dirty(pmd)));
 }
 
-static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_swap_soft_dirty_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -804,31 +804,29 @@ static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD swap soft dirty\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 	WARN_ON(!pmd_swp_soft_dirty(pmd_swp_mksoft_dirty(pmd)));
 	WARN_ON(pmd_swp_soft_dirty(pmd_swp_clear_soft_dirty(pmd)));
 }
 #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
-static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot)
-{
-}
+static void __init pmd_soft_dirty_tests(struct pgtable_debug_args *args) { }
+static void __init pmd_swap_soft_dirty_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-static void __init pte_swap_tests(unsigned long pfn, pgprot_t prot)
+static void __init pte_swap_tests(struct pgtable_debug_args *args)
 {
 	swp_entry_t swp;
 	pte_t pte;
 
 	pr_debug("Validating PTE swap\n");
-	pte = pfn_pte(pfn, prot);
+	pte = pfn_pte(args->fixed_pte_pfn, args->page_prot);
 	swp = __pte_to_swp_entry(pte);
 	pte = __swp_entry_to_pte(swp);
-	WARN_ON(pfn != pte_pfn(pte));
+	WARN_ON(args->fixed_pte_pfn != pte_pfn(pte));
 }
 
 #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
-static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_swap_tests(struct pgtable_debug_args *args)
 {
 	swp_entry_t swp;
 	pmd_t pmd;
@@ -837,13 +835,13 @@ static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PMD swap\n");
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 	swp = __pmd_to_swp_entry(pmd);
 	pmd = __swp_entry_to_pmd(swp);
-	WARN_ON(pfn != pmd_pfn(pmd));
+	WARN_ON(args->fixed_pmd_pfn != pmd_pfn(pmd));
 }
 #else  /* !CONFIG_ARCH_ENABLE_THP_MIGRATION */
-static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_swap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_ARCH_ENABLE_THP_MIGRATION */
 
 static void __init swap_migration_tests(void)
@@ -1257,13 +1255,13 @@ static int __init debug_vm_pgtable(void)
 	pmd_devmap_tests(&args);
 	pud_devmap_tests(&args);
 
-	pte_soft_dirty_tests(pte_aligned, prot);
-	pmd_soft_dirty_tests(pmd_aligned, prot);
-	pte_swap_soft_dirty_tests(pte_aligned, prot);
-	pmd_swap_soft_dirty_tests(pmd_aligned, prot);
+	pte_soft_dirty_tests(&args);
+	pmd_soft_dirty_tests(&args);
+	pte_swap_soft_dirty_tests(&args);
+	pmd_swap_soft_dirty_tests(&args);
 
-	pte_swap_tests(pte_aligned, prot);
-	pmd_swap_tests(pmd_aligned, prot);
+	pte_swap_tests(&args);
+	pmd_swap_tests(&args);
 
 	swap_migration_tests();
 
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 06/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and thp tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (4 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 05/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in soft_dirty and swap tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests Gavin Shan
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in the migration and thp test
functions. It's notable that the pre-allocated page is used in
swap_migration_tests() as set_pte_at() is used there.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index a20ed77bf05f..d32e55a95c55 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -844,7 +844,7 @@ static void __init pmd_swap_tests(struct pgtable_debug_args *args)
 static void __init pmd_swap_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_ARCH_ENABLE_THP_MIGRATION */
 
-static void __init swap_migration_tests(void)
+static void __init swap_migration_tests(struct pgtable_debug_args *args)
 {
 	struct page *page;
 	swp_entry_t swp;
@@ -860,9 +860,10 @@ static void __init swap_migration_tests(void)
 	 * problematic. Lets allocate a dedicated page explicitly for this
 	 * purpose that will be freed subsequently.
 	 */
-	page = alloc_page(GFP_KERNEL);
+	page = (args->pte_pfn != ULONG_MAX) ?
+	       pfn_to_page(args->pte_pfn) : NULL;
 	if (!page) {
-		pr_err("page allocation failed\n");
+		pr_err("no page available\n");
 		return;
 	}
 
@@ -883,7 +884,6 @@ static void __init swap_migration_tests(void)
 	WARN_ON(!is_migration_entry(swp));
 	WARN_ON(is_writable_migration_entry(swp));
 	__ClearPageLocked(page);
-	__free_page(page);
 }
 
 #ifdef CONFIG_HUGETLB_PAGE
@@ -915,7 +915,7 @@ static void __init hugetlb_basic_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_HUGETLB_PAGE */
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot)
+static void __init pmd_thp_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -934,7 +934,7 @@ static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot)
 	 * needs to return true. pmd_present() should be true whenever
 	 * pmd_trans_huge() returns true.
 	 */
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot);
 	WARN_ON(!pmd_trans_huge(pmd_mkhuge(pmd)));
 
 #ifndef __HAVE_ARCH_PMDP_INVALIDATE
@@ -944,7 +944,7 @@ static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot)
 }
 
 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
-static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot)
+static void __init pud_thp_tests(struct pgtable_debug_args *args)
 {
 	pud_t pud;
 
@@ -952,7 +952,7 @@ static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot)
 		return;
 
 	pr_debug("Validating PUD based THP\n");
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->fixed_pud_pfn, args->page_prot);
 	WARN_ON(!pud_trans_huge(pud_mkhuge(pud)));
 
 	/*
@@ -964,11 +964,11 @@ static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot)
 	 */
 }
 #else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
-static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pud_thp_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
 #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
-static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot) { }
-static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_thp_tests(struct pgtable_debug_args *args) { }
+static void __init pud_thp_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 static unsigned long __init get_random_vaddr(void)
@@ -1263,10 +1263,10 @@ static int __init debug_vm_pgtable(void)
 	pte_swap_tests(&args);
 	pmd_swap_tests(&args);
 
-	swap_migration_tests();
+	swap_migration_tests(&args);
 
-	pmd_thp_tests(pmd_aligned, prot);
-	pud_thp_tests(pud_aligned, prot);
+	pmd_thp_tests(&args);
+	pud_thp_tests(&args);
 
 	/*
 	 * Page table modifying tests. They need to hold
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (5 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 06/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and thp tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-22  5:56   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD " Gavin Shan
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in PTE modifying tests. The allocated
page is used as set_pte_at() is used there. The tests are skipped if
the allocated page doesn't exist. Besides, the unused variable @ptep
and @pte_aligned in debug_vm_pgtable() are dropped.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 75 ++++++++++++++++++++++---------------------
 1 file changed, 39 insertions(+), 36 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index d32e55a95c55..eb6dda88e0d9 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -116,10 +116,7 @@ static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
 	WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte))));
 }
 
-static void __init pte_advanced_tests(struct mm_struct *mm,
-				      struct vm_area_struct *vma, pte_t *ptep,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot)
+static void __init pte_advanced_tests(struct pgtable_debug_args *args)
 {
 	pte_t pte;
 
@@ -130,33 +127,38 @@ static void __init pte_advanced_tests(struct mm_struct *mm,
 	 */
 
 	pr_debug("Validating PTE advanced\n");
-	pte = pfn_pte(pfn, prot);
-	set_pte_at(mm, vaddr, ptep, pte);
-	ptep_set_wrprotect(mm, vaddr, ptep);
-	pte = ptep_get(ptep);
+	if (args->pte_pfn == ULONG_MAX) {
+		pr_debug("%s: Skipped\n", __func__);
+		return;
+	}
+
+	pte = pfn_pte(args->pte_pfn, args->page_prot);
+	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
+	ptep_set_wrprotect(args->mm, args->vaddr, args->ptep);
+	pte = ptep_get(args->ptep);
 	WARN_ON(pte_write(pte));
-	ptep_get_and_clear(mm, vaddr, ptep);
-	pte = ptep_get(ptep);
+	ptep_get_and_clear(args->mm, args->vaddr, args->ptep);
+	pte = ptep_get(args->ptep);
 	WARN_ON(!pte_none(pte));
 
-	pte = pfn_pte(pfn, prot);
+	pte = pfn_pte(args->pte_pfn, args->page_prot);
 	pte = pte_wrprotect(pte);
 	pte = pte_mkclean(pte);
-	set_pte_at(mm, vaddr, ptep, pte);
+	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
 	pte = pte_mkwrite(pte);
 	pte = pte_mkdirty(pte);
-	ptep_set_access_flags(vma, vaddr, ptep, pte, 1);
-	pte = ptep_get(ptep);
+	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(mm, vaddr, ptep, 1);
-	pte = ptep_get(ptep);
+	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(pfn, prot);
+	pte = pfn_pte(args->pte_pfn, args->page_prot);
 	pte = pte_mkyoung(pte);
-	set_pte_at(mm, vaddr, ptep, pte);
-	ptep_test_and_clear_young(vma, vaddr, ptep);
-	pte = ptep_get(ptep);
+	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
+	ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep);
+	pte = ptep_get(args->ptep);
 	WARN_ON(pte_young(pte));
 }
 
@@ -617,20 +619,24 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
 }
 #endif /* PAGETABLE_P4D_FOLDED */
 
-static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep,
-				   unsigned long pfn, unsigned long vaddr,
-				   pgprot_t prot)
+static void __init pte_clear_tests(struct pgtable_debug_args *args)
 {
-	pte_t pte = pfn_pte(pfn, prot);
+	pte_t pte;
 
 	pr_debug("Validating PTE clear\n");
+	if (args->pte_pfn == ULONG_MAX) {
+		pr_debug("%s: Skipped\n", __func__);
+		return;
+	}
+
+	pte = pfn_pte(args->pte_pfn, args->page_prot);
 #ifndef CONFIG_RISCV
 	pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
 #endif
-	set_pte_at(mm, vaddr, ptep, pte);
+	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
 	barrier();
-	pte_clear(mm, vaddr, ptep);
-	pte = ptep_get(ptep);
+	pte_clear(args->mm, args->vaddr, args->ptep);
+	pte = ptep_get(args->ptep);
 	WARN_ON(!pte_none(pte));
 }
 
@@ -1150,11 +1156,10 @@ static int __init debug_vm_pgtable(void)
 	p4d_t *p4dp, *saved_p4dp;
 	pud_t *pudp, *saved_pudp;
 	pmd_t *pmdp, *saved_pmdp, pmd;
-	pte_t *ptep;
 	pgtable_t saved_ptep;
 	pgprot_t prot;
 	phys_addr_t paddr;
-	unsigned long vaddr, pte_aligned, pmd_aligned;
+	unsigned long vaddr, pmd_aligned;
 	unsigned long pud_aligned;
 	spinlock_t *ptl = NULL;
 	int idx, ret;
@@ -1189,10 +1194,8 @@ static int __init debug_vm_pgtable(void)
 	 */
 	paddr = __pa_symbol(&start_kernel);
 
-	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;
 	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
 	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
-	WARN_ON(!pfn_valid(pte_aligned));
 
 	pgdp = pgd_offset(mm, vaddr);
 	p4dp = p4d_alloc(mm, pgdp, vaddr);
@@ -1272,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
 	 * Page table modifying tests. They need to hold
 	 * proper page table lock.
 	 */
-
-	ptep = pte_offset_map_lock(mm, pmdp, vaddr, &ptl);
-	pte_clear_tests(mm, ptep, pte_aligned, vaddr, prot);
-	pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
-	pte_unmap_unlock(ptep, ptl);
+	ptl = pte_lockptr(args.mm, args.pmdp);
+	spin_lock(ptl);
+	pte_clear_tests(&args);
+	pte_advanced_tests(&args);
+	spin_unlock(ptl);
 
 	ptl = pmd_lock(mm, pmdp);
 	pmd_clear_tests(mm, pmdp);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (6 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-22  5:45   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD " Gavin Shan
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in PMD modifying tests. The allocated
huge page is used when set_pmd_at() is used. The corresponding tests
are skipped if the huge page doesn't exist. Besides, the unused variable
@pmd_aligned in debug_vm_pgtable() is dropped.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
 1 file changed, 48 insertions(+), 54 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index eb6dda88e0d9..cec3cbf99a6b 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
 	WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
 }
 
-static void __init pmd_advanced_tests(struct mm_struct *mm,
-				      struct vm_area_struct *vma, pmd_t *pmdp,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot, pgtable_t pgtable)
+static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
+	unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
 
 	if (!has_transparent_hugepage())
 		return;
 
 	pr_debug("Validating PMD advanced\n");
-	/* Align the address wrt HPAGE_PMD_SIZE */
-	vaddr &= HPAGE_PMD_MASK;
+	if (args->pmd_pfn == ULONG_MAX) {
+		pr_debug("%s: Skipped\n", __func__);
+		return;
+	}
 
-	pgtable_trans_huge_deposit(mm, pmdp, pgtable);
+	pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
 
-	pmd = pfn_pmd(pfn, prot);
-	set_pmd_at(mm, vaddr, pmdp, pmd);
-	pmdp_set_wrprotect(mm, vaddr, pmdp);
-	pmd = READ_ONCE(*pmdp);
+	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
+	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
+	pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(pmd_write(pmd));
-	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
-	pmd = READ_ONCE(*pmdp);
+	pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(!pmd_none(pmd));
 
-	pmd = pfn_pmd(pfn, prot);
+	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
 	pmd = pmd_wrprotect(pmd);
 	pmd = pmd_mkclean(pmd);
-	set_pmd_at(mm, vaddr, pmdp, pmd);
+	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
 	pmd = pmd_mkwrite(pmd);
 	pmd = pmd_mkdirty(pmd);
-	pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
-	pmd = READ_ONCE(*pmdp);
+	pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
-	pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
-	pmd = READ_ONCE(*pmdp);
+	pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(!pmd_none(pmd));
 
-	pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
+	pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
 	pmd = pmd_mkyoung(pmd);
-	set_pmd_at(mm, vaddr, pmdp, pmd);
-	pmdp_test_and_clear_young(vma, vaddr, pmdp);
-	pmd = READ_ONCE(*pmdp);
+	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
+	pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(pmd_young(pmd));
 
 	/*  Clear the pte entries  */
-	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
-	pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
+	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)
@@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
 #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 mm_struct *mm,
-				      struct vm_area_struct *vma, pmd_t *pmdp,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot, pgtable_t pgtable)
-{
-}
+static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
 static void __init pud_advanced_tests(struct mm_struct *mm,
 				      struct vm_area_struct *vma, pud_t *pudp,
 				      unsigned long pfn, unsigned long vaddr,
@@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
-static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
+static void __init pmd_huge_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
-	if (!arch_vmap_pmd_supported(prot))
+	if (!arch_vmap_pmd_supported(args->page_prot))
 		return;
 
 	pr_debug("Validating PMD huge\n");
@@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
 	 * X86 defined pmd_set_huge() verifies that the given
 	 * PMD is not a populated non-leaf entry.
 	 */
-	WRITE_ONCE(*pmdp, __pmd(0));
-	WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
-	WARN_ON(!pmd_clear_huge(pmdp));
-	pmd = READ_ONCE(*pmdp);
+	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 = READ_ONCE(*(args->pmdp));
 	WARN_ON(!pmd_none(pmd));
 }
 
@@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
 	WARN_ON(!pud_none(pud));
 }
 #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
-static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
+static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
 static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
 #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
 
@@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
 	WARN_ON(!pte_none(pte));
 }
 
-static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
+static void __init pmd_clear_tests(struct pgtable_debug_args *args)
 {
-	pmd_t pmd = READ_ONCE(*pmdp);
+	pmd_t pmd = READ_ONCE(*(args->pmdp));
 
 	pr_debug("Validating PMD clear\n");
 	pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
-	WRITE_ONCE(*pmdp, pmd);
-	pmd_clear(pmdp);
-	pmd = READ_ONCE(*pmdp);
+	WRITE_ONCE(*(args->pmdp), pmd);
+	pmd_clear(args->pmdp);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(!pmd_none(pmd));
 }
 
-static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
-				      pgtable_t pgtable)
+static void __init pmd_populate_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
@@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
 	 * This entry points to next level page table page.
 	 * Hence this must not qualify as pmd_bad().
 	 */
-	pmd_populate(mm, pmdp, pgtable);
-	pmd = READ_ONCE(*pmdp);
+	pmd_populate(args->mm, args->pmdp, args->start_ptep);
+	pmd = READ_ONCE(*(args->pmdp));
 	WARN_ON(pmd_bad(pmd));
 }
 
@@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
 	pgtable_t saved_ptep;
 	pgprot_t prot;
 	phys_addr_t paddr;
-	unsigned long vaddr, pmd_aligned;
+	unsigned long vaddr;
 	unsigned long pud_aligned;
 	spinlock_t *ptl = NULL;
 	int idx, ret;
@@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
 	 */
 	paddr = __pa_symbol(&start_kernel);
 
-	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
 	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
 
 	pgdp = pgd_offset(mm, vaddr);
@@ -1281,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
 	pte_advanced_tests(&args);
 	spin_unlock(ptl);
 
-	ptl = pmd_lock(mm, pmdp);
-	pmd_clear_tests(mm, pmdp);
-	pmd_advanced_tests(mm, vma, pmdp, pmd_aligned, vaddr, prot, saved_ptep);
-	pmd_huge_tests(pmdp, pmd_aligned, prot);
-	pmd_populate_tests(mm, pmdp, saved_ptep);
+	ptl = pmd_lock(args.mm, args.pmdp);
+	pmd_clear_tests(&args);
+	pmd_advanced_tests(&args);
+	pmd_huge_tests(&args);
+	pmd_populate_tests(&args);
 	spin_unlock(ptl);
 
 	ptl = pud_lock(mm, pudp);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (7 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD " Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-22  5:39   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D " Gavin Shan
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in PUD modifying tests. The allocated
huge page is used when set_pud_at() is used. The corresponding tests
are skipped if the huge page doesn't exist. Besides, the following unused
variables in debug_vm_pgtable() are dropped: @prot, @paddr, @pud_aligned.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 130 ++++++++++++++++--------------------------
 1 file changed, 50 insertions(+), 80 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index cec3cbf99a6b..57b7ead0708b 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -338,55 +338,55 @@ static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
 	WARN_ON(!pud_bad(pud_mkhuge(pud)));
 }
 
-static void __init pud_advanced_tests(struct mm_struct *mm,
-				      struct vm_area_struct *vma, pud_t *pudp,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot)
+static void __init pud_advanced_tests(struct pgtable_debug_args *args)
 {
+	unsigned long vaddr = (args->vaddr & HPAGE_PUD_MASK);
 	pud_t pud;
 
 	if (!has_transparent_hugepage())
 		return;
 
 	pr_debug("Validating PUD advanced\n");
-	/* Align the address wrt HPAGE_PUD_SIZE */
-	vaddr &= HPAGE_PUD_MASK;
+	if (args->pud_pfn == ULONG_MAX) {
+		pr_debug("%s: Skipped\n", __func__);
+		return;
+	}
 
-	pud = pfn_pud(pfn, prot);
-	set_pud_at(mm, vaddr, pudp, pud);
-	pudp_set_wrprotect(mm, vaddr, pudp);
-	pud = READ_ONCE(*pudp);
+	pud = pfn_pud(args->pud_pfn, args->page_prot);
+	set_pud_at(args->mm, vaddr, args->pudp, pud);
+	pudp_set_wrprotect(args->mm, vaddr, args->pudp);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(pud_write(pud));
 
 #ifndef __PAGETABLE_PMD_FOLDED
-	pudp_huge_get_and_clear(mm, vaddr, pudp);
-	pud = READ_ONCE(*pudp);
+	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(!pud_none(pud));
 #endif /* __PAGETABLE_PMD_FOLDED */
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->pud_pfn, args->page_prot);
 	pud = pud_wrprotect(pud);
 	pud = pud_mkclean(pud);
-	set_pud_at(mm, vaddr, pudp, pud);
+	set_pud_at(args->mm, vaddr, args->pudp, pud);
 	pud = pud_mkwrite(pud);
 	pud = pud_mkdirty(pud);
-	pudp_set_access_flags(vma, vaddr, pudp, pud, 1);
-	pud = READ_ONCE(*pudp);
+	pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(!(pud_write(pud) && pud_dirty(pud)));
 
 #ifndef __PAGETABLE_PMD_FOLDED
-	pudp_huge_get_and_clear_full(mm, vaddr, pudp, 1);
-	pud = READ_ONCE(*pudp);
+	pudp_huge_get_and_clear_full(args->mm, vaddr, args->pudp, 1);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(!pud_none(pud));
 #endif /* __PAGETABLE_PMD_FOLDED */
 
-	pud = pfn_pud(pfn, prot);
+	pud = pfn_pud(args->pud_pfn, args->page_prot);
 	pud = pud_mkyoung(pud);
-	set_pud_at(mm, vaddr, pudp, pud);
-	pudp_test_and_clear_young(vma, vaddr, pudp);
-	pud = READ_ONCE(*pudp);
+	set_pud_at(args->mm, vaddr, args->pudp, pud);
+	pudp_test_and_clear_young(args->vma, vaddr, args->pudp);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(pud_young(pud));
 
-	pudp_huge_get_and_clear(mm, vaddr, pudp);
+	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
 }
 
 static void __init pud_leaf_tests(struct pgtable_debug_args *args)
@@ -406,24 +406,14 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args)
 }
 #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 mm_struct *mm,
-				      struct vm_area_struct *vma, pud_t *pudp,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot)
-{
-}
+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 mm_struct *mm,
-				      struct vm_area_struct *vma, pud_t *pudp,
-				      unsigned long pfn, unsigned long vaddr,
-				      pgprot_t prot)
-{
-}
+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) { }
 static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
@@ -450,11 +440,11 @@ static void __init pmd_huge_tests(struct pgtable_debug_args *args)
 	WARN_ON(!pmd_none(pmd));
 }
 
-static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
+static void __init pud_huge_tests(struct pgtable_debug_args *args)
 {
 	pud_t pud;
 
-	if (!arch_vmap_pud_supported(prot))
+	if (!arch_vmap_pud_supported(args->page_prot))
 		return;
 
 	pr_debug("Validating PUD huge\n");
@@ -462,15 +452,16 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
 	 * X86 defined pud_set_huge() verifies that the given
 	 * PUD is not a populated non-leaf entry.
 	 */
-	WRITE_ONCE(*pudp, __pud(0));
-	WARN_ON(!pud_set_huge(pudp, __pfn_to_phys(pfn), prot));
-	WARN_ON(!pud_clear_huge(pudp));
-	pud = READ_ONCE(*pudp);
+	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 = READ_ONCE(*(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(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
+static void __init pud_huge_tests(struct pgtable_debug_args *args) { }
 #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
 
 static void __init p4d_basic_tests(void)
@@ -492,27 +483,26 @@ static void __init pgd_basic_tests(void)
 }
 
 #ifndef __PAGETABLE_PUD_FOLDED
-static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp)
+static void __init pud_clear_tests(struct pgtable_debug_args *args)
 {
-	pud_t pud = READ_ONCE(*pudp);
+	pud_t pud = READ_ONCE(*(args->pudp));
 
-	if (mm_pmd_folded(mm))
+	if (mm_pmd_folded(args->mm))
 		return;
 
 	pr_debug("Validating PUD clear\n");
 	pud = __pud(pud_val(pud) | RANDOM_ORVALUE);
-	WRITE_ONCE(*pudp, pud);
-	pud_clear(pudp);
-	pud = READ_ONCE(*pudp);
+	WRITE_ONCE(*(args->pudp), pud);
+	pud_clear(args->pudp);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(!pud_none(pud));
 }
 
-static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
-				      pmd_t *pmdp)
+static void __init pud_populate_tests(struct pgtable_debug_args *args)
 {
 	pud_t pud;
 
-	if (mm_pmd_folded(mm))
+	if (mm_pmd_folded(args->mm))
 		return;
 
 	pr_debug("Validating PUD populate\n");
@@ -520,16 +510,13 @@ static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
 	 * This entry points to next level page table page.
 	 * Hence this must not qualify as pud_bad().
 	 */
-	pud_populate(mm, pudp, pmdp);
-	pud = READ_ONCE(*pudp);
+	pud_populate(args->mm, args->pudp, args->start_pmdp);
+	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(pud_bad(pud));
 }
 #else  /* !__PAGETABLE_PUD_FOLDED */
-static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp) { }
-static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
-				      pmd_t *pmdp)
-{
-}
+static void __init pud_clear_tests(struct pgtable_debug_args *args) { }
+static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
 #endif /* PAGETABLE_PUD_FOLDED */
 
 #ifndef __PAGETABLE_P4D_FOLDED
@@ -1152,10 +1139,7 @@ static int __init debug_vm_pgtable(void)
 	pud_t *pudp, *saved_pudp;
 	pmd_t *pmdp, *saved_pmdp, pmd;
 	pgtable_t saved_ptep;
-	pgprot_t prot;
-	phys_addr_t paddr;
 	unsigned long vaddr;
-	unsigned long pud_aligned;
 	spinlock_t *ptl = NULL;
 	int idx, ret;
 
@@ -1164,7 +1148,6 @@ static int __init debug_vm_pgtable(void)
 	if (ret)
 		return ret;
 
-	prot = vm_get_page_prot(VMFLAGS);
 	vaddr = get_random_vaddr();
 	mm = mm_alloc();
 	if (!mm) {
@@ -1178,19 +1161,6 @@ static int __init debug_vm_pgtable(void)
 		return 1;
 	}
 
-	/*
-	 * PFN for mapping at PTE level is determined from a standard kernel
-	 * text symbol. But pfns for higher page table levels are derived by
-	 * masking lower bits of this real pfn. These derived pfns might not
-	 * exist on the platform but that does not really matter as pfn_pxx()
-	 * helpers will still create appropriate entries for the test. This
-	 * helps avoid large memory block allocations to be used for mapping
-	 * at higher page table levels.
-	 */
-	paddr = __pa_symbol(&start_kernel);
-
-	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
-
 	pgdp = pgd_offset(mm, vaddr);
 	p4dp = p4d_alloc(mm, pgdp, vaddr);
 	pudp = pud_alloc(mm, p4dp, vaddr);
@@ -1282,11 +1252,11 @@ static int __init debug_vm_pgtable(void)
 	pmd_populate_tests(&args);
 	spin_unlock(ptl);
 
-	ptl = pud_lock(mm, pudp);
-	pud_clear_tests(mm, pudp);
-	pud_advanced_tests(mm, vma, pudp, pud_aligned, vaddr, prot);
-	pud_huge_tests(pudp, pud_aligned, prot);
-	pud_populate_tests(mm, pudp, saved_pmdp);
+	ptl = pud_lock(args.mm, args.pudp);
+	pud_clear_tests(&args);
+	pud_advanced_tests(&args);
+	pud_huge_tests(&args);
+	pud_populate_tests(&args);
 	spin_unlock(ptl);
 
 	spin_lock(&mm->page_table_lock);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (8 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD " Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-22  5:09   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code Gavin Shan
  2021-07-19 13:06 ` [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag Gavin Shan
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

This uses struct pgtable_debug_args in PGD/P4D modifying tests. No
allocated huge page is used in these tests. Besides, the unused
variable @saved_p4dp and @saved_pudp are dropped.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 86 +++++++++++++++++++------------------------
 1 file changed, 38 insertions(+), 48 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 57b7ead0708b..5ebacc940b68 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -520,27 +520,26 @@ static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
 #endif /* PAGETABLE_PUD_FOLDED */
 
 #ifndef __PAGETABLE_P4D_FOLDED
-static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp)
+static void __init p4d_clear_tests(struct pgtable_debug_args *args)
 {
-	p4d_t p4d = READ_ONCE(*p4dp);
+	p4d_t p4d = READ_ONCE(*(args->p4dp));
 
-	if (mm_pud_folded(mm))
+	if (mm_pud_folded(args->mm))
 		return;
 
 	pr_debug("Validating P4D clear\n");
 	p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE);
-	WRITE_ONCE(*p4dp, p4d);
-	p4d_clear(p4dp);
-	p4d = READ_ONCE(*p4dp);
+	WRITE_ONCE(*(args->p4dp), p4d);
+	p4d_clear(args->p4dp);
+	p4d = READ_ONCE(*(args->p4dp));
 	WARN_ON(!p4d_none(p4d));
 }
 
-static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
-				      pud_t *pudp)
+static void __init p4d_populate_tests(struct pgtable_debug_args *args)
 {
 	p4d_t p4d;
 
-	if (mm_pud_folded(mm))
+	if (mm_pud_folded(args->mm))
 		return;
 
 	pr_debug("Validating P4D populate\n");
@@ -548,34 +547,33 @@ static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
 	 * This entry points to next level page table page.
 	 * Hence this must not qualify as p4d_bad().
 	 */
-	pud_clear(pudp);
-	p4d_clear(p4dp);
-	p4d_populate(mm, p4dp, pudp);
-	p4d = READ_ONCE(*p4dp);
+	pud_clear(args->pudp);
+	p4d_clear(args->p4dp);
+	p4d_populate(args->mm, args->p4dp, args->start_pudp);
+	p4d = READ_ONCE(*(args->p4dp));
 	WARN_ON(p4d_bad(p4d));
 }
 
-static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp)
+static void __init pgd_clear_tests(struct pgtable_debug_args *args)
 {
-	pgd_t pgd = READ_ONCE(*pgdp);
+	pgd_t pgd = READ_ONCE(*(args->pgdp));
 
-	if (mm_p4d_folded(mm))
+	if (mm_p4d_folded(args->mm))
 		return;
 
 	pr_debug("Validating PGD clear\n");
 	pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE);
-	WRITE_ONCE(*pgdp, pgd);
-	pgd_clear(pgdp);
-	pgd = READ_ONCE(*pgdp);
+	WRITE_ONCE(*(args->pgdp), pgd);
+	pgd_clear(args->pgdp);
+	pgd = READ_ONCE(*(args->pgdp));
 	WARN_ON(!pgd_none(pgd));
 }
 
-static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
-				      p4d_t *p4dp)
+static void __init pgd_populate_tests(struct pgtable_debug_args *args)
 {
 	pgd_t pgd;
 
-	if (mm_p4d_folded(mm))
+	if (mm_p4d_folded(args->mm))
 		return;
 
 	pr_debug("Validating PGD populate\n");
@@ -583,23 +581,17 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
 	 * This entry points to next level page table page.
 	 * Hence this must not qualify as pgd_bad().
 	 */
-	p4d_clear(p4dp);
-	pgd_clear(pgdp);
-	pgd_populate(mm, pgdp, p4dp);
-	pgd = READ_ONCE(*pgdp);
+	p4d_clear(args->p4dp);
+	pgd_clear(args->pgdp);
+	pgd_populate(args->mm, args->pgdp, args->start_p4dp);
+	pgd = READ_ONCE(*(args->pgdp));
 	WARN_ON(pgd_bad(pgd));
 }
 #else  /* !__PAGETABLE_P4D_FOLDED */
-static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp) { }
-static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) { }
-static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
-				      pud_t *pudp)
-{
-}
-static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
-				      p4d_t *p4dp)
-{
-}
+static void __init p4d_clear_tests(struct pgtable_debug_args *args) { }
+static void __init pgd_clear_tests(struct pgtable_debug_args *args) { }
+static void __init p4d_populate_tests(struct pgtable_debug_args *args) { }
+static void __init pgd_populate_tests(struct pgtable_debug_args *args) { }
 #endif /* PAGETABLE_P4D_FOLDED */
 
 static void __init pte_clear_tests(struct pgtable_debug_args *args)
@@ -1135,8 +1127,8 @@ static int __init debug_vm_pgtable(void)
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
 	pgd_t *pgdp;
-	p4d_t *p4dp, *saved_p4dp;
-	pud_t *pudp, *saved_pudp;
+	p4d_t *p4dp;
+	pud_t *pudp;
 	pmd_t *pmdp, *saved_pmdp, pmd;
 	pgtable_t saved_ptep;
 	unsigned long vaddr;
@@ -1180,8 +1172,6 @@ static int __init debug_vm_pgtable(void)
 	 * page table pages.
 	 */
 	pmd = READ_ONCE(*pmdp);
-	saved_p4dp = p4d_offset(pgdp, 0UL);
-	saved_pudp = pud_offset(p4dp, 0UL);
 	saved_pmdp = pmd_offset(pudp, 0UL);
 	saved_ptep = pmd_pgtable(pmd);
 
@@ -1259,15 +1249,15 @@ static int __init debug_vm_pgtable(void)
 	pud_populate_tests(&args);
 	spin_unlock(ptl);
 
-	spin_lock(&mm->page_table_lock);
-	p4d_clear_tests(mm, p4dp);
-	pgd_clear_tests(mm, pgdp);
-	p4d_populate_tests(mm, p4dp, saved_pudp);
-	pgd_populate_tests(mm, pgdp, saved_p4dp);
-	spin_unlock(&mm->page_table_lock);
+	spin_lock(&(args.mm->page_table_lock));
+	p4d_clear_tests(&args);
+	pgd_clear_tests(&args);
+	p4d_populate_tests(&args);
+	pgd_populate_tests(&args);
+	spin_unlock(&(args.mm->page_table_lock));
 
-	p4d_free(mm, saved_p4dp);
-	pud_free(mm, saved_pudp);
+	p4d_free(mm, p4d_offset(pgdp, 0UL));
+	pud_free(mm, pud_offset(p4dp, 0UL));
 	pmd_free(mm, saved_pmdp);
 	pte_free(mm, saved_ptep);
 
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (9 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D " Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-22  4:51   ` Anshuman Khandual
  2021-07-19 13:06 ` [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag Gavin Shan
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

The variables used by old implementation isn't needed as we switched
to "struct pgtable_debug_args". Lets remove them and related code in
debug_vm_pgtable().

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 54 -------------------------------------------
 1 file changed, 54 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 5ebacc940b68..4f7bf1c9724a 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -1124,14 +1124,6 @@ static int __init init_args(struct pgtable_debug_args *args)
 static int __init debug_vm_pgtable(void)
 {
 	struct pgtable_debug_args args;
-	struct vm_area_struct *vma;
-	struct mm_struct *mm;
-	pgd_t *pgdp;
-	p4d_t *p4dp;
-	pud_t *pudp;
-	pmd_t *pmdp, *saved_pmdp, pmd;
-	pgtable_t saved_ptep;
-	unsigned long vaddr;
 	spinlock_t *ptl = NULL;
 	int idx, ret;
 
@@ -1140,41 +1132,6 @@ static int __init debug_vm_pgtable(void)
 	if (ret)
 		return ret;
 
-	vaddr = get_random_vaddr();
-	mm = mm_alloc();
-	if (!mm) {
-		pr_err("mm_struct allocation failed\n");
-		return 1;
-	}
-
-	vma = vm_area_alloc(mm);
-	if (!vma) {
-		pr_err("vma allocation failed\n");
-		return 1;
-	}
-
-	pgdp = pgd_offset(mm, vaddr);
-	p4dp = p4d_alloc(mm, pgdp, vaddr);
-	pudp = pud_alloc(mm, p4dp, vaddr);
-	pmdp = pmd_alloc(mm, pudp, vaddr);
-	/*
-	 * Allocate pgtable_t
-	 */
-	if (pte_alloc(mm, pmdp)) {
-		pr_err("pgtable allocation failed\n");
-		return 1;
-	}
-
-	/*
-	 * Save all the page table page addresses as the page table
-	 * entries will be used for testing with random or garbage
-	 * values. These saved addresses will be used for freeing
-	 * page table pages.
-	 */
-	pmd = READ_ONCE(*pmdp);
-	saved_pmdp = pmd_offset(pudp, 0UL);
-	saved_ptep = pmd_pgtable(pmd);
-
 	/*
 	 * Iterate over the protection_map[] to make sure that all
 	 * the basic page table transformation validations just hold
@@ -1256,17 +1213,6 @@ static int __init debug_vm_pgtable(void)
 	pgd_populate_tests(&args);
 	spin_unlock(&(args.mm->page_table_lock));
 
-	p4d_free(mm, p4d_offset(pgdp, 0UL));
-	pud_free(mm, pud_offset(p4dp, 0UL));
-	pmd_free(mm, saved_pmdp);
-	pte_free(mm, saved_ptep);
-
-	vm_area_free(vma);
-	mm_dec_nr_puds(mm);
-	mm_dec_nr_pmds(mm);
-	mm_dec_nr_ptes(mm);
-	mmdrop(mm);
-
 	destroy_args(&args);
 	return 0;
 }
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag
  2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
                   ` (10 preceding siblings ...)
  2021-07-19 13:06 ` [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code Gavin Shan
@ 2021-07-19 13:06 ` Gavin Shan
  2021-07-21 10:18   ` Anshuman Khandual
  11 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-19 13:06 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, anshuman.khandual, catalin.marinas, will, akpm,
	chuhu, shan.gavin

In page table entry modifying tests, set_xxx_at() are used to populate
the page table entries. On ARM64, PG_arch_1 is set to the target page
flag if execution permission is given. The page flag is kept when the
page is free'd to buddy's free area list. However, it will trigger page
checking failure when it's pulled from the buddy's free area list, as
the following warning messages indicate.

   BUG: Bad page state in process memhog  pfn:08000
   page:0000000015c0a628 refcount:0 mapcount:0 \
        mapping:0000000000000000 index:0x1 pfn:0x8000
   flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff)
   raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000
   raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
   page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set

This fixes the issue by clearing PG_arch_1 through flush_dcache_page()
after set_xxx_at() is called.

Signed-off-by: Gavin Shan <gshan@redhat.com>
---
 mm/debug_vm_pgtable.c | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 4f7bf1c9724a..de9def6f7ce1 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -29,6 +29,8 @@
 #include <linux/start_kernel.h>
 #include <linux/sched/mm.h>
 #include <linux/io.h>
+
+#include <asm/cacheflush.h>
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
 
@@ -118,6 +120,7 @@ static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
 
 static void __init pte_advanced_tests(struct pgtable_debug_args *args)
 {
+	struct page *page;
 	pte_t pte;
 
 	/*
@@ -127,13 +130,16 @@ static void __init pte_advanced_tests(struct pgtable_debug_args *args)
 	 */
 
 	pr_debug("Validating PTE advanced\n");
-	if (args->pte_pfn == ULONG_MAX) {
+	page = (args->pte_pfn != ULONG_MAX) ?
+	       pfn_to_page(args->pte_pfn) : NULL;
+	if (!page) {
 		pr_debug("%s: Skipped\n", __func__);
 		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));
@@ -145,6 +151,7 @@ static void __init pte_advanced_tests(struct pgtable_debug_args *args)
 	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);
 	pte = pte_mkdirty(pte);
 	ptep_set_access_flags(args->vma, args->vaddr, args->ptep, pte, 1);
@@ -157,6 +164,7 @@ static void __init pte_advanced_tests(struct pgtable_debug_args *args)
 	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));
@@ -215,6 +223,7 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
 
 static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 {
+	struct page *page;
 	pmd_t pmd;
 	unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
 
@@ -222,7 +231,9 @@ static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 		return;
 
 	pr_debug("Validating PMD advanced\n");
-	if (args->pmd_pfn == ULONG_MAX) {
+	page = (args->pmd_pfn != ULONG_MAX) ?
+	       pfn_to_page(args->pmd_pfn) : NULL;
+	if (!page) {
 		pr_debug("%s: Skipped\n", __func__);
 		return;
 	}
@@ -231,6 +242,7 @@ static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 
 	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 = READ_ONCE(*(args->pmdp));
 	WARN_ON(pmd_write(pmd));
@@ -242,6 +254,7 @@ static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 	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);
 	pmd = pmd_mkdirty(pmd);
 	pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
@@ -254,6 +267,7 @@ static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 	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 = READ_ONCE(*(args->pmdp));
 	WARN_ON(pmd_young(pmd));
@@ -340,6 +354,7 @@ static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
 
 static void __init pud_advanced_tests(struct pgtable_debug_args *args)
 {
+	struct page *page;
 	unsigned long vaddr = (args->vaddr & HPAGE_PUD_MASK);
 	pud_t pud;
 
@@ -347,13 +362,16 @@ static void __init pud_advanced_tests(struct pgtable_debug_args *args)
 		return;
 
 	pr_debug("Validating PUD advanced\n");
-	if (args->pud_pfn == ULONG_MAX) {
+	page = (args->pud_pfn != ULONG_MAX) ?
+	       pfn_to_page(args->pud_pfn) : NULL;
+	if (!page) {
 		pr_debug("%s: Skipped\n", __func__);
 		return;
 	}
 
 	pud = pfn_pud(args->pud_pfn, args->page_prot);
 	set_pud_at(args->mm, vaddr, args->pudp, pud);
+	flush_dcache_page(page);
 	pudp_set_wrprotect(args->mm, vaddr, args->pudp);
 	pud = READ_ONCE(*(args->pudp));
 	WARN_ON(pud_write(pud));
@@ -367,6 +385,7 @@ static void __init pud_advanced_tests(struct pgtable_debug_args *args)
 	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);
@@ -382,6 +401,7 @@ static void __init pud_advanced_tests(struct pgtable_debug_args *args)
 	pud = pfn_pud(args->pud_pfn, args->page_prot);
 	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 = READ_ONCE(*(args->pudp));
 	WARN_ON(pud_young(pud));
@@ -596,10 +616,13 @@ static void __init pgd_populate_tests(struct pgtable_debug_args *args) { }
 
 static void __init pte_clear_tests(struct pgtable_debug_args *args)
 {
+	struct page *page;
 	pte_t pte;
 
 	pr_debug("Validating PTE clear\n");
-	if (args->pte_pfn == ULONG_MAX) {
+	page = (args->pte_pfn != ULONG_MAX) ?
+	       pfn_to_page(args->pte_pfn) : NULL;
+	if (!page) {
 		pr_debug("%s: Skipped\n", __func__);
 		return;
 	}
@@ -609,6 +632,7 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
 	pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
 #endif
 	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
+	flush_dcache_page(page);
 	barrier();
 	pte_clear(args->mm, args->vaddr, args->ptep);
 	pte = ptep_get(args->ptep);
-- 
2.23.0


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-19 13:06 ` [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args Gavin Shan
@ 2021-07-21  5:44   ` Anshuman Khandual
  2021-07-21 10:20     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-21  5:44 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> In debug_vm_pgtable(), there are many local variables introduced to
> track the needed information and they are passed to the functions for
> various test cases. It'd better to introduce a struct as place holder
> for these information. With it, what the functions for various test
> cases need is the struct, to simplify the code. It also makes code
> easier to be maintained.
> 
> Besides, set_xxx_at() could access the data on the corresponding pages
> in the page table modifying tests. So the accessed pages in the tests
> should have been allocated from buddy. Otherwise, we're accessing pages
> that aren't owned by us. This causes issues like page flag corruption.
> 
> This introduces "struct pgtable_debug_args". The struct is initialized
> and destroyed, but the information in the struct isn't used yet. They
> will be used in subsequent patches.
> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 196 insertions(+), 1 deletion(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 1c922691aa61..ea153ff40d23 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -58,6 +58,36 @@
>  #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
>  #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;
> +
> +	unsigned long		pud_pfn;
> +	unsigned long		pmd_pfn;
> +	unsigned long		pte_pfn;
> +
> +	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(unsigned long pfn, int idx)
>  {
>  	pgprot_t prot = protection_map[idx];
> @@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
>  	return random_vaddr;
>  }
>  
> +static void __init destroy_args(struct pgtable_debug_args *args)
> +{
> +	struct page *page = NULL;
> +
> +	/* Free (huge) page */
> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> +	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
> +	    has_transparent_hugepage() &&
> +	    args->pud_pfn != ULONG_MAX) {
> +		page = pfn_to_page(args->pud_pfn);
> +		__free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
> +	} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> +		   has_transparent_hugepage() &&
> +		   args->pmd_pfn != ULONG_MAX) {
> +		page = pfn_to_page(args->pmd_pfn);
> +		__free_pages(page, HPAGE_PMD_ORDER);
> +	} else if (args->pte_pfn != ULONG_MAX) {
> +		page = pfn_to_page(args->pte_pfn);
> +		__free_pages(page, 0);
> +	}
> +
> +	/* Free page table */
> +	if (args->start_ptep) {
> +		pte_free(args->mm, args->start_ptep);
> +		mm_dec_nr_ptes(args->mm);
> +	}
> +
> +	if (args->start_pmdp) {
> +		pmd_free(args->mm, args->start_pmdp);
> +		mm_dec_nr_pmds(args->mm);
> +	}
> +
> +	if (args->start_pudp) {
> +		pud_free(args->mm, args->start_pudp);
> +		mm_dec_nr_puds(args->mm);
> +	}
> +
> +	if (args->start_p4dp)
> +		p4d_free(args->mm, args->p4dp);
> +
> +	/* Free vma and mm struct */
> +	if (args->vma)
> +		vm_area_free(args->vma);
> +	if (args->mm)
> +		mmdrop(args->mm);
> +}
> +
> +static int __init init_args(struct pgtable_debug_args *args)
> +{
> +	struct page *page = NULL;
> +	phys_addr_t phys;
> +	int ret = 0;
> +
> +	/* Initialize the debugging data */
> +	memset(args, 0, sizeof(*args));
> +	args->page_prot      = vm_get_page_prot(VMFLAGS);
> +	args->page_prot_none = __P000;

Please preserve the existing comments before this assignment.

        /*
         * __P000 (or even __S000) will help create page table entries with
         * PROT_NONE permission as required for pxx_protnone_tests().
         */

> +	args->pud_pfn        = ULONG_MAX;
> +	args->pmd_pfn        = ULONG_MAX;
> +	args->pte_pfn        = ULONG_MAX;
> +	args->fixed_pgd_pfn  = ULONG_MAX;
> +	args->fixed_p4d_pfn  = ULONG_MAX;
> +	args->fixed_pud_pfn  = ULONG_MAX;
> +	args->fixed_pmd_pfn  = ULONG_MAX;
> +	args->fixed_pte_pfn  = ULONG_MAX;
> +
> +	/* Allocate mm and vma */
> +	args->mm = mm_alloc();
> +	if (!args->mm) {
> +		pr_err("Failed to allocate mm struct\n");
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	args->vma = vm_area_alloc(args->mm);
> +	if (!args->vma) {
> +		pr_err("Failed to allocate vma\n");
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	/* Figure out the virtual address and allocate page table entries */
> +	args->vaddr = get_random_vaddr();

Please group args->vaddr's init with page_prot and page_prot_none above.

> +	args->pgdp = pgd_offset(args->mm, args->vaddr);
> +	args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
> +	args->pudp = args->p4dp ?
> +		     pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
> +	args->pmdp = args->pudp ?
> +		     pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
> +	args->ptep = args->pmdp ?
> +		     pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
> +	if (!args->ptep) {
> +		pr_err("Failed to allocate page table\n");
> +		ret = -ENOMEM;
> +		goto error;
> +	}

Why not just assert that all page table level pointers are allocated
successfully, otherwise bail out the test completely. Something like
this at each level.

	if (!args->p4dp) {
		pr_err("Failed to allocate page table\n");
		ret = -ENOMEM;
		goto error;
	}

Is there any value in proceeding with the test when some page table
pointers have not been allocated. Also individual tests do not cross
check these pointers. Also asserting successful allocations will
make the freeing path simpler, as I had mentioned earlier.

> +
> +	/*
> +	 * The above page table entries will be modified. Lets save the
> +	 * page table entries so that they can be released when the tests
> +	 * are completed.
> +	 */
> +	args->start_p4dp = p4d_offset(args->pgdp, 0UL);
> +	args->start_pudp = pud_offset(args->p4dp, 0UL);
> +	args->start_pmdp = pmd_offset(args->pudp, 0UL);
> +	args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));

If the above page table pointers have been validated to be allocated
successfully, we could add these here.

	WARN_ON(!args->start_p4dp)
	WARN_ON(!args->start_pudp)
	WARN_ON(!args->start_pmdp)
	WARN_ON(!args->start_ptep)

Afterwards all those if (args->start_pxdp) checks in the freeing path
will not be required anymore.

> +
> +	/*
> +	 * Figure out the fixed addresses, which are all around the kernel
> +	 * symbol (@start_kernel). The corresponding PFNs might be invalid,
> +	 * but it's fine as the following tests won't access the pages.
> +	 */
> +	phys = __pa_symbol(&start_kernel);
> +	args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
> +	args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
> +	args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
> +	args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
> +	args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
> +
> +	/*
> +	 * Allocate (huge) pages because some of the tests need to access
> +	 * the data in the pages. The corresponding tests will be skipped
> +	 * if we fail to allocate (huge) pages.
> +	 */
> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> +	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
> +	    has_transparent_hugepage()) {
> +		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
> +				   HPAGE_PUD_SHIFT - PAGE_SHIFT);

Please drop __GFP_NOWARN and instead use something like alloc_contig_pages()
when required allocation order exceed (MAX_ORDER - 1). Else the test might
not be able to execute on platform configurations, where PUD THP is enabled.

> +		if (page) {
> +			args->pud_pfn = page_to_pfn(page);
> +			args->pmd_pfn = args->pud_pfn;
> +			args->pte_pfn = args->pud_pfn;
> +			return 0;
> +		}
> +	}
> +
> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> +	    has_transparent_hugepage()) {
> +		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN, HPAGE_PMD_ORDER);
> +		if (page) {
> +			args->pmd_pfn = page_to_pfn(page);
> +			args->pte_pfn = args->pmd_pfn;
> +			return 0;
> +		}
> +	}
> +
> +	page = alloc_pages(GFP_KERNEL, 0);
> +	if (page)
> +		args->pte_pfn = page_to_pfn(page);
> +
> +	return 0;
> +
> +error:
> +	destroy_args(args);
> +	return ret;
> +}
> +
>  static int __init debug_vm_pgtable(void)
>  {
> +	struct pgtable_debug_args args;>  	struct vm_area_struct *vma;
>  	struct mm_struct *mm;
>  	pgd_t *pgdp;
> @@ -970,9 +1159,13 @@ static int __init debug_vm_pgtable(void)
>  	unsigned long vaddr, pte_aligned, pmd_aligned;
>  	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
>  	spinlock_t *ptl = NULL;
> -	int idx;
> +	int idx, ret;
>  
>  	pr_info("Validating architecture page table helpers\n");
> +	ret = init_args(&args);
> +	if (ret)
> +		return ret;
> +
>  	prot = vm_get_page_prot(VMFLAGS);
>  	vaddr = get_random_vaddr();
>  	mm = mm_alloc();
> @@ -1127,6 +1320,8 @@ static int __init debug_vm_pgtable(void)
>  	mm_dec_nr_pmds(mm);
>  	mm_dec_nr_ptes(mm);
>  	mmdrop(mm);
> +
> +	destroy_args(&args);
>  	return 0;
>  }
>  late_initcall(debug_vm_pgtable);
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests
  2021-07-19 13:06 ` [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests Gavin Shan
@ 2021-07-21  5:57   ` Anshuman Khandual
  2021-07-21 11:57     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-21  5:57 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in the basic test functions. There
> are more cleanups applied:
> 
>    * Move hugetlb_basic_tests() so that the basic tests are treated
>      as a bundle.

Please dont move this test as it is placed near to the THP functions.
Also please don't club any non-related changes here while converting
various tests into this new arg structure.

> 
>    * Drop parameters to {pgd, p4d}_basic_tests() as they're never
>      used.

Right, they are not getting used right now. But then it might be just
better to replace them with this new structure in order not to do any
unrelated changes.

> 
>    * Drop unused variables @{pgd, p4d}_aligned in debug_vm_pgtable().
> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 51 ++++++++++++++++++++-----------------------
>  1 file changed, 24 insertions(+), 27 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index ea153ff40d23..5f79d725f8d8 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -88,10 +88,10 @@ struct pgtable_debug_args {
>  	unsigned long		fixed_pte_pfn;
>  };
>  
> -static void __init pte_basic_tests(unsigned long pfn, int idx)
> +static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
>  {
>  	pgprot_t prot = protection_map[idx];
> -	pte_t pte = pfn_pte(pfn, prot);
> +	pte_t pte = pfn_pte(args->fixed_pte_pfn, prot);
>  	unsigned long val = idx, *ptr = &val;
>  
>  	pr_debug("Validating PTE basic (%pGv)\n", ptr);
> @@ -173,7 +173,7 @@ static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot)
>  }
>  
>  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
> -static void __init pmd_basic_tests(unsigned long pfn, int idx)
> +static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>  {
>  	pgprot_t prot = protection_map[idx];
>  	unsigned long val = idx, *ptr = &val;
> @@ -183,7 +183,7 @@ static void __init pmd_basic_tests(unsigned long pfn, int idx)
>  		return;
>  
>  	pr_debug("Validating PMD basic (%pGv)\n", ptr);
> -	pmd = pfn_pmd(pfn, prot);
> +	pmd = pfn_pmd(args->fixed_pmd_pfn, prot);
>  
>  	/*
>  	 * This test needs to be executed after the given page table entry
> @@ -295,7 +295,7 @@ static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot)
>  }
>  
>  #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx)
> +static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
>  {
>  	pgprot_t prot = protection_map[idx];
>  	unsigned long val = idx, *ptr = &val;
> @@ -305,7 +305,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
>  		return;
>  
>  	pr_debug("Validating PUD basic (%pGv)\n", ptr);
> -	pud = pfn_pud(pfn, prot);
> +	pud = pfn_pud(args->fixed_pud_pfn, prot);
>  
>  	/*
>  	 * This test needs to be executed after the given page table entry
> @@ -326,7 +326,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
>  	WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud))));
>  	WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud))));
>  
> -	if (mm_pmd_folded(mm))
> +	if (mm_pmd_folded(args->mm))
>  		return;
>  
>  	/*
> @@ -403,7 +403,7 @@ static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot)
>  	WARN_ON(!pud_leaf(pud));
>  }
>  #else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
> +static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }
>  static void __init pud_advanced_tests(struct mm_struct *mm,
>  				      struct vm_area_struct *vma, pud_t *pudp,
>  				      unsigned long pfn, unsigned long vaddr,
> @@ -413,8 +413,8 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
>  static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { }
>  #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
>  #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
> -static void __init pmd_basic_tests(unsigned long pfn, int idx) { }
> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
> +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 mm_struct *mm,
>  				      struct vm_area_struct *vma, pmd_t *pmdp,
>  				      unsigned long pfn, unsigned long vaddr,
> @@ -475,7 +475,7 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>  static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>  #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>  
> -static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
> +static void __init p4d_basic_tests(void)
>  {
>  	p4d_t p4d;
>  
> @@ -484,7 +484,7 @@ static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
>  	WARN_ON(!p4d_same(p4d, p4d));
>  }
>  
> -static void __init pgd_basic_tests(unsigned long pfn, pgprot_t prot)
> +static void __init pgd_basic_tests(void)
>  {
>  	pgd_t pgd;
>  
> @@ -889,7 +889,7 @@ static void __init swap_migration_tests(void)
>  }
>  
>  #ifdef CONFIG_HUGETLB_PAGE
> -static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
> +static void __init hugetlb_basic_tests(struct pgtable_debug_args *args)
>  {
>  	struct page *page;
>  	pte_t pte;
> @@ -899,21 +899,21 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
>  	 * Accessing the page associated with the pfn is safe here,
>  	 * as it was previously derived from a real kernel symbol.
>  	 */
> -	page = pfn_to_page(pfn);
> -	pte = mk_huge_pte(page, prot);
> +	page = pfn_to_page(args->fixed_pmd_pfn);
> +	pte = mk_huge_pte(page, args->page_prot);
>  
>  	WARN_ON(!huge_pte_dirty(huge_pte_mkdirty(pte)));
>  	WARN_ON(!huge_pte_write(huge_pte_mkwrite(huge_pte_wrprotect(pte))));
>  	WARN_ON(huge_pte_write(huge_pte_wrprotect(huge_pte_mkwrite(pte))));
>  
>  #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
> -	pte = pfn_pte(pfn, prot);
> +	pte = pfn_pte(args->fixed_pmd_pfn, args->page_prot);
>  
>  	WARN_ON(!pte_huge(pte_mkhuge(pte)));
>  #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
>  }
>  #else  /* !CONFIG_HUGETLB_PAGE */
> -static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
> +static void __init hugetlb_basic_tests(struct pgtable_debug_args *args) { }
>  #endif /* CONFIG_HUGETLB_PAGE */
>  
>  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
> @@ -1157,7 +1157,7 @@ static int __init debug_vm_pgtable(void)
>  	pgprot_t prot, protnone;
>  	phys_addr_t paddr;
>  	unsigned long vaddr, pte_aligned, pmd_aligned;
> -	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
> +	unsigned long pud_aligned;
>  	spinlock_t *ptl = NULL;
>  	int idx, ret;
>  
> @@ -1200,8 +1200,6 @@ static int __init debug_vm_pgtable(void)
>  	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;
>  	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>  	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
> -	p4d_aligned = (paddr & P4D_MASK) >> PAGE_SHIFT;
> -	pgd_aligned = (paddr & PGDIR_MASK) >> PAGE_SHIFT;
>  	WARN_ON(!pfn_valid(pte_aligned));
>  
>  	pgdp = pgd_offset(mm, vaddr);
> @@ -1235,9 +1233,9 @@ static int __init debug_vm_pgtable(void)
>  	 * given page table entry.
>  	 */
>  	for (idx = 0; idx < ARRAY_SIZE(protection_map); idx++) {
> -		pte_basic_tests(pte_aligned, idx);
> -		pmd_basic_tests(pmd_aligned, idx);
> -		pud_basic_tests(mm, pud_aligned, idx);
> +		pte_basic_tests(&args, idx);
> +		pmd_basic_tests(&args, idx);
> +		pud_basic_tests(&args, idx);
>  	}
>  
>  	/*
> @@ -1247,8 +1245,9 @@ static int __init debug_vm_pgtable(void)
>  	 * the above iteration for now to save some test execution
>  	 * time.
>  	 */
> -	p4d_basic_tests(p4d_aligned, prot);
> -	pgd_basic_tests(pgd_aligned, prot);
> +	p4d_basic_tests();
> +	pgd_basic_tests();
> +	hugetlb_basic_tests(&args);
>  
>  	pmd_leaf_tests(pmd_aligned, prot);
>  	pud_leaf_tests(pud_aligned, prot);
> @@ -1277,8 +1276,6 @@ static int __init debug_vm_pgtable(void)
>  	pmd_thp_tests(pmd_aligned, prot);
>  	pud_thp_tests(pud_aligned, prot);
>  
> -	hugetlb_basic_tests(pte_aligned, prot);
> -
>  	/*
>  	 * Page table modifying tests. They need to hold
>  	 * proper page table lock.
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag
  2021-07-19 13:06 ` [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag Gavin Shan
@ 2021-07-21 10:18   ` Anshuman Khandual
  2021-07-21 12:03     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-21 10:18 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> In page table entry modifying tests, set_xxx_at() are used to populate
> the page table entries. On ARM64, PG_arch_1 is set to the target page
> flag if execution permission is given. The page flag is kept when the
> page is free'd to buddy's free area list. However, it will trigger page
> checking failure when it's pulled from the buddy's free area list, as
> the following warning messages indicate.
> 
>    BUG: Bad page state in process memhog  pfn:08000
>    page:0000000015c0a628 refcount:0 mapcount:0 \
>         mapping:0000000000000000 index:0x1 pfn:0x8000
>    flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff)
>    raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000
>    raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
>    page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set
> 
> This fixes the issue by clearing PG_arch_1 through flush_dcache_page()
> after set_xxx_at() is called.

Could you please add comments before each flush_dcache_page() instance
explaining why this is needed for arm64 platforms with relevant PG_arch_1
context and how this does not have any adverse effect on other platforms ?
It should be easy for some one looking at this code after a while to figure
out from where flush_dcache_page() came from.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-21  5:44   ` Anshuman Khandual
@ 2021-07-21 10:20     ` Gavin Shan
  2021-07-21 10:39       ` Anshuman Khandual
  2021-07-22  4:41       ` Anshuman Khandual
  0 siblings, 2 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-21 10:20 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/21/21 3:44 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> In debug_vm_pgtable(), there are many local variables introduced to
>> track the needed information and they are passed to the functions for
>> various test cases. It'd better to introduce a struct as place holder
>> for these information. With it, what the functions for various test
>> cases need is the struct, to simplify the code. It also makes code
>> easier to be maintained.
>>
>> Besides, set_xxx_at() could access the data on the corresponding pages
>> in the page table modifying tests. So the accessed pages in the tests
>> should have been allocated from buddy. Otherwise, we're accessing pages
>> that aren't owned by us. This causes issues like page flag corruption.
>>
>> This introduces "struct pgtable_debug_args". The struct is initialized
>> and destroyed, but the information in the struct isn't used yet. They
>> will be used in subsequent patches.
>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 196 insertions(+), 1 deletion(-)
>>

I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
I will wait to integrate your comments to v4 until you finish the review
on all patches in v3 series.

>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index 1c922691aa61..ea153ff40d23 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -58,6 +58,36 @@
>>   #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
>>   #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;
>> +
>> +	unsigned long		pud_pfn;
>> +	unsigned long		pmd_pfn;
>> +	unsigned long		pte_pfn;
>> +
>> +	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(unsigned long pfn, int idx)
>>   {
>>   	pgprot_t prot = protection_map[idx];
>> @@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
>>   	return random_vaddr;
>>   }
>>   
>> +static void __init destroy_args(struct pgtable_debug_args *args)
>> +{
>> +	struct page *page = NULL;
>> +
>> +	/* Free (huge) page */
>> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>> +	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>> +	    has_transparent_hugepage() &&
>> +	    args->pud_pfn != ULONG_MAX) {
>> +		page = pfn_to_page(args->pud_pfn);
>> +		__free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
>> +	} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>> +		   has_transparent_hugepage() &&
>> +		   args->pmd_pfn != ULONG_MAX) {
>> +		page = pfn_to_page(args->pmd_pfn);
>> +		__free_pages(page, HPAGE_PMD_ORDER);
>> +	} else if (args->pte_pfn != ULONG_MAX) {
>> +		page = pfn_to_page(args->pte_pfn);
>> +		__free_pages(page, 0);
>> +	}
>> +
>> +	/* Free page table */
>> +	if (args->start_ptep) {
>> +		pte_free(args->mm, args->start_ptep);
>> +		mm_dec_nr_ptes(args->mm);
>> +	}
>> +
>> +	if (args->start_pmdp) {
>> +		pmd_free(args->mm, args->start_pmdp);
>> +		mm_dec_nr_pmds(args->mm);
>> +	}
>> +
>> +	if (args->start_pudp) {
>> +		pud_free(args->mm, args->start_pudp);
>> +		mm_dec_nr_puds(args->mm);
>> +	}
>> +
>> +	if (args->start_p4dp)
>> +		p4d_free(args->mm, args->p4dp);
>> +
>> +	/* Free vma and mm struct */
>> +	if (args->vma)
>> +		vm_area_free(args->vma);
>> +	if (args->mm)
>> +		mmdrop(args->mm);
>> +}
>> +
>> +static int __init init_args(struct pgtable_debug_args *args)
>> +{
>> +	struct page *page = NULL;
>> +	phys_addr_t phys;
>> +	int ret = 0;
>> +
>> +	/* Initialize the debugging data */
>> +	memset(args, 0, sizeof(*args));
>> +	args->page_prot      = vm_get_page_prot(VMFLAGS);
>> +	args->page_prot_none = __P000;
> 
> Please preserve the existing comments before this assignment.
> 
>          /*
>           * __P000 (or even __S000) will help create page table entries with
>           * PROT_NONE permission as required for pxx_protnone_tests().
>           */
> 

Sure. I will combine the comments in v4 as below:

	/*
	 * Initialize the debugging arguments.
	 *
	 * __P000 (or even __S000) will help create page table entries with
          * PROT_NONE permission as required for pxx_protnone_tests().
          */


>> +	args->pud_pfn        = ULONG_MAX;
>> +	args->pmd_pfn        = ULONG_MAX;
>> +	args->pte_pfn        = ULONG_MAX;
>> +	args->fixed_pgd_pfn  = ULONG_MAX;
>> +	args->fixed_p4d_pfn  = ULONG_MAX;
>> +	args->fixed_pud_pfn  = ULONG_MAX;
>> +	args->fixed_pmd_pfn  = ULONG_MAX;
>> +	args->fixed_pte_pfn  = ULONG_MAX;
>> +
>> +	/* Allocate mm and vma */
>> +	args->mm = mm_alloc();
>> +	if (!args->mm) {
>> +		pr_err("Failed to allocate mm struct\n");
>> +		ret = -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	args->vma = vm_area_alloc(args->mm);
>> +	if (!args->vma) {
>> +		pr_err("Failed to allocate vma\n");
>> +		ret = -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	/* Figure out the virtual address and allocate page table entries */
>> +	args->vaddr = get_random_vaddr();
> 
> Please group args->vaddr's init with page_prot and page_prot_none above.
> 

Yes, It will make the code tidy. I'll move this line accordingly in v4,
but the related comments will be dropped as the code is self-explanatory.

         /* Allocate page table entries */

>> +	args->pgdp = pgd_offset(args->mm, args->vaddr);
>> +	args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>> +	args->pudp = args->p4dp ?
>> +		     pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
>> +	args->pmdp = args->pudp ?
>> +		     pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
>> +	args->ptep = args->pmdp ?
>> +		     pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
>> +	if (!args->ptep) {
>> +		pr_err("Failed to allocate page table\n");
>> +		ret = -ENOMEM;
>> +		goto error;
>> +	}
> 
> Why not just assert that all page table level pointers are allocated
> successfully, otherwise bail out the test completely. Something like
> this at each level.
> 
> 	if (!args->p4dp) {
> 		pr_err("Failed to allocate page table\n");
> 		ret = -ENOMEM;
> 		goto error;
> 	}
> 
> Is there any value in proceeding with the test when some page table
> pointers have not been allocated. Also individual tests do not cross
> check these pointers. Also asserting successful allocations will
> make the freeing path simpler, as I had mentioned earlier.
> 

There is no tests will be carried out if we fail to allocate any level
of page table entries. For other questions, please refer below response.
In summary, this snippet needs to be combined with next snippet, as below.

>> +
>> +	/*
>> +	 * The above page table entries will be modified. Lets save the
>> +	 * page table entries so that they can be released when the tests
>> +	 * are completed.
>> +	 */
>> +	args->start_p4dp = p4d_offset(args->pgdp, 0UL);
>> +	args->start_pudp = pud_offset(args->p4dp, 0UL);
>> +	args->start_pmdp = pmd_offset(args->pudp, 0UL);
>> +	args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
> 
> If the above page table pointers have been validated to be allocated
> successfully, we could add these here.
> 
> 	WARN_ON(!args->start_p4dp)
> 	WARN_ON(!args->start_pudp)
> 	WARN_ON(!args->start_pmdp)
> 	WARN_ON(!args->start_ptep)
> 
> Afterwards all those if (args->start_pxdp) checks in the freeing path
> will not be required anymore.
> 

The check on @args->start_pxdp is still needed in destroy_args() for
couple of cases: (1) destroy_args() is called on failing to allocate
@args->mm or @args->vma. That time, no page table entries are allocated.
(2) It's possible to fail allocating current level of page table entries
even the previous levels of page table entries are allocated successfully.

So Lets change these (above) two snippets as below in v4:

	/*
	 * Allocate page table entries. The allocated page table entries
	 * will be modified in the tests. Lets save the page table entries
	 * so that they can be released when the tests are completed.
	 */
	args->pgdp = pgd_offset(args->mm, args->vaddr);
	args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
	if (!args->p4dp) {
	    pr_err("Failed to allocate p4d entries\n");
	    ret = -ENOMEM;
	    goto error;
	}

	args->start_p4dp = p4d_offset(args->pgdp, 0UL);
	args->pudp = pud_alloc(args->mm, args->p4dp, args->vaddr);
	if (!args->pudp) {
	    pr_err("Failed to allocate pud entries\n");
	    ret = -ENOMEM;
	    goto error;
	}

	args->pmdp = pmd_alloc(args->mm, args->pudp, args->vaddr);
	if (!args->pmdp) {
	    pr_err("Failed to allocate PMD entries\n");
	    ret = -ENOMEM;
	    goto error;
	}

	args->start_pmdp = pmd_offset(args->pudp, 0UL);
	args->ptep = pte_alloc_map(args->mm, args->pmdp, args->vaddr);
	if (!args->ptep) {
	    pr_err("Failed to allocate page table\n");
	    ret = -ENOMEM;
	    goto error;
	}

	args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));

>> +
>> +	/*
>> +	 * Figure out the fixed addresses, which are all around the kernel
>> +	 * symbol (@start_kernel). The corresponding PFNs might be invalid,
>> +	 * but it's fine as the following tests won't access the pages.
>> +	 */
>> +	phys = __pa_symbol(&start_kernel);
>> +	args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
>> +	args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
>> +	args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
>> +	args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
>> +	args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
>> +
>> +	/*
>> +	 * Allocate (huge) pages because some of the tests need to access
>> +	 * the data in the pages. The corresponding tests will be skipped
>> +	 * if we fail to allocate (huge) pages.
>> +	 */
>> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>> +	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>> +	    has_transparent_hugepage()) {
>> +		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>> +				   HPAGE_PUD_SHIFT - PAGE_SHIFT);
> 
> Please drop __GFP_NOWARN and instead use something like alloc_contig_pages()
> when required allocation order exceed (MAX_ORDER - 1). Else the test might
> not be able to execute on platform configurations, where PUD THP is enabled.
> 

Yes, It's correct that alloc_contig_pages() should be used here, depending
on CONFIG_CONTIG_ALLOC. Otherwise, alloc_pages(...__GFP_NOWARN...) is still
used as we're doing. This snippet will be changed like below in v4:

	/*
	 * Allocate (huge) pages because some of the tests need to access
	 * the data in the pages. The corresponding tests will be skipped
	 * if we fail to allocate (huge) pages.
	 */
	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
	    IS_ENABLED(CONFIG_CONTIG_ALLOC)) &&
	    has_transparent_hugepage()) {
		page = alloc_contig_pages((1 << (HPAGE_PUD_SHIFT - PAGE_SHIFT)),
					  GFP_KERNEL | __GFP_NOWARN,
					  first_online_node, NULL);
		if (page) {
			args->is_contiguous_pud_page = true;
			args->pud_pfn = page_to_pfn(page);
			args->pmd_pfn = args->pud_pfn;
			args->pte_pfn = args->pud_pfn;
			return 0;
		}
	}

	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
	    IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
	    has_transparent_hugepage()) {
		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
				   HPAGE_PUD_SHIFT - PAGE_SHIFT);
		if (page) {
			args->is_contiguous_pud_page = false;
			args->pud_pfn = page_to_pfn(page);
			args->pmd_pfn = args->pud_pfn;
			args->pte_pfn = args->pud_pfn;
			return 0;
		}
	}

	[... The logic to allocate PMD huge page or page is kept as of being]
         [... The code to release the PUD huge page needs changes based on @args->is_contiguous_pud_page]


>> +		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>> +				   HPAGE_PUD_SHIFT - PAGE_SHIFT);
>> +		if (page) {
>> +			args->pud_pfn = page_to_pfn(page);
>> +			args->pmd_pfn = args->pud_pfn;
>> +			args->pte_pfn = args->pud_pfn;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>> +	    has_transparent_hugepage()) {
>> +		page = alloc_pages(GFP_KERNEL | __GFP_NOWARN, HPAGE_PMD_ORDER);
>> +		if (page) {
>> +			args->pmd_pfn = page_to_pfn(page);
>> +			args->pte_pfn = args->pmd_pfn;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	page = alloc_pages(GFP_KERNEL, 0);
>> +	if (page)
>> +		args->pte_pfn = page_to_pfn(page);
>> +
>> +	return 0;
>> +
>> +error:
>> +	destroy_args(args);
>> +	return ret;
>> +}
>> +
>>   static int __init debug_vm_pgtable(void)
>>   {
>> +	struct pgtable_debug_args args;>  	struct vm_area_struct *vma;
>>   	struct mm_struct *mm;
>>   	pgd_t *pgdp;
>> @@ -970,9 +1159,13 @@ static int __init debug_vm_pgtable(void)
>>   	unsigned long vaddr, pte_aligned, pmd_aligned;
>>   	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
>>   	spinlock_t *ptl = NULL;
>> -	int idx;
>> +	int idx, ret;
>>   
>>   	pr_info("Validating architecture page table helpers\n");
>> +	ret = init_args(&args);
>> +	if (ret)
>> +		return ret;
>> +
>>   	prot = vm_get_page_prot(VMFLAGS);
>>   	vaddr = get_random_vaddr();
>>   	mm = mm_alloc();
>> @@ -1127,6 +1320,8 @@ static int __init debug_vm_pgtable(void)
>>   	mm_dec_nr_pmds(mm);
>>   	mm_dec_nr_ptes(mm);
>>   	mmdrop(mm);
>> +
>> +	destroy_args(&args);
>>   	return 0;
>>   }
>>   late_initcall(debug_vm_pgtable);
>>

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-21 10:20     ` Gavin Shan
@ 2021-07-21 10:39       ` Anshuman Khandual
  2021-07-21 10:59         ` Anshuman Khandual
  2021-07-22  4:41       ` Anshuman Khandual
  1 sibling, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-21 10:39 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin,
	Christophe Leroy, Gerald Schaefer, Qian Cai, Aneesh Kumar K.V



On 7/21/21 3:50 PM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>> In debug_vm_pgtable(), there are many local variables introduced to
>>> track the needed information and they are passed to the functions for
>>> various test cases. It'd better to introduce a struct as place holder
>>> for these information. With it, what the functions for various test
>>> cases need is the struct, to simplify the code. It also makes code
>>> easier to be maintained.
>>>
>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>> in the page table modifying tests. So the accessed pages in the tests
>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>> that aren't owned by us. This causes issues like page flag corruption.
>>>
>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>> and destroyed, but the information in the struct isn't used yet. They
>>> will be used in subsequent patches.
>>>
>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>> ---
>>>   mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>   1 file changed, 196 insertions(+), 1 deletion(-)
>>>
> 
> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
> I will wait to integrate your comments to v4 until you finish the review
> on all patches in v3 series

Yes, please do wait for the complete review and test before going for V4.
Also please add the following emails on copy next time, so that we might
have some more reviews here. Thank you.

+ Christophe Leroy <christophe.leroy@csgroup.eu>
+ Gerald Schaefer <gerald.schaefer@de.ibm.com>
+ Qian Cai <cai@lca.pw>
+ Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-21 10:39       ` Anshuman Khandual
@ 2021-07-21 10:59         ` Anshuman Khandual
  2021-07-21 11:59           ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-21 10:59 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin,
	Christophe Leroy, Gerald Schaefer, Qian Cai, Aneesh Kumar K.V



On 7/21/21 4:09 PM, Anshuman Khandual wrote:
> 
> On 7/21/21 3:50 PM, Gavin Shan wrote:
>> Hi Anshuman,
>>
>> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>> In debug_vm_pgtable(), there are many local variables introduced to
>>>> track the needed information and they are passed to the functions for
>>>> various test cases. It'd better to introduce a struct as place holder
>>>> for these information. With it, what the functions for various test
>>>> cases need is the struct, to simplify the code. It also makes code
>>>> easier to be maintained.
>>>>
>>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>>> in the page table modifying tests. So the accessed pages in the tests
>>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>>> that aren't owned by us. This causes issues like page flag corruption.
>>>>
>>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>>> and destroyed, but the information in the struct isn't used yet. They
>>>> will be used in subsequent patches.
>>>>
>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>> ---
>>>>   mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>>   1 file changed, 196 insertions(+), 1 deletion(-)
>>>>
>> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
>> I will wait to integrate your comments to v4 until you finish the review
>> on all patches in v3 series
> Yes, please do wait for the complete review and test before going for V4.
> Also please add the following emails on copy next time, so that we might
> have some more reviews here. Thank you.
> 
> + Christophe Leroy <christophe.leroy@csgroup.eu>
> + Gerald Schaefer <gerald.schaefer@de.ibm.com>

This one instead.

Gerald Schaefer <gerald.schaefer@linux.ibm.com>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests
  2021-07-21  5:57   ` Anshuman Khandual
@ 2021-07-21 11:57     ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-21 11:57 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/21/21 3:57 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> This uses struct pgtable_debug_args in the basic test functions. There
>> are more cleanups applied:
>>
>>     * Move hugetlb_basic_tests() so that the basic tests are treated
>>       as a bundle.
> 
> Please dont move this test as it is placed near to the THP functions.
> Also please don't club any non-related changes here while converting
> various tests into this new arg structure.
> 

Yes. I won't move hugetlb_basic_tests() around in v4.

>>
>>     * Drop parameters to {pgd, p4d}_basic_tests() as they're never
>>       used.
> 
> Right, they are not getting used right now. But then it might be just
> better to replace them with this new structure in order not to do any
> unrelated changes.
> 

Sure. I will pass &args to these two functions in v4, even they are
not used. Hopefully, I understand your comments correctly.


>>
>>     * Drop unused variables @{pgd, p4d}_aligned in debug_vm_pgtable().
>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 51 ++++++++++++++++++++-----------------------
>>   1 file changed, 24 insertions(+), 27 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index ea153ff40d23..5f79d725f8d8 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -88,10 +88,10 @@ struct pgtable_debug_args {
>>   	unsigned long		fixed_pte_pfn;
>>   };
>>   
>> -static void __init pte_basic_tests(unsigned long pfn, int idx)
>> +static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
>>   {
>>   	pgprot_t prot = protection_map[idx];
>> -	pte_t pte = pfn_pte(pfn, prot);
>> +	pte_t pte = pfn_pte(args->fixed_pte_pfn, prot);
>>   	unsigned long val = idx, *ptr = &val;
>>   
>>   	pr_debug("Validating PTE basic (%pGv)\n", ptr);
>> @@ -173,7 +173,7 @@ static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot)
>>   }
>>   
>>   #ifdef CONFIG_TRANSPARENT_HUGEPAGE
>> -static void __init pmd_basic_tests(unsigned long pfn, int idx)
>> +static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>>   {
>>   	pgprot_t prot = protection_map[idx];
>>   	unsigned long val = idx, *ptr = &val;
>> @@ -183,7 +183,7 @@ static void __init pmd_basic_tests(unsigned long pfn, int idx)
>>   		return;
>>   
>>   	pr_debug("Validating PMD basic (%pGv)\n", ptr);
>> -	pmd = pfn_pmd(pfn, prot);
>> +	pmd = pfn_pmd(args->fixed_pmd_pfn, prot);
>>   
>>   	/*
>>   	 * This test needs to be executed after the given page table entry
>> @@ -295,7 +295,7 @@ static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot)
>>   }
>>   
>>   #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
>> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx)
>> +static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
>>   {
>>   	pgprot_t prot = protection_map[idx];
>>   	unsigned long val = idx, *ptr = &val;
>> @@ -305,7 +305,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
>>   		return;
>>   
>>   	pr_debug("Validating PUD basic (%pGv)\n", ptr);
>> -	pud = pfn_pud(pfn, prot);
>> +	pud = pfn_pud(args->fixed_pud_pfn, prot);
>>   
>>   	/*
>>   	 * This test needs to be executed after the given page table entry
>> @@ -326,7 +326,7 @@ static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int
>>   	WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud))));
>>   	WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud))));
>>   
>> -	if (mm_pmd_folded(mm))
>> +	if (mm_pmd_folded(args->mm))
>>   		return;
>>   
>>   	/*
>> @@ -403,7 +403,7 @@ static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot)
>>   	WARN_ON(!pud_leaf(pud));
>>   }
>>   #else  /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
>> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
>> +static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }
>>   static void __init pud_advanced_tests(struct mm_struct *mm,
>>   				      struct vm_area_struct *vma, pud_t *pudp,
>>   				      unsigned long pfn, unsigned long vaddr,
>> @@ -413,8 +413,8 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
>>   static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { }
>>   #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
>>   #else  /* !CONFIG_TRANSPARENT_HUGEPAGE */
>> -static void __init pmd_basic_tests(unsigned long pfn, int idx) { }
>> -static void __init pud_basic_tests(struct mm_struct *mm, unsigned long pfn, int idx) { }
>> +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 mm_struct *mm,
>>   				      struct vm_area_struct *vma, pmd_t *pmdp,
>>   				      unsigned long pfn, unsigned long vaddr,
>> @@ -475,7 +475,7 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>   static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>>   #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>   
>> -static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
>> +static void __init p4d_basic_tests(void)
>>   {
>>   	p4d_t p4d;
>>   
>> @@ -484,7 +484,7 @@ static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot)
>>   	WARN_ON(!p4d_same(p4d, p4d));
>>   }
>>   
>> -static void __init pgd_basic_tests(unsigned long pfn, pgprot_t prot)
>> +static void __init pgd_basic_tests(void)
>>   {
>>   	pgd_t pgd;
>>   
>> @@ -889,7 +889,7 @@ static void __init swap_migration_tests(void)
>>   }
>>   
>>   #ifdef CONFIG_HUGETLB_PAGE
>> -static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
>> +static void __init hugetlb_basic_tests(struct pgtable_debug_args *args)
>>   {
>>   	struct page *page;
>>   	pte_t pte;
>> @@ -899,21 +899,21 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
>>   	 * Accessing the page associated with the pfn is safe here,
>>   	 * as it was previously derived from a real kernel symbol.
>>   	 */
>> -	page = pfn_to_page(pfn);
>> -	pte = mk_huge_pte(page, prot);
>> +	page = pfn_to_page(args->fixed_pmd_pfn);
>> +	pte = mk_huge_pte(page, args->page_prot);
>>   
>>   	WARN_ON(!huge_pte_dirty(huge_pte_mkdirty(pte)));
>>   	WARN_ON(!huge_pte_write(huge_pte_mkwrite(huge_pte_wrprotect(pte))));
>>   	WARN_ON(huge_pte_write(huge_pte_wrprotect(huge_pte_mkwrite(pte))));
>>   
>>   #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
>> -	pte = pfn_pte(pfn, prot);
>> +	pte = pfn_pte(args->fixed_pmd_pfn, args->page_prot);
>>   
>>   	WARN_ON(!pte_huge(pte_mkhuge(pte)));
>>   #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
>>   }
>>   #else  /* !CONFIG_HUGETLB_PAGE */
>> -static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
>> +static void __init hugetlb_basic_tests(struct pgtable_debug_args *args) { }
>>   #endif /* CONFIG_HUGETLB_PAGE */
>>   
>>   #ifdef CONFIG_TRANSPARENT_HUGEPAGE
>> @@ -1157,7 +1157,7 @@ static int __init debug_vm_pgtable(void)
>>   	pgprot_t prot, protnone;
>>   	phys_addr_t paddr;
>>   	unsigned long vaddr, pte_aligned, pmd_aligned;
>> -	unsigned long pud_aligned, p4d_aligned, pgd_aligned;
>> +	unsigned long pud_aligned;
>>   	spinlock_t *ptl = NULL;
>>   	int idx, ret;
>>   
>> @@ -1200,8 +1200,6 @@ static int __init debug_vm_pgtable(void)
>>   	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;
>>   	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>>   	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>> -	p4d_aligned = (paddr & P4D_MASK) >> PAGE_SHIFT;
>> -	pgd_aligned = (paddr & PGDIR_MASK) >> PAGE_SHIFT;
>>   	WARN_ON(!pfn_valid(pte_aligned));
>>   
>>   	pgdp = pgd_offset(mm, vaddr);
>> @@ -1235,9 +1233,9 @@ static int __init debug_vm_pgtable(void)
>>   	 * given page table entry.
>>   	 */
>>   	for (idx = 0; idx < ARRAY_SIZE(protection_map); idx++) {
>> -		pte_basic_tests(pte_aligned, idx);
>> -		pmd_basic_tests(pmd_aligned, idx);
>> -		pud_basic_tests(mm, pud_aligned, idx);
>> +		pte_basic_tests(&args, idx);
>> +		pmd_basic_tests(&args, idx);
>> +		pud_basic_tests(&args, idx);
>>   	}
>>   
>>   	/*
>> @@ -1247,8 +1245,9 @@ static int __init debug_vm_pgtable(void)
>>   	 * the above iteration for now to save some test execution
>>   	 * time.
>>   	 */
>> -	p4d_basic_tests(p4d_aligned, prot);
>> -	pgd_basic_tests(pgd_aligned, prot);
>> +	p4d_basic_tests();
>> +	pgd_basic_tests();
>> +	hugetlb_basic_tests(&args);
>>   
>>   	pmd_leaf_tests(pmd_aligned, prot);
>>   	pud_leaf_tests(pud_aligned, prot);
>> @@ -1277,8 +1276,6 @@ static int __init debug_vm_pgtable(void)
>>   	pmd_thp_tests(pmd_aligned, prot);
>>   	pud_thp_tests(pud_aligned, prot);
>>   
>> -	hugetlb_basic_tests(pte_aligned, prot);
>> -
>>   	/*
>>   	 * Page table modifying tests. They need to hold
>>   	 * proper page table lock.
>>

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-21 10:59         ` Anshuman Khandual
@ 2021-07-21 11:59           ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-21 11:59 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin,
	Christophe Leroy, Gerald Schaefer, Qian Cai, Aneesh Kumar K.V

On 7/21/21 8:59 PM, Anshuman Khandual wrote:
> On 7/21/21 4:09 PM, Anshuman Khandual wrote:
>> On 7/21/21 3:50 PM, Gavin Shan wrote:
>>> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>>> In debug_vm_pgtable(), there are many local variables introduced to
>>>>> track the needed information and they are passed to the functions for
>>>>> various test cases. It'd better to introduce a struct as place holder
>>>>> for these information. With it, what the functions for various test
>>>>> cases need is the struct, to simplify the code. It also makes code
>>>>> easier to be maintained.
>>>>>
>>>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>>>> in the page table modifying tests. So the accessed pages in the tests
>>>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>>>> that aren't owned by us. This causes issues like page flag corruption.
>>>>>
>>>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>>>> and destroyed, but the information in the struct isn't used yet. They
>>>>> will be used in subsequent patches.
>>>>>
>>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>>> ---
>>>>>    mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>>>    1 file changed, 196 insertions(+), 1 deletion(-)
>>>>>
>>> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
>>> I will wait to integrate your comments to v4 until you finish the review
>>> on all patches in v3 series
>> Yes, please do wait for the complete review and test before going for V4.
>> Also please add the following emails on copy next time, so that we might
>> have some more reviews here. Thank you.
>>
>> + Christophe Leroy <christophe.leroy@csgroup.eu>
>> + Gerald Schaefer <gerald.schaefer@de.ibm.com>
> 
> This one instead.
> 
> Gerald Schaefer <gerald.schaefer@linux.ibm.com>
> 

Sure, It's always nice to have more reviews from the experts. I will
include them when I post v4. Thanks again for your time to review :)

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag
  2021-07-21 10:18   ` Anshuman Khandual
@ 2021-07-21 12:03     ` Gavin Shan
  2021-07-22  3:51       ` Anshuman Khandual
  0 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-21 12:03 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/21/21 8:18 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> In page table entry modifying tests, set_xxx_at() are used to populate
>> the page table entries. On ARM64, PG_arch_1 is set to the target page
>> flag if execution permission is given. The page flag is kept when the
>> page is free'd to buddy's free area list. However, it will trigger page
>> checking failure when it's pulled from the buddy's free area list, as
>> the following warning messages indicate.
>>
>>     BUG: Bad page state in process memhog  pfn:08000
>>     page:0000000015c0a628 refcount:0 mapcount:0 \
>>          mapping:0000000000000000 index:0x1 pfn:0x8000
>>     flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff)
>>     raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000
>>     raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
>>     page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set
>>
>> This fixes the issue by clearing PG_arch_1 through flush_dcache_page()
>> after set_xxx_at() is called.
> 
> Could you please add comments before each flush_dcache_page() instance
> explaining why this is needed for arm64 platforms with relevant PG_arch_1
> context and how this does not have any adverse effect on other platforms ?
> It should be easy for some one looking at this code after a while to figure
> out from where flush_dcache_page() came from.
> 

Good point. I will improve chage log to include the commit ID in v4 where the
page flag (PG_arch_1) is used and explain how. In that case, it's much clearer
to understand the reason why we need flush_dcache_page() after set_xxx_at() on
ARM64.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag
  2021-07-21 12:03     ` Gavin Shan
@ 2021-07-22  3:51       ` Anshuman Khandual
  2021-07-22  6:54         ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  3:51 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Small nit:

s/Fix corrupted page flag/Fix page flag corruption on arm64/

On 7/21/21 5:33 PM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/21/21 8:18 PM, Anshuman Khandual wrote:
>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>> In page table entry modifying tests, set_xxx_at() are used to populate
>>> the page table entries. On ARM64, PG_arch_1 is set to the target page
>>> flag if execution permission is given. The page flag is kept when the
>>> page is free'd to buddy's free area list. However, it will trigger page
>>> checking failure when it's pulled from the buddy's free area list, as
>>> the following warning messages indicate.
>>>
>>>     BUG: Bad page state in process memhog  pfn:08000
>>>     page:0000000015c0a628 refcount:0 mapcount:0 \
>>>          mapping:0000000000000000 index:0x1 pfn:0x8000
>>>     flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff)
>>>     raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000
>>>     raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
>>>     page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set
>>>
>>> This fixes the issue by clearing PG_arch_1 through flush_dcache_page()
>>> after set_xxx_at() is called.
>>
>> Could you please add comments before each flush_dcache_page() instance
>> explaining why this is needed for arm64 platforms with relevant PG_arch_1
>> context and how this does not have any adverse effect on other platforms ?
>> It should be easy for some one looking at this code after a while to figure
>> out from where flush_dcache_page() came from.
>>
> 
> Good point. I will improve chage log to include the commit ID in v4 where the
> page flag (PG_arch_1) is used and explain how. In that case, it's much clearer
> to understand the reason why we need flush_dcache_page() after set_xxx_at() on
> ARM64.

But also some in code comments where flush_dcache_page() is being called.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-21 10:20     ` Gavin Shan
  2021-07-21 10:39       ` Anshuman Khandual
@ 2021-07-22  4:41       ` Anshuman Khandual
  2021-07-22  6:23         ` Gavin Shan
  1 sibling, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  4:41 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

On 7/21/21 3:50 PM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>> In debug_vm_pgtable(), there are many local variables introduced to
>>> track the needed information and they are passed to the functions for
>>> various test cases. It'd better to introduce a struct as place holder
>>> for these information. With it, what the functions for various test
>>> cases need is the struct, to simplify the code. It also makes code
>>> easier to be maintained.
>>>
>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>> in the page table modifying tests. So the accessed pages in the tests
>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>> that aren't owned by us. This causes issues like page flag corruption.
>>>
>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>> and destroyed, but the information in the struct isn't used yet. They
>>> will be used in subsequent patches.
>>>
>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>> ---
>>>   mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>   1 file changed, 196 insertions(+), 1 deletion(-)
>>>
> 
> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
> I will wait to integrate your comments to v4 until you finish the review
> on all patches in v3 series.
> 
>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>> index 1c922691aa61..ea153ff40d23 100644
>>> --- a/mm/debug_vm_pgtable.c
>>> +++ b/mm/debug_vm_pgtable.c
>>> @@ -58,6 +58,36 @@
>>>   #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
>>>   #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;
>>> +
>>> +    unsigned long        pud_pfn;
>>> +    unsigned long        pmd_pfn;
>>> +    unsigned long        pte_pfn;
>>> +
>>> +    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(unsigned long pfn, int idx)
>>>   {
>>>       pgprot_t prot = protection_map[idx];
>>> @@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
>>>       return random_vaddr;
>>>   }
>>>   +static void __init destroy_args(struct pgtable_debug_args *args)
>>> +{
>>> +    struct page *page = NULL;
>>> +
>>> +    /* Free (huge) page */
>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>> +        has_transparent_hugepage() &&
>>> +        args->pud_pfn != ULONG_MAX) {
>>> +        page = pfn_to_page(args->pud_pfn);
>>> +        __free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>> +    } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>> +           has_transparent_hugepage() &&
>>> +           args->pmd_pfn != ULONG_MAX) {
>>> +        page = pfn_to_page(args->pmd_pfn);
>>> +        __free_pages(page, HPAGE_PMD_ORDER);
>>> +    } else if (args->pte_pfn != ULONG_MAX) {
>>> +        page = pfn_to_page(args->pte_pfn);
>>> +        __free_pages(page, 0);
>>> +    }
>>> +
>>> +    /* Free page table */
>>> +    if (args->start_ptep) {
>>> +        pte_free(args->mm, args->start_ptep);
>>> +        mm_dec_nr_ptes(args->mm);
>>> +    }
>>> +
>>> +    if (args->start_pmdp) {
>>> +        pmd_free(args->mm, args->start_pmdp);
>>> +        mm_dec_nr_pmds(args->mm);
>>> +    }
>>> +
>>> +    if (args->start_pudp) {
>>> +        pud_free(args->mm, args->start_pudp);
>>> +        mm_dec_nr_puds(args->mm);
>>> +    }
>>> +
>>> +    if (args->start_p4dp)
>>> +        p4d_free(args->mm, args->p4dp);
>>> +
>>> +    /* Free vma and mm struct */
>>> +    if (args->vma)
>>> +        vm_area_free(args->vma);
>>> +    if (args->mm)
>>> +        mmdrop(args->mm);
>>> +}
>>> +
>>> +static int __init init_args(struct pgtable_debug_args *args)
>>> +{
>>> +    struct page *page = NULL;
>>> +    phys_addr_t phys;
>>> +    int ret = 0;
>>> +
>>> +    /* Initialize the debugging data */
>>> +    memset(args, 0, sizeof(*args));
>>> +    args->page_prot      = vm_get_page_prot(VMFLAGS);
>>> +    args->page_prot_none = __P000;
>>
>> Please preserve the existing comments before this assignment.
>>
>>          /*
>>           * __P000 (or even __S000) will help create page table entries with
>>           * PROT_NONE permission as required for pxx_protnone_tests().
>>           */
>>
> 
> Sure. I will combine the comments in v4 as below:
> 
>     /*
>      * Initialize the debugging arguments.
>      *
>      * __P000 (or even __S000) will help create page table entries with
>          * PROT_NONE permission as required for pxx_protnone_tests().
>          */
> 
> 
>>> +    args->pud_pfn        = ULONG_MAX;
>>> +    args->pmd_pfn        = ULONG_MAX;
>>> +    args->pte_pfn        = ULONG_MAX;
>>> +    args->fixed_pgd_pfn  = ULONG_MAX;
>>> +    args->fixed_p4d_pfn  = ULONG_MAX;
>>> +    args->fixed_pud_pfn  = ULONG_MAX;
>>> +    args->fixed_pmd_pfn  = ULONG_MAX;
>>> +    args->fixed_pte_pfn  = ULONG_MAX;
>>> +
>>> +    /* Allocate mm and vma */
>>> +    args->mm = mm_alloc();
>>> +    if (!args->mm) {
>>> +        pr_err("Failed to allocate mm struct\n");
>>> +        ret = -ENOMEM;
>>> +        goto error;
>>> +    }
>>> +
>>> +    args->vma = vm_area_alloc(args->mm);
>>> +    if (!args->vma) {
>>> +        pr_err("Failed to allocate vma\n");
>>> +        ret = -ENOMEM;
>>> +        goto error;
>>> +    }
>>> +
>>> +    /* Figure out the virtual address and allocate page table entries */
>>> +    args->vaddr = get_random_vaddr();
>>
>> Please group args->vaddr's init with page_prot and page_prot_none above.
>>
> 
> Yes, It will make the code tidy. I'll move this line accordingly in v4,
> but the related comments will be dropped as the code is self-explanatory.
> 
>         /* Allocate page table entries */
> 
>>> +    args->pgdp = pgd_offset(args->mm, args->vaddr);
>>> +    args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>>> +    args->pudp = args->p4dp ?
>>> +             pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
>>> +    args->pmdp = args->pudp ?
>>> +             pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
>>> +    args->ptep = args->pmdp ?
>>> +             pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
>>> +    if (!args->ptep) {
>>> +        pr_err("Failed to allocate page table\n");
>>> +        ret = -ENOMEM;
>>> +        goto error;
>>> +    }
>>
>> Why not just assert that all page table level pointers are allocated
>> successfully, otherwise bail out the test completely. Something like
>> this at each level.
>>
>>     if (!args->p4dp) {
>>         pr_err("Failed to allocate page table\n");
>>         ret = -ENOMEM;
>>         goto error;
>>     }
>>
>> Is there any value in proceeding with the test when some page table
>> pointers have not been allocated. Also individual tests do not cross
>> check these pointers. Also asserting successful allocations will
>> make the freeing path simpler, as I had mentioned earlier.
>>
> 
> There is no tests will be carried out if we fail to allocate any level
> of page table entries. For other questions, please refer below response.
> In summary, this snippet needs to be combined with next snippet, as below.
> 
>>> +
>>> +    /*
>>> +     * The above page table entries will be modified. Lets save the
>>> +     * page table entries so that they can be released when the tests
>>> +     * are completed.
>>> +     */
>>> +    args->start_p4dp = p4d_offset(args->pgdp, 0UL);
>>> +    args->start_pudp = pud_offset(args->p4dp, 0UL);
>>> +    args->start_pmdp = pmd_offset(args->pudp, 0UL);
>>> +    args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
>>
>> If the above page table pointers have been validated to be allocated
>> successfully, we could add these here.
>>
>>     WARN_ON(!args->start_p4dp)
>>     WARN_ON(!args->start_pudp)
>>     WARN_ON(!args->start_pmdp)
>>     WARN_ON(!args->start_ptep)
>>
>> Afterwards all those if (args->start_pxdp) checks in the freeing path
>> will not be required anymore.
>>
> 
> The check on @args->start_pxdp is still needed in destroy_args() for
> couple of cases: (1) destroy_args() is called on failing to allocate
> @args->mm or @args->vma. That time, no page table entries are allocated.
> (2) It's possible to fail allocating current level of page table entries
> even the previous levels of page table entries are allocated successfully.

This makes sense as destroy_args() is getting called if any of these
allocations fails during init_args(). Did not realize that earlier.

> 
> So Lets change these (above) two snippets as below in v4:
> 
>     /*
>      * Allocate page table entries. The allocated page table entries
>      * will be modified in the tests. Lets save the page table entries
>      * so that they can be released when the tests are completed.
>      */
>     args->pgdp = pgd_offset(args->mm, args->vaddr);
>     args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>     if (!args->p4dp) {
>         pr_err("Failed to allocate p4d entries\n");
>         ret = -ENOMEM;
>         goto error;
>     }
> 
>     args->start_p4dp = p4d_offset(args->pgdp, 0UL);

Dont bring the arg->start_pxdp assignments here. If all page table level
pointer allocations succeed, they all get assigned together like we have
right now. Although a sanity check afterwards like the following, might
still be better.

WARN_ON(!args->start_p4dp)
WARN_ON(!args->start_pudp)
WARN_ON(!args->start_pmdp)
WARN_ON(!args->start_ptep)

>     args->pudp = pud_alloc(args->mm, args->p4dp, args->vaddr);
>     if (!args->pudp) {
>         pr_err("Failed to allocate pud entries\n");
>         ret = -ENOMEM;
>         goto error;
>     }
> 
>     args->pmdp = pmd_alloc(args->mm, args->pudp, args->vaddr);
>     if (!args->pmdp) {
>         pr_err("Failed to allocate PMD entries\n");
>         ret = -ENOMEM;
>         goto error;
>     }
> 
>     args->start_pmdp = pmd_offset(args->pudp, 0UL);
>     args->ptep = pte_alloc_map(args->mm, args->pmdp, args->vaddr);
>     if (!args->ptep) {
>         pr_err("Failed to allocate page table\n");
>         ret = -ENOMEM;
>         goto error;
>     }
> 
>     args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
> 
>>> +
>>> +    /*
>>> +     * Figure out the fixed addresses, which are all around the kernel
>>> +     * symbol (@start_kernel). The corresponding PFNs might be invalid,
>>> +     * but it's fine as the following tests won't access the pages.
>>> +     */
>>> +    phys = __pa_symbol(&start_kernel);
>>> +    args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
>>> +    args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
>>> +    args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
>>> +    args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
>>> +    args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
>>> +
>>> +    /*
>>> +     * Allocate (huge) pages because some of the tests need to access
>>> +     * the data in the pages. The corresponding tests will be skipped
>>> +     * if we fail to allocate (huge) pages.
>>> +     */
>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>> +        has_transparent_hugepage()) {
>>> +        page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>>> +                   HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>
>> Please drop __GFP_NOWARN and instead use something like alloc_contig_pages()
>> when required allocation order exceed (MAX_ORDER - 1). Else the test might
>> not be able to execute on platform configurations, where PUD THP is enabled.
>>
> 
> Yes, It's correct that alloc_contig_pages() should be used here, depending
> on CONFIG_CONTIG_ALLOC. Otherwise, alloc_pages(...__GFP_NOWARN...) is still
> used as we're doing. This snippet will be changed like below in v4:

First 'order > (MAX_ORDER - 1)' needs to be established before calling into
alloc_contig_pages() without __GFP_NOWARN and set a new flag indicating that
there is contig page allocated. But if 'order <= (MAX_ORDER - 1)', then call
alloc_pages(..) without  __GFP_NOWARN. There is no need to add  __GFP_NOWARN
in any case. In case CONFIG_CONTIG_ALLOC is not available, directly return a
NULL as that would have been the case with alloc_pages(...__GFP_NOWARN...) as
well.

Symbol alloc_contig_pages() is not available outside CONFIG_CONTIG_ALLOC. So
IS_ENABLED() construct will not work, unless there is an empty stub added in
the header. Otherwise #ifdef CONFIG_CONTIG_ALLOC needs to be used instead.

Regardless please do test this on a x86 platform with PUD based THP in order
to make sure every thing works as expected.

> 
>     /*
>      * Allocate (huge) pages because some of the tests need to access
>      * the data in the pages. The corresponding tests will be skipped
>      * if we fail to allocate (huge) pages.
>      */
>     if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>         IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>         IS_ENABLED(CONFIG_CONTIG_ALLOC)) &&
>         has_transparent_hugepage()) {
>         page = alloc_contig_pages((1 << (HPAGE_PUD_SHIFT - PAGE_SHIFT)),
>                       GFP_KERNEL | __GFP_NOWARN,
>                       first_online_node, NULL);
>         if (page) {
>             args->is_contiguous_pud_page = true;
>             args->pud_pfn = page_to_pfn(page);
>             args->pmd_pfn = args->pud_pfn;
>             args->pte_pfn = args->pud_pfn;
>             return 0;
>         }
>     }
> 
>     if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>         IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>         has_transparent_hugepage()) {
>         page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>                    HPAGE_PUD_SHIFT - PAGE_SHIFT);
>         if (page) {
>             args->is_contiguous_pud_page = false;
>             args->pud_pfn = page_to_pfn(page);
>             args->pmd_pfn = args->pud_pfn;
>             args->pte_pfn = args->pud_pfn;
>             return 0;
>         }
>     }
> 
>     [... The logic to allocate PMD huge page or page is kept as of being]

IIRC it is also not guaranteed that PMD_SHIFT <= (MAX_ORDER - 1). Hence
this same scheme should be followed for PMD level allocation as well.

>     [... The code to release the PUD huge page needs changes based on @args->is_contiguous_pud_page]

Right, a flag would be needed to call the appropriate free function.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code
  2021-07-19 13:06 ` [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code Gavin Shan
@ 2021-07-22  4:51   ` Anshuman Khandual
  2021-07-22  6:53     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  4:51 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Small nit for the subject line.

s/Remove unused code/Remove unused page table debug elements/

On 7/19/21 6:36 PM, Gavin Shan wrote:
> The variables used by old implementation isn't needed as we switched
> to "struct pgtable_debug_args". Lets remove them and related code in
> debug_vm_pgtable().
> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 54 -------------------------------------------
>  1 file changed, 54 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 5ebacc940b68..4f7bf1c9724a 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -1124,14 +1124,6 @@ static int __init init_args(struct pgtable_debug_args *args)
>  static int __init debug_vm_pgtable(void)
>  {
>  	struct pgtable_debug_args args;
> -	struct vm_area_struct *vma;
> -	struct mm_struct *mm;
> -	pgd_t *pgdp;
> -	p4d_t *p4dp;
> -	pud_t *pudp;
> -	pmd_t *pmdp, *saved_pmdp, pmd;
> -	pgtable_t saved_ptep;
> -	unsigned long vaddr;
>  	spinlock_t *ptl = NULL;
>  	int idx, ret;
>  
> @@ -1140,41 +1132,6 @@ static int __init debug_vm_pgtable(void)
>  	if (ret)
>  		return ret;
>  
> -	vaddr = get_random_vaddr();
> -	mm = mm_alloc();
> -	if (!mm) {
> -		pr_err("mm_struct allocation failed\n");
> -		return 1;
> -	}
> -
> -	vma = vm_area_alloc(mm);
> -	if (!vma) {
> -		pr_err("vma allocation failed\n");
> -		return 1;
> -	}
> -
> -	pgdp = pgd_offset(mm, vaddr);
> -	p4dp = p4d_alloc(mm, pgdp, vaddr);
> -	pudp = pud_alloc(mm, p4dp, vaddr);
> -	pmdp = pmd_alloc(mm, pudp, vaddr);
> -	/*
> -	 * Allocate pgtable_t
> -	 */
> -	if (pte_alloc(mm, pmdp)) {
> -		pr_err("pgtable allocation failed\n");
> -		return 1;
> -	}
> -
> -	/*
> -	 * Save all the page table page addresses as the page table
> -	 * entries will be used for testing with random or garbage
> -	 * values. These saved addresses will be used for freeing
> -	 * page table pages.
> -	 */

Please move this comment as is to the right place inside init_args()
in the first patch itself. If possible all comments should be moved
during the first patch and just the code gets dropped here.

> -	pmd = READ_ONCE(*pmdp);
> -	saved_pmdp = pmd_offset(pudp, 0UL);
> -	saved_ptep = pmd_pgtable(pmd);
> -
>  	/*
>  	 * Iterate over the protection_map[] to make sure that all
>  	 * the basic page table transformation validations just hold
> @@ -1256,17 +1213,6 @@ static int __init debug_vm_pgtable(void)
>  	pgd_populate_tests(&args);
>  	spin_unlock(&(args.mm->page_table_lock));
>  
> -	p4d_free(mm, p4d_offset(pgdp, 0UL));
> -	pud_free(mm, pud_offset(p4dp, 0UL));
> -	pmd_free(mm, saved_pmdp);
> -	pte_free(mm, saved_ptep);
> -
> -	vm_area_free(vma);
> -	mm_dec_nr_puds(mm);
> -	mm_dec_nr_pmds(mm);
> -	mm_dec_nr_ptes(mm);
> -	mmdrop(mm);
> -
>  	destroy_args(&args);
>  	return 0;
>  }
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests
  2021-07-19 13:06 ` [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D " Gavin Shan
@ 2021-07-22  5:09   ` Anshuman Khandual
  2021-07-22  6:50     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  5:09 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in PGD/P4D modifying tests. No
> allocated huge page is used in these tests. Besides, the unused
> variable @saved_p4dp and @saved_pudp are dropped.

Please dont drop  @saved_p4dp and @saved_pudp just yet.

> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 86 +++++++++++++++++++------------------------
>  1 file changed, 38 insertions(+), 48 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 57b7ead0708b..5ebacc940b68 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -520,27 +520,26 @@ static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
>  #endif /* PAGETABLE_PUD_FOLDED */
>  
>  #ifndef __PAGETABLE_P4D_FOLDED
> -static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp)
> +static void __init p4d_clear_tests(struct pgtable_debug_args *args)
>  {
> -	p4d_t p4d = READ_ONCE(*p4dp);
> +	p4d_t p4d = READ_ONCE(*(args->p4dp));
>  
> -	if (mm_pud_folded(mm))
> +	if (mm_pud_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating P4D clear\n");
>  	p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE);
> -	WRITE_ONCE(*p4dp, p4d);
> -	p4d_clear(p4dp);
> -	p4d = READ_ONCE(*p4dp);
> +	WRITE_ONCE(*(args->p4dp), p4d);
> +	p4d_clear(args->p4dp);
> +	p4d = READ_ONCE(*(args->p4dp));
>  	WARN_ON(!p4d_none(p4d));
>  }
>  
> -static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
> -				      pud_t *pudp)
> +static void __init p4d_populate_tests(struct pgtable_debug_args *args)
>  {
>  	p4d_t p4d;
>  
> -	if (mm_pud_folded(mm))
> +	if (mm_pud_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating P4D populate\n");
> @@ -548,34 +547,33 @@ static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
>  	 * This entry points to next level page table page.
>  	 * Hence this must not qualify as p4d_bad().
>  	 */
> -	pud_clear(pudp);
> -	p4d_clear(p4dp);
> -	p4d_populate(mm, p4dp, pudp);
> -	p4d = READ_ONCE(*p4dp);
> +	pud_clear(args->pudp);
> +	p4d_clear(args->p4dp);
> +	p4d_populate(args->mm, args->p4dp, args->start_pudp);
> +	p4d = READ_ONCE(*(args->p4dp));
>  	WARN_ON(p4d_bad(p4d));
>  }
>  
> -static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp)
> +static void __init pgd_clear_tests(struct pgtable_debug_args *args)
>  {
> -	pgd_t pgd = READ_ONCE(*pgdp);
> +	pgd_t pgd = READ_ONCE(*(args->pgdp));
>  
> -	if (mm_p4d_folded(mm))
> +	if (mm_p4d_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating PGD clear\n");
>  	pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE);
> -	WRITE_ONCE(*pgdp, pgd);
> -	pgd_clear(pgdp);
> -	pgd = READ_ONCE(*pgdp);
> +	WRITE_ONCE(*(args->pgdp), pgd);
> +	pgd_clear(args->pgdp);
> +	pgd = READ_ONCE(*(args->pgdp));
>  	WARN_ON(!pgd_none(pgd));
>  }
>  
> -static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
> -				      p4d_t *p4dp)
> +static void __init pgd_populate_tests(struct pgtable_debug_args *args)
>  {
>  	pgd_t pgd;
>  
> -	if (mm_p4d_folded(mm))
> +	if (mm_p4d_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating PGD populate\n");
> @@ -583,23 +581,17 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>  	 * This entry points to next level page table page.
>  	 * Hence this must not qualify as pgd_bad().
>  	 */
> -	p4d_clear(p4dp);
> -	pgd_clear(pgdp);
> -	pgd_populate(mm, pgdp, p4dp);
> -	pgd = READ_ONCE(*pgdp);
> +	p4d_clear(args->p4dp);
> +	pgd_clear(args->pgdp);
> +	pgd_populate(args->mm, args->pgdp, args->start_p4dp);
> +	pgd = READ_ONCE(*(args->pgdp));
>  	WARN_ON(pgd_bad(pgd));
>  }
>  #else  /* !__PAGETABLE_P4D_FOLDED */
> -static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp) { }
> -static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) { }
> -static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
> -				      pud_t *pudp)
> -{
> -}
> -static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
> -				      p4d_t *p4dp)
> -{
> -}
> +static void __init p4d_clear_tests(struct pgtable_debug_args *args) { }
> +static void __init pgd_clear_tests(struct pgtable_debug_args *args) { }
> +static void __init p4d_populate_tests(struct pgtable_debug_args *args) { }
> +static void __init pgd_populate_tests(struct pgtable_debug_args *args) { }
>  #endif /* PAGETABLE_P4D_FOLDED */
>  
>  static void __init pte_clear_tests(struct pgtable_debug_args *args)
> @@ -1135,8 +1127,8 @@ static int __init debug_vm_pgtable(void)
>  	struct vm_area_struct *vma;
>  	struct mm_struct *mm;
>  	pgd_t *pgdp;
> -	p4d_t *p4dp, *saved_p4dp;
> -	pud_t *pudp, *saved_pudp;
> +	p4d_t *p4dp;
> +	pud_t *pudp;
>  	pmd_t *pmdp, *saved_pmdp, pmd;
>  	pgtable_t saved_ptep;
>  	unsigned long vaddr;
> @@ -1180,8 +1172,6 @@ static int __init debug_vm_pgtable(void)
>  	 * page table pages.
>  	 */
>  	pmd = READ_ONCE(*pmdp);
> -	saved_p4dp = p4d_offset(pgdp, 0UL);
> -	saved_pudp = pud_offset(p4dp, 0UL);
>  	saved_pmdp = pmd_offset(pudp, 0UL);
>  	saved_ptep = pmd_pgtable(pmd);
>  
> @@ -1259,15 +1249,15 @@ static int __init debug_vm_pgtable(void)
>  	pud_populate_tests(&args);
>  	spin_unlock(ptl);
>  
> -	spin_lock(&mm->page_table_lock);
> -	p4d_clear_tests(mm, p4dp);
> -	pgd_clear_tests(mm, pgdp);
> -	p4d_populate_tests(mm, p4dp, saved_pudp);
> -	pgd_populate_tests(mm, pgdp, saved_p4dp);
> -	spin_unlock(&mm->page_table_lock);
> +	spin_lock(&(args.mm->page_table_lock));
> +	p4d_clear_tests(&args);
> +	pgd_clear_tests(&args);
> +	p4d_populate_tests(&args);
> +	pgd_populate_tests(&args);
> +	spin_unlock(&(args.mm->page_table_lock));
>  
> -	p4d_free(mm, saved_p4dp);
> -	pud_free(mm, saved_pudp);
> +	p4d_free(mm, p4d_offset(pgdp, 0UL));
> +	pud_free(mm, pud_offset(p4dp, 0UL));

Please keep @saved_pudp and @saved_p4dp declaration, assignment and
usage unchanged for now. Drop them only during [PATCH 11/12]. So in
each patch like these, drop the elements only if there is an unused
warning during build.

There are two set of page table debug elements i.e old and new. The
test is transitioning from old to new. Even after the transition is
complete, the old elements are properly declared, initialized and
freed up. Entire old set should be dropped only in [PATCH 11/12].

>  	pmd_free(mm, saved_pmdp);
>  	pte_free(mm, saved_ptep);
>  
>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests
  2021-07-19 13:06 ` [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD " Gavin Shan
@ 2021-07-22  5:39   ` Anshuman Khandual
  2021-07-22  6:47     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  5:39 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin


On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in PUD modifying tests. The allocated
> huge page is used when set_pud_at() is used. The corresponding tests
> are skipped if the huge page doesn't exist. Besides, the following unused
> variables in debug_vm_pgtable() are dropped: @prot, @paddr, @pud_aligned.

Please dont drop @prot, @paddr, @pud_aligned just yet.

> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 130 ++++++++++++++++--------------------------
>  1 file changed, 50 insertions(+), 80 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index cec3cbf99a6b..57b7ead0708b 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -338,55 +338,55 @@ static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
>  	WARN_ON(!pud_bad(pud_mkhuge(pud)));
>  }
>  
> -static void __init pud_advanced_tests(struct mm_struct *mm,
> -				      struct vm_area_struct *vma, pud_t *pudp,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot)
> +static void __init pud_advanced_tests(struct pgtable_debug_args *args)
>  {
> +	unsigned long vaddr = (args->vaddr & HPAGE_PUD_MASK);
>  	pud_t pud;
>  
>  	if (!has_transparent_hugepage())
>  		return;
>  
>  	pr_debug("Validating PUD advanced\n");
> -	/* Align the address wrt HPAGE_PUD_SIZE */
> -	vaddr &= HPAGE_PUD_MASK;

Please just leave these unchanged. If has_transparent_hugepage() evaluates
negative, it skips the masking operation. As mentioned earlier please avoid
changing the test in any manner during these transition patches.

> +	if (args->pud_pfn == ULONG_MAX) {
> +		pr_debug("%s: Skipped\n", __func__);

Just return. Please dont call out "Skipped". Applicable for all transition
patches here.

> +		return;
> +	}
>  
> -	pud = pfn_pud(pfn, prot);
> -	set_pud_at(mm, vaddr, pudp, pud);
> -	pudp_set_wrprotect(mm, vaddr, pudp);
> -	pud = READ_ONCE(*pudp);
> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
> +	pudp_set_wrprotect(args->mm, vaddr, args->pudp);
> +	pud = READ_ONCE(*(args->pudp));

Seems like an extra braces while de-referencing arg->pudp. Could these be
dropped. Just READ_ONCE(*args->pudp).

>  	WARN_ON(pud_write(pud));
>  
>  #ifndef __PAGETABLE_PMD_FOLDED
> -	pudp_huge_get_and_clear(mm, vaddr, pudp);
> -	pud = READ_ONCE(*pudp);
> +	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(!pud_none(pud));
>  #endif /* __PAGETABLE_PMD_FOLDED */
> -	pud = pfn_pud(pfn, prot);
> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
>  	pud = pud_wrprotect(pud);
>  	pud = pud_mkclean(pud);
> -	set_pud_at(mm, vaddr, pudp, pud);
> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
>  	pud = pud_mkwrite(pud);
>  	pud = pud_mkdirty(pud);
> -	pudp_set_access_flags(vma, vaddr, pudp, pud, 1);
> -	pud = READ_ONCE(*pudp);
> +	pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(!(pud_write(pud) && pud_dirty(pud)));
>  
>  #ifndef __PAGETABLE_PMD_FOLDED
> -	pudp_huge_get_and_clear_full(mm, vaddr, pudp, 1);
> -	pud = READ_ONCE(*pudp);
> +	pudp_huge_get_and_clear_full(args->mm, vaddr, args->pudp, 1);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(!pud_none(pud));
>  #endif /* __PAGETABLE_PMD_FOLDED */
>  
> -	pud = pfn_pud(pfn, prot);
> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
>  	pud = pud_mkyoung(pud);
> -	set_pud_at(mm, vaddr, pudp, pud);
> -	pudp_test_and_clear_young(vma, vaddr, pudp);
> -	pud = READ_ONCE(*pudp);
> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
> +	pudp_test_and_clear_young(args->vma, vaddr, args->pudp);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(pud_young(pud));
>  
> -	pudp_huge_get_and_clear(mm, vaddr, pudp);
> +	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
>  }
>  
>  static void __init pud_leaf_tests(struct pgtable_debug_args *args)
> @@ -406,24 +406,14 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args)
>  }
>  #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 mm_struct *mm,
> -				      struct vm_area_struct *vma, pud_t *pudp,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot)
> -{
> -}
> +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 mm_struct *mm,
> -				      struct vm_area_struct *vma, pud_t *pudp,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot)
> -{
> -}
> +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) { }
>  static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
> @@ -450,11 +440,11 @@ static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>  	WARN_ON(!pmd_none(pmd));
>  }
>  
> -static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
> +static void __init pud_huge_tests(struct pgtable_debug_args *args)
>  {
>  	pud_t pud;
>  
> -	if (!arch_vmap_pud_supported(prot))
> +	if (!arch_vmap_pud_supported(args->page_prot))
>  		return;
>  
>  	pr_debug("Validating PUD huge\n");
> @@ -462,15 +452,16 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>  	 * X86 defined pud_set_huge() verifies that the given
>  	 * PUD is not a populated non-leaf entry.
>  	 */
> -	WRITE_ONCE(*pudp, __pud(0));
> -	WARN_ON(!pud_set_huge(pudp, __pfn_to_phys(pfn), prot));
> -	WARN_ON(!pud_clear_huge(pudp));
> -	pud = READ_ONCE(*pudp);
> +	WRITE_ONCE(*(args->pudp), __pud(0));
> +	WARN_ON(!pud_set_huge(args->pudp, __pfn_to_phys(args->fixed_pud_pfn),
> +			      args->page_prot));

Please dont break the line, we could go upto 100 width. Please do
the same for the entire series. Improves the readability.

> +	WARN_ON(!pud_clear_huge(args->pudp));
> +	pud = READ_ONCE(*(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(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
> +static void __init pud_huge_tests(struct pgtable_debug_args *args) { }
>  #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>  
>  static void __init p4d_basic_tests(void)
> @@ -492,27 +483,26 @@ static void __init pgd_basic_tests(void)
>  }
>  
>  #ifndef __PAGETABLE_PUD_FOLDED
> -static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp)
> +static void __init pud_clear_tests(struct pgtable_debug_args *args)
>  {
> -	pud_t pud = READ_ONCE(*pudp);
> +	pud_t pud = READ_ONCE(*(args->pudp));
>  
> -	if (mm_pmd_folded(mm))
> +	if (mm_pmd_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating PUD clear\n");
>  	pud = __pud(pud_val(pud) | RANDOM_ORVALUE);
> -	WRITE_ONCE(*pudp, pud);
> -	pud_clear(pudp);
> -	pud = READ_ONCE(*pudp);
> +	WRITE_ONCE(*(args->pudp), pud);
> +	pud_clear(args->pudp);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(!pud_none(pud));
>  }
>  
> -static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
> -				      pmd_t *pmdp)
> +static void __init pud_populate_tests(struct pgtable_debug_args *args)
>  {
>  	pud_t pud;
>  
> -	if (mm_pmd_folded(mm))
> +	if (mm_pmd_folded(args->mm))
>  		return;
>  
>  	pr_debug("Validating PUD populate\n");
> @@ -520,16 +510,13 @@ static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
>  	 * This entry points to next level page table page.
>  	 * Hence this must not qualify as pud_bad().
>  	 */
> -	pud_populate(mm, pudp, pmdp);
> -	pud = READ_ONCE(*pudp);
> +	pud_populate(args->mm, args->pudp, args->start_pmdp);
> +	pud = READ_ONCE(*(args->pudp));
>  	WARN_ON(pud_bad(pud));
>  }
>  #else  /* !__PAGETABLE_PUD_FOLDED */
> -static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp) { }
> -static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
> -				      pmd_t *pmdp)
> -{
> -}
> +static void __init pud_clear_tests(struct pgtable_debug_args *args) { }
> +static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
>  #endif /* PAGETABLE_PUD_FOLDED */
>  
>  #ifndef __PAGETABLE_P4D_FOLDED
> @@ -1152,10 +1139,7 @@ static int __init debug_vm_pgtable(void)
>  	pud_t *pudp, *saved_pudp;
>  	pmd_t *pmdp, *saved_pmdp, pmd;
>  	pgtable_t saved_ptep;
> -	pgprot_t prot;
> -	phys_addr_t paddr;
>  	unsigned long vaddr;
> -	unsigned long pud_aligned;
>  	spinlock_t *ptl = NULL;
>  	int idx, ret;
>  
> @@ -1164,7 +1148,6 @@ static int __init debug_vm_pgtable(void)
>  	if (ret)
>  		return ret;
>  
> -	prot = vm_get_page_prot(VMFLAGS);

Please dont drop these just yet and wait until [PATCH 11/12].

>  	vaddr = get_random_vaddr();
>  	mm = mm_alloc();
>  	if (!mm) {
> @@ -1178,19 +1161,6 @@ static int __init debug_vm_pgtable(void)
>  		return 1;
>  	}
>  
> -	/*
> -	 * PFN for mapping at PTE level is determined from a standard kernel
> -	 * text symbol. But pfns for higher page table levels are derived by
> -	 * masking lower bits of this real pfn. These derived pfns might not
> -	 * exist on the platform but that does not really matter as pfn_pxx()
> -	 * helpers will still create appropriate entries for the test. This
> -	 * helps avoid large memory block allocations to be used for mapping
> -	 * at higher page table levels.
> -	 */

Please move this comment as is to the right place inside init_args()
in the first patch itself. If possible all comments should be moved
during the first patch and just the code remains till [PATCH 11/12].

> -	paddr = __pa_symbol(&start_kernel);
> -
> -	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
> -

Please dont drop these just yet and wait until [PATCH 11/12].

>  	pgdp = pgd_offset(mm, vaddr);
>  	p4dp = p4d_alloc(mm, pgdp, vaddr);
>  	pudp = pud_alloc(mm, p4dp, vaddr);
> @@ -1282,11 +1252,11 @@ static int __init debug_vm_pgtable(void)
>  	pmd_populate_tests(&args);
>  	spin_unlock(ptl);
>  
> -	ptl = pud_lock(mm, pudp);
> -	pud_clear_tests(mm, pudp);
> -	pud_advanced_tests(mm, vma, pudp, pud_aligned, vaddr, prot);
> -	pud_huge_tests(pudp, pud_aligned, prot);
> -	pud_populate_tests(mm, pudp, saved_pmdp);
> +	ptl = pud_lock(args.mm, args.pudp);
> +	pud_clear_tests(&args);
> +	pud_advanced_tests(&args);
> +	pud_huge_tests(&args);
> +	pud_populate_tests(&args);
>  	spin_unlock(ptl);
>  
>  	spin_lock(&mm->page_table_lock);
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-19 13:06 ` [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD " Gavin Shan
@ 2021-07-22  5:45   ` Anshuman Khandual
  2021-07-22  6:41     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  5:45 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in PMD modifying tests. The allocated
> huge page is used when set_pmd_at() is used. The corresponding tests
> are skipped if the huge page doesn't exist. Besides, the unused variable
> @pmd_aligned in debug_vm_pgtable() is dropped.

Please dont drop @pmd_aligned just yet.

> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
>  1 file changed, 48 insertions(+), 54 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index eb6dda88e0d9..cec3cbf99a6b 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>  	WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>  }
>  
> -static void __init pmd_advanced_tests(struct mm_struct *mm,
> -				      struct vm_area_struct *vma, pmd_t *pmdp,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot, pgtable_t pgtable)
> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
>  {
>  	pmd_t pmd;
> +	unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
>  
>  	if (!has_transparent_hugepage())
>  		return;
>  
>  	pr_debug("Validating PMD advanced\n");
> -	/* Align the address wrt HPAGE_PMD_SIZE */
> -	vaddr &= HPAGE_PMD_MASK;

Please just leave these unchanged. If has_transparent_hugepage() evaluates
negative, it skips the masking operation. As mentioned earlier please avoid
changing the test in any manner during these transition patches.

> +	if (args->pmd_pfn == ULONG_MAX) {
> +		pr_debug("%s: Skipped\n", __func__);
> +		return;
> +	}

Just return. Please dont call out "Skipped".

>  
> -	pgtable_trans_huge_deposit(mm, pmdp, pgtable);
> +	pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
>  
> -	pmd = pfn_pmd(pfn, prot);
> -	set_pmd_at(mm, vaddr, pmdp, pmd);
> -	pmdp_set_wrprotect(mm, vaddr, pmdp);
> -	pmd = READ_ONCE(*pmdp);
> +	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
> +	pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(pmd_write(pmd));
> -	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
> -	pmd = READ_ONCE(*pmdp);
> +	pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(!pmd_none(pmd));
>  
> -	pmd = pfn_pmd(pfn, prot);
> +	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>  	pmd = pmd_wrprotect(pmd);
>  	pmd = pmd_mkclean(pmd);
> -	set_pmd_at(mm, vaddr, pmdp, pmd);
> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>  	pmd = pmd_mkwrite(pmd);
>  	pmd = pmd_mkdirty(pmd);
> -	pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
> -	pmd = READ_ONCE(*pmdp);
> +	pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
> -	pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
> -	pmd = READ_ONCE(*pmdp);
> +	pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(!pmd_none(pmd));
>  
> -	pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
> +	pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
>  	pmd = pmd_mkyoung(pmd);
> -	set_pmd_at(mm, vaddr, pmdp, pmd);
> -	pmdp_test_and_clear_young(vma, vaddr, pmdp);
> -	pmd = READ_ONCE(*pmdp);
> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
> +	pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(pmd_young(pmd));
>  
>  	/*  Clear the pte entries  */
> -	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
> -	pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
> +	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)
> @@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
>  #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 mm_struct *mm,
> -				      struct vm_area_struct *vma, pmd_t *pmdp,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot, pgtable_t pgtable)
> -{
> -}
> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
>  static void __init pud_advanced_tests(struct mm_struct *mm,
>  				      struct vm_area_struct *vma, pud_t *pudp,
>  				      unsigned long pfn, unsigned long vaddr,
> @@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
>  
>  #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
> +static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>  {
>  	pmd_t pmd;
>  
> -	if (!arch_vmap_pmd_supported(prot))
> +	if (!arch_vmap_pmd_supported(args->page_prot))
>  		return;
>  
>  	pr_debug("Validating PMD huge\n");
> @@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>  	 * X86 defined pmd_set_huge() verifies that the given
>  	 * PMD is not a populated non-leaf entry.
>  	 */
> -	WRITE_ONCE(*pmdp, __pmd(0));
> -	WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
> -	WARN_ON(!pmd_clear_huge(pmdp));
> -	pmd = READ_ONCE(*pmdp);
> +	WRITE_ONCE(*(args->pmdp), __pmd(0));

Possible extra braces.

> +	WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn),
> +			      args->page_prot));

Dont break the line.

> +	WARN_ON(!pmd_clear_huge(args->pmdp));
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(!pmd_none(pmd));
>  }
>  
> @@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>  	WARN_ON(!pud_none(pud));
>  }
>  #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
> +static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
>  static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>  #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>  
> @@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
>  	WARN_ON(!pte_none(pte));
>  }
>  
> -static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
> +static void __init pmd_clear_tests(struct pgtable_debug_args *args)
>  {
> -	pmd_t pmd = READ_ONCE(*pmdp);
> +	pmd_t pmd = READ_ONCE(*(args->pmdp));
>  
>  	pr_debug("Validating PMD clear\n");
>  	pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
> -	WRITE_ONCE(*pmdp, pmd);
> -	pmd_clear(pmdp);
> -	pmd = READ_ONCE(*pmdp);
> +	WRITE_ONCE(*(args->pmdp), pmd);
> +	pmd_clear(args->pmdp);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(!pmd_none(pmd));
>  }
>  
> -static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
> -				      pgtable_t pgtable)
> +static void __init pmd_populate_tests(struct pgtable_debug_args *args)
>  {
>  	pmd_t pmd;
>  
> @@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>  	 * This entry points to next level page table page.
>  	 * Hence this must not qualify as pmd_bad().
>  	 */
> -	pmd_populate(mm, pmdp, pgtable);
> -	pmd = READ_ONCE(*pmdp);
> +	pmd_populate(args->mm, args->pmdp, args->start_ptep);
> +	pmd = READ_ONCE(*(args->pmdp));
>  	WARN_ON(pmd_bad(pmd));
>  }
>  
> @@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>  	pgtable_t saved_ptep;
>  	pgprot_t prot;
>  	phys_addr_t paddr;
> -	unsigned long vaddr, pmd_aligned;
> +	unsigned long vaddr;
>  	unsigned long pud_aligned;
>  	spinlock_t *ptl = NULL;
>  	int idx, ret;
> @@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
>  	 */
>  	paddr = __pa_symbol(&start_kernel);
>  
> -	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;

Please dont drop these just yet and wait until [PATCH 11/12].

>  	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>  
>  	pgdp = pgd_offset(mm, vaddr);
> @@ -1281,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>  	pte_advanced_tests(&args);
>  	spin_unlock(ptl);
>  
> -	ptl = pmd_lock(mm, pmdp);
> -	pmd_clear_tests(mm, pmdp);
> -	pmd_advanced_tests(mm, vma, pmdp, pmd_aligned, vaddr, prot, saved_ptep);
> -	pmd_huge_tests(pmdp, pmd_aligned, prot);
> -	pmd_populate_tests(mm, pmdp, saved_ptep);
> +	ptl = pmd_lock(args.mm, args.pmdp);
> +	pmd_clear_tests(&args);
> +	pmd_advanced_tests(&args);
> +	pmd_huge_tests(&args);
> +	pmd_populate_tests(&args);
>  	spin_unlock(ptl);
>  
>  	ptl = pud_lock(mm, pudp);
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests
  2021-07-19 13:06 ` [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests Gavin Shan
@ 2021-07-22  5:56   ` Anshuman Khandual
  2021-07-22  6:37     ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  5:56 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in PTE modifying tests. The allocated
> page is used as set_pte_at() is used there. The tests are skipped if
> the allocated page doesn't exist. Besides, the unused variable @ptep
> and @pte_aligned in debug_vm_pgtable() are dropped.

Please dont drop @ptep and @pte_aligned just yet.

> 
> Signed-off-by: Gavin Shan <gshan@redhat.com>
> ---
>  mm/debug_vm_pgtable.c | 75 ++++++++++++++++++++++---------------------
>  1 file changed, 39 insertions(+), 36 deletions(-)
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index d32e55a95c55..eb6dda88e0d9 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -116,10 +116,7 @@ static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
>  	WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte))));
>  }
>  
> -static void __init pte_advanced_tests(struct mm_struct *mm,
> -				      struct vm_area_struct *vma, pte_t *ptep,
> -				      unsigned long pfn, unsigned long vaddr,
> -				      pgprot_t prot)
> +static void __init pte_advanced_tests(struct pgtable_debug_args *args)
>  {
>  	pte_t pte;
>  
> @@ -130,33 +127,38 @@ static void __init pte_advanced_tests(struct mm_struct *mm,
>  	 */
>  
>  	pr_debug("Validating PTE advanced\n");
> -	pte = pfn_pte(pfn, prot);
> -	set_pte_at(mm, vaddr, ptep, pte);
> -	ptep_set_wrprotect(mm, vaddr, ptep);
> -	pte = ptep_get(ptep);
> +	if (args->pte_pfn == ULONG_MAX) {
> +		pr_debug("%s: Skipped\n", __func__);
> +		return;
> +	}

Just return. Please dont call out "Skipped". Also this check should be
performed before printing pr_debug("Validating PTE advanced\n"). The
print indicates that the test has started.

> +
> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
> +	ptep_set_wrprotect(args->mm, args->vaddr, args->ptep);
> +	pte = ptep_get(args->ptep);
>  	WARN_ON(pte_write(pte));
> -	ptep_get_and_clear(mm, vaddr, ptep);
> -	pte = ptep_get(ptep);
> +	ptep_get_and_clear(args->mm, args->vaddr, args->ptep);
> +	pte = ptep_get(args->ptep);
>  	WARN_ON(!pte_none(pte));
>  
> -	pte = pfn_pte(pfn, prot);
> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
>  	pte = pte_wrprotect(pte);
>  	pte = pte_mkclean(pte);
> -	set_pte_at(mm, vaddr, ptep, pte);
> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>  	pte = pte_mkwrite(pte);
>  	pte = pte_mkdirty(pte);
> -	ptep_set_access_flags(vma, vaddr, ptep, pte, 1);
> -	pte = ptep_get(ptep);
> +	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(mm, vaddr, ptep, 1);
> -	pte = ptep_get(ptep);
> +	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(pfn, prot);
> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
>  	pte = pte_mkyoung(pte);
> -	set_pte_at(mm, vaddr, ptep, pte);
> -	ptep_test_and_clear_young(vma, vaddr, ptep);
> -	pte = ptep_get(ptep);
> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
> +	ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep);
> +	pte = ptep_get(args->ptep);
>  	WARN_ON(pte_young(pte));
>  }
>  
> @@ -617,20 +619,24 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>  }
>  #endif /* PAGETABLE_P4D_FOLDED */
>  
> -static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep,
> -				   unsigned long pfn, unsigned long vaddr,
> -				   pgprot_t prot)
> +static void __init pte_clear_tests(struct pgtable_debug_args *args)
>  {
> -	pte_t pte = pfn_pte(pfn, prot);
> +	pte_t pte;
>  
>  	pr_debug("Validating PTE clear\n");
> +	if (args->pte_pfn == ULONG_MAX) {
> +		pr_debug("%s: Skipped\n", __func__);
> +		return;
> +	}

Just return. Please dont call out "Skipped". Also this check should be
performed before printing pr_debug("Validating PTE clear\n"). The print
indicates that the test has started.

> +
> +	pte = pfn_pte(args->pte_pfn, args->page_prot);

Please keep this unchanged and move to its original position.

>  #ifndef CONFIG_RISCV
>  	pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
>  #endif
> -	set_pte_at(mm, vaddr, ptep, pte);
> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>  	barrier();
> -	pte_clear(mm, vaddr, ptep);
> -	pte = ptep_get(ptep);
> +	pte_clear(args->mm, args->vaddr, args->ptep);
> +	pte = ptep_get(args->ptep);
>  	WARN_ON(!pte_none(pte));
>  }
>  
> @@ -1150,11 +1156,10 @@ static int __init debug_vm_pgtable(void)
>  	p4d_t *p4dp, *saved_p4dp;
>  	pud_t *pudp, *saved_pudp;
>  	pmd_t *pmdp, *saved_pmdp, pmd;
> -	pte_t *ptep;
>  	pgtable_t saved_ptep;
>  	pgprot_t prot;
>  	phys_addr_t paddr;
> -	unsigned long vaddr, pte_aligned, pmd_aligned;
> +	unsigned long vaddr, pmd_aligned;
>  	unsigned long pud_aligned;
>  	spinlock_t *ptl = NULL;
>  	int idx, ret;
> @@ -1189,10 +1194,8 @@ static int __init debug_vm_pgtable(void)
>  	 */
>  	paddr = __pa_symbol(&start_kernel);
>  
> -	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;

Please dont drop pte_aligned just yet.

>  	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>  	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
> -	WARN_ON(!pfn_valid(pte_aligned));

This should go into init_args() at the right place as the following,
after evaluating it from 'start_kernel' symbol - just to be sure.

WARN_ON(!pfn_valid(args->fixed_pte_pfn))

>  
>  	pgdp = pgd_offset(mm, vaddr);
>  	p4dp = p4d_alloc(mm, pgdp, vaddr);
> @@ -1272,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>  	 * Page table modifying tests. They need to hold
>  	 * proper page table lock.
>  	 */
> -
> -	ptep = pte_offset_map_lock(mm, pmdp, vaddr, &ptl);
> -	pte_clear_tests(mm, ptep, pte_aligned, vaddr, prot);
> -	pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
> -	pte_unmap_unlock(ptep, ptl);
> +	ptl = pte_lockptr(args.mm, args.pmdp);
> +	spin_lock(ptl);
> +	pte_clear_tests(&args);
> +	pte_advanced_tests(&args);
> +	spin_unlock(ptl);

Why pte_offset_map_lock()/pte_unmap_unlock() has been dropped and
spin_lock()/spin_unlock() sequence has been added ? Please dont
change the tests in these patches.

>  
>  	ptl = pmd_lock(mm, pmdp);
>  	pmd_clear_tests(mm, pmdp);
>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-22  4:41       ` Anshuman Khandual
@ 2021-07-22  6:23         ` Gavin Shan
  2021-07-22  7:08           ` Anshuman Khandual
  0 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:23 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 2:41 PM, Anshuman Khandual wrote:
> On 7/21/21 3:50 PM, Gavin Shan wrote:
>> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>> In debug_vm_pgtable(), there are many local variables introduced to
>>>> track the needed information and they are passed to the functions for
>>>> various test cases. It'd better to introduce a struct as place holder
>>>> for these information. With it, what the functions for various test
>>>> cases need is the struct, to simplify the code. It also makes code
>>>> easier to be maintained.
>>>>
>>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>>> in the page table modifying tests. So the accessed pages in the tests
>>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>>> that aren't owned by us. This causes issues like page flag corruption.
>>>>
>>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>>> and destroyed, but the information in the struct isn't used yet. They
>>>> will be used in subsequent patches.
>>>>
>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>> ---
>>>>    mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>>    1 file changed, 196 insertions(+), 1 deletion(-)
>>>>
>>
>> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
>> I will wait to integrate your comments to v4 until you finish the review
>> on all patches in v3 series.
>>
>>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>>> index 1c922691aa61..ea153ff40d23 100644
>>>> --- a/mm/debug_vm_pgtable.c
>>>> +++ b/mm/debug_vm_pgtable.c
>>>> @@ -58,6 +58,36 @@
>>>>    #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
>>>>    #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;
>>>> +
>>>> +    unsigned long        pud_pfn;
>>>> +    unsigned long        pmd_pfn;
>>>> +    unsigned long        pte_pfn;
>>>> +
>>>> +    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(unsigned long pfn, int idx)
>>>>    {
>>>>        pgprot_t prot = protection_map[idx];
>>>> @@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
>>>>        return random_vaddr;
>>>>    }
>>>>    +static void __init destroy_args(struct pgtable_debug_args *args)
>>>> +{
>>>> +    struct page *page = NULL;
>>>> +
>>>> +    /* Free (huge) page */
>>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>> +        has_transparent_hugepage() &&
>>>> +        args->pud_pfn != ULONG_MAX) {
>>>> +        page = pfn_to_page(args->pud_pfn);
>>>> +        __free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>>> +    } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>> +           has_transparent_hugepage() &&
>>>> +           args->pmd_pfn != ULONG_MAX) {
>>>> +        page = pfn_to_page(args->pmd_pfn);
>>>> +        __free_pages(page, HPAGE_PMD_ORDER);
>>>> +    } else if (args->pte_pfn != ULONG_MAX) {
>>>> +        page = pfn_to_page(args->pte_pfn);
>>>> +        __free_pages(page, 0);
>>>> +    }
>>>> +
>>>> +    /* Free page table */
>>>> +    if (args->start_ptep) {
>>>> +        pte_free(args->mm, args->start_ptep);
>>>> +        mm_dec_nr_ptes(args->mm);
>>>> +    }
>>>> +
>>>> +    if (args->start_pmdp) {
>>>> +        pmd_free(args->mm, args->start_pmdp);
>>>> +        mm_dec_nr_pmds(args->mm);
>>>> +    }
>>>> +
>>>> +    if (args->start_pudp) {
>>>> +        pud_free(args->mm, args->start_pudp);
>>>> +        mm_dec_nr_puds(args->mm);
>>>> +    }
>>>> +
>>>> +    if (args->start_p4dp)
>>>> +        p4d_free(args->mm, args->p4dp);
>>>> +
>>>> +    /* Free vma and mm struct */
>>>> +    if (args->vma)
>>>> +        vm_area_free(args->vma);
>>>> +    if (args->mm)
>>>> +        mmdrop(args->mm);
>>>> +}
>>>> +
>>>> +static int __init init_args(struct pgtable_debug_args *args)
>>>> +{
>>>> +    struct page *page = NULL;
>>>> +    phys_addr_t phys;
>>>> +    int ret = 0;
>>>> +
>>>> +    /* Initialize the debugging data */
>>>> +    memset(args, 0, sizeof(*args));
>>>> +    args->page_prot      = vm_get_page_prot(VMFLAGS);
>>>> +    args->page_prot_none = __P000;
>>>
>>> Please preserve the existing comments before this assignment.
>>>
>>>           /*
>>>            * __P000 (or even __S000) will help create page table entries with
>>>            * PROT_NONE permission as required for pxx_protnone_tests().
>>>            */
>>>
>>
>> Sure. I will combine the comments in v4 as below:
>>
>>      /*
>>       * Initialize the debugging arguments.
>>       *
>>       * __P000 (or even __S000) will help create page table entries with
>>           * PROT_NONE permission as required for pxx_protnone_tests().
>>           */
>>
>>
>>>> +    args->pud_pfn        = ULONG_MAX;
>>>> +    args->pmd_pfn        = ULONG_MAX;
>>>> +    args->pte_pfn        = ULONG_MAX;
>>>> +    args->fixed_pgd_pfn  = ULONG_MAX;
>>>> +    args->fixed_p4d_pfn  = ULONG_MAX;
>>>> +    args->fixed_pud_pfn  = ULONG_MAX;
>>>> +    args->fixed_pmd_pfn  = ULONG_MAX;
>>>> +    args->fixed_pte_pfn  = ULONG_MAX;
>>>> +
>>>> +    /* Allocate mm and vma */
>>>> +    args->mm = mm_alloc();
>>>> +    if (!args->mm) {
>>>> +        pr_err("Failed to allocate mm struct\n");
>>>> +        ret = -ENOMEM;
>>>> +        goto error;
>>>> +    }
>>>> +
>>>> +    args->vma = vm_area_alloc(args->mm);
>>>> +    if (!args->vma) {
>>>> +        pr_err("Failed to allocate vma\n");
>>>> +        ret = -ENOMEM;
>>>> +        goto error;
>>>> +    }
>>>> +
>>>> +    /* Figure out the virtual address and allocate page table entries */
>>>> +    args->vaddr = get_random_vaddr();
>>>
>>> Please group args->vaddr's init with page_prot and page_prot_none above.
>>>
>>
>> Yes, It will make the code tidy. I'll move this line accordingly in v4,
>> but the related comments will be dropped as the code is self-explanatory.
>>
>>          /* Allocate page table entries */
>>
>>>> +    args->pgdp = pgd_offset(args->mm, args->vaddr);
>>>> +    args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>>>> +    args->pudp = args->p4dp ?
>>>> +             pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
>>>> +    args->pmdp = args->pudp ?
>>>> +             pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
>>>> +    args->ptep = args->pmdp ?
>>>> +             pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
>>>> +    if (!args->ptep) {
>>>> +        pr_err("Failed to allocate page table\n");
>>>> +        ret = -ENOMEM;
>>>> +        goto error;
>>>> +    }
>>>
>>> Why not just assert that all page table level pointers are allocated
>>> successfully, otherwise bail out the test completely. Something like
>>> this at each level.
>>>
>>>      if (!args->p4dp) {
>>>          pr_err("Failed to allocate page table\n");
>>>          ret = -ENOMEM;
>>>          goto error;
>>>      }
>>>
>>> Is there any value in proceeding with the test when some page table
>>> pointers have not been allocated. Also individual tests do not cross
>>> check these pointers. Also asserting successful allocations will
>>> make the freeing path simpler, as I had mentioned earlier.
>>>
>>
>> There is no tests will be carried out if we fail to allocate any level
>> of page table entries. For other questions, please refer below response.
>> In summary, this snippet needs to be combined with next snippet, as below.
>>
>>>> +
>>>> +    /*
>>>> +     * The above page table entries will be modified. Lets save the
>>>> +     * page table entries so that they can be released when the tests
>>>> +     * are completed.
>>>> +     */
>>>> +    args->start_p4dp = p4d_offset(args->pgdp, 0UL);
>>>> +    args->start_pudp = pud_offset(args->p4dp, 0UL);
>>>> +    args->start_pmdp = pmd_offset(args->pudp, 0UL);
>>>> +    args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
>>>
>>> If the above page table pointers have been validated to be allocated
>>> successfully, we could add these here.
>>>
>>>      WARN_ON(!args->start_p4dp)
>>>      WARN_ON(!args->start_pudp)
>>>      WARN_ON(!args->start_pmdp)
>>>      WARN_ON(!args->start_ptep)
>>>
>>> Afterwards all those if (args->start_pxdp) checks in the freeing path
>>> will not be required anymore.
>>>
>>
>> The check on @args->start_pxdp is still needed in destroy_args() for
>> couple of cases: (1) destroy_args() is called on failing to allocate
>> @args->mm or @args->vma. That time, no page table entries are allocated.
>> (2) It's possible to fail allocating current level of page table entries
>> even the previous levels of page table entries are allocated successfully.
> 
> This makes sense as destroy_args() is getting called if any of these
> allocations fails during init_args(). Did not realize that earlier.
> 
>>
>> So Lets change these (above) two snippets as below in v4:
>>
>>      /*
>>       * Allocate page table entries. The allocated page table entries
>>       * will be modified in the tests. Lets save the page table entries
>>       * so that they can be released when the tests are completed.
>>       */
>>      args->pgdp = pgd_offset(args->mm, args->vaddr);
>>      args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>>      if (!args->p4dp) {
>>          pr_err("Failed to allocate p4d entries\n");
>>          ret = -ENOMEM;
>>          goto error;
>>      }
>>
>>      args->start_p4dp = p4d_offset(args->pgdp, 0UL);
> 
> Dont bring the arg->start_pxdp assignments here. If all page table level
> pointer allocations succeed, they all get assigned together like we have
> right now. Although a sanity check afterwards like the following, might
> still be better.
> 
> WARN_ON(!args->start_p4dp)
> WARN_ON(!args->start_pudp)
> WARN_ON(!args->start_pmdp)
> WARN_ON(!args->start_ptep)
> 

We have to assign arg->start_pxdp here because destroy_args() relies
it to release the corresponding page tables in failing path. For example,
the args->start_p4dp is going to be release if we fail to populate
args->start_pudp.

Ok. I will add WARN_ON() for each level of page table entries right after
they are assigned in v4.

>>      args->pudp = pud_alloc(args->mm, args->p4dp, args->vaddr);
>>      if (!args->pudp) {
>>          pr_err("Failed to allocate pud entries\n");
>>          ret = -ENOMEM;
>>          goto error;
>>      }
>>
>>      args->pmdp = pmd_alloc(args->mm, args->pudp, args->vaddr);
>>      if (!args->pmdp) {
>>          pr_err("Failed to allocate PMD entries\n");
>>          ret = -ENOMEM;
>>          goto error;
>>      }
>>
>>      args->start_pmdp = pmd_offset(args->pudp, 0UL);
>>      args->ptep = pte_alloc_map(args->mm, args->pmdp, args->vaddr);
>>      if (!args->ptep) {
>>          pr_err("Failed to allocate page table\n");
>>          ret = -ENOMEM;
>>          goto error;
>>      }
>>
>>      args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
>>
>>>> +
>>>> +    /*
>>>> +     * Figure out the fixed addresses, which are all around the kernel
>>>> +     * symbol (@start_kernel). The corresponding PFNs might be invalid,
>>>> +     * but it's fine as the following tests won't access the pages.
>>>> +     */
>>>> +    phys = __pa_symbol(&start_kernel);
>>>> +    args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
>>>> +    args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
>>>> +    args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
>>>> +    args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
>>>> +    args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
>>>> +
>>>> +    /*
>>>> +     * Allocate (huge) pages because some of the tests need to access
>>>> +     * the data in the pages. The corresponding tests will be skipped
>>>> +     * if we fail to allocate (huge) pages.
>>>> +     */
>>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>> +        has_transparent_hugepage()) {
>>>> +        page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>>>> +                   HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>>
>>> Please drop __GFP_NOWARN and instead use something like alloc_contig_pages()
>>> when required allocation order exceed (MAX_ORDER - 1). Else the test might
>>> not be able to execute on platform configurations, where PUD THP is enabled.
>>>
>>
>> Yes, It's correct that alloc_contig_pages() should be used here, depending
>> on CONFIG_CONTIG_ALLOC. Otherwise, alloc_pages(...__GFP_NOWARN...) is still
>> used as we're doing. This snippet will be changed like below in v4:
> 
> First 'order > (MAX_ORDER - 1)' needs to be established before calling into
> alloc_contig_pages() without __GFP_NOWARN and set a new flag indicating that
> there is contig page allocated. But if 'order <= (MAX_ORDER - 1)', then call
> alloc_pages(..) without  __GFP_NOWARN. There is no need to add  __GFP_NOWARN
> in any case. In case CONFIG_CONTIG_ALLOC is not available, directly return a
> NULL as that would have been the case with alloc_pages(...__GFP_NOWARN...) as
> well.
> 
> Symbol alloc_contig_pages() is not available outside CONFIG_CONTIG_ALLOC. So
> IS_ENABLED() construct will not work, unless there is an empty stub added in
> the header. Otherwise #ifdef CONFIG_CONTIG_ALLOC needs to be used instead.
> 
> Regardless please do test this on a x86 platform with PUD based THP in order
> to make sure every thing works as expected.
> 

Thanks, I will change the code accordingly in v4 and test it on x86
before posting it.

>>
>>      /*
>>       * Allocate (huge) pages because some of the tests need to access
>>       * the data in the pages. The corresponding tests will be skipped
>>       * if we fail to allocate (huge) pages.
>>       */
>>      if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>          IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>          IS_ENABLED(CONFIG_CONTIG_ALLOC)) &&
>>          has_transparent_hugepage()) {
>>          page = alloc_contig_pages((1 << (HPAGE_PUD_SHIFT - PAGE_SHIFT)),
>>                        GFP_KERNEL | __GFP_NOWARN,
>>                        first_online_node, NULL);
>>          if (page) {
>>              args->is_contiguous_pud_page = true;
>>              args->pud_pfn = page_to_pfn(page);
>>              args->pmd_pfn = args->pud_pfn;
>>              args->pte_pfn = args->pud_pfn;
>>              return 0;
>>          }
>>      }
>>
>>      if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>          IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>          has_transparent_hugepage()) {
>>          page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>>                     HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>          if (page) {
>>              args->is_contiguous_pud_page = false;
>>              args->pud_pfn = page_to_pfn(page);
>>              args->pmd_pfn = args->pud_pfn;
>>              args->pte_pfn = args->pud_pfn;
>>              return 0;
>>          }
>>      }
>>
>>      [... The logic to allocate PMD huge page or page is kept as of being]
> 
> IIRC it is also not guaranteed that PMD_SHIFT <= (MAX_ORDER - 1). Hence
> this same scheme should be followed for PMD level allocation as well.
> 

In theory, it's possible to have PMD_SHIFT <= (MAX_ORDER - 1) with misconfigured
kernel. I will apply the similar logic to PMD huge page in v4.

>>      [... The code to release the PUD huge page needs changes based on @args->is_contiguous_pud_page]
> 
> Right, a flag would be needed to call the appropriate free function.
> 

Yes. We need two falgs for PUD and PMD huge pages separately.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests
  2021-07-22  5:56   ` Anshuman Khandual
@ 2021-07-22  6:37     ` Gavin Shan
  2021-07-23  2:39       ` Anshuman Khandual
  0 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:37 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 3:56 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> This uses struct pgtable_debug_args in PTE modifying tests. The allocated
>> page is used as set_pte_at() is used there. The tests are skipped if
>> the allocated page doesn't exist. Besides, the unused variable @ptep
>> and @pte_aligned in debug_vm_pgtable() are dropped.
> 
> Please dont drop @ptep and @pte_aligned just yet.
> 

We need to do so. Otherwise, there are build warning raised to
complain something like 'unused variable'.

>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 75 ++++++++++++++++++++++---------------------
>>   1 file changed, 39 insertions(+), 36 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index d32e55a95c55..eb6dda88e0d9 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -116,10 +116,7 @@ static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx)
>>   	WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte))));
>>   }
>>   
>> -static void __init pte_advanced_tests(struct mm_struct *mm,
>> -				      struct vm_area_struct *vma, pte_t *ptep,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot)
>> +static void __init pte_advanced_tests(struct pgtable_debug_args *args)
>>   {
>>   	pte_t pte;
>>   
>> @@ -130,33 +127,38 @@ static void __init pte_advanced_tests(struct mm_struct *mm,
>>   	 */
>>   
>>   	pr_debug("Validating PTE advanced\n");
>> -	pte = pfn_pte(pfn, prot);
>> -	set_pte_at(mm, vaddr, ptep, pte);
>> -	ptep_set_wrprotect(mm, vaddr, ptep);
>> -	pte = ptep_get(ptep);
>> +	if (args->pte_pfn == ULONG_MAX) {
>> +		pr_debug("%s: Skipped\n", __func__);
>> +		return;
>> +	}
> 
> Just return. Please dont call out "Skipped". Also this check should be
> performed before printing pr_debug("Validating PTE advanced\n"). The
> print indicates that the test has started.
> 

Sure.

>> +
>> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
>> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>> +	ptep_set_wrprotect(args->mm, args->vaddr, args->ptep);
>> +	pte = ptep_get(args->ptep);
>>   	WARN_ON(pte_write(pte));
>> -	ptep_get_and_clear(mm, vaddr, ptep);
>> -	pte = ptep_get(ptep);
>> +	ptep_get_and_clear(args->mm, args->vaddr, args->ptep);
>> +	pte = ptep_get(args->ptep);
>>   	WARN_ON(!pte_none(pte));
>>   
>> -	pte = pfn_pte(pfn, prot);
>> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
>>   	pte = pte_wrprotect(pte);
>>   	pte = pte_mkclean(pte);
>> -	set_pte_at(mm, vaddr, ptep, pte);
>> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>>   	pte = pte_mkwrite(pte);
>>   	pte = pte_mkdirty(pte);
>> -	ptep_set_access_flags(vma, vaddr, ptep, pte, 1);
>> -	pte = ptep_get(ptep);
>> +	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(mm, vaddr, ptep, 1);
>> -	pte = ptep_get(ptep);
>> +	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(pfn, prot);
>> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
>>   	pte = pte_mkyoung(pte);
>> -	set_pte_at(mm, vaddr, ptep, pte);
>> -	ptep_test_and_clear_young(vma, vaddr, ptep);
>> -	pte = ptep_get(ptep);
>> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>> +	ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep);
>> +	pte = ptep_get(args->ptep);
>>   	WARN_ON(pte_young(pte));
>>   }
>>   
>> @@ -617,20 +619,24 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>>   }
>>   #endif /* PAGETABLE_P4D_FOLDED */
>>   
>> -static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep,
>> -				   unsigned long pfn, unsigned long vaddr,
>> -				   pgprot_t prot)
>> +static void __init pte_clear_tests(struct pgtable_debug_args *args)
>>   {
>> -	pte_t pte = pfn_pte(pfn, prot);
>> +	pte_t pte;
>>   
>>   	pr_debug("Validating PTE clear\n");
>> +	if (args->pte_pfn == ULONG_MAX) {
>> +		pr_debug("%s: Skipped\n", __func__);
>> +		return;
>> +	}
> 
> Just return. Please dont call out "Skipped". Also this check should be
> performed before printing pr_debug("Validating PTE clear\n"). The print
> indicates that the test has started.
> 

Sure.

>> +
>> +	pte = pfn_pte(args->pte_pfn, args->page_prot);
> 
> Please keep this unchanged and move to its original position.
> 

Ok.

>>   #ifndef CONFIG_RISCV
>>   	pte = __pte(pte_val(pte) | RANDOM_ORVALUE);
>>   #endif
>> -	set_pte_at(mm, vaddr, ptep, pte);
>> +	set_pte_at(args->mm, args->vaddr, args->ptep, pte);
>>   	barrier();
>> -	pte_clear(mm, vaddr, ptep);
>> -	pte = ptep_get(ptep);
>> +	pte_clear(args->mm, args->vaddr, args->ptep);
>> +	pte = ptep_get(args->ptep);
>>   	WARN_ON(!pte_none(pte));
>>   }
>>   
>> @@ -1150,11 +1156,10 @@ static int __init debug_vm_pgtable(void)
>>   	p4d_t *p4dp, *saved_p4dp;
>>   	pud_t *pudp, *saved_pudp;
>>   	pmd_t *pmdp, *saved_pmdp, pmd;
>> -	pte_t *ptep;
>>   	pgtable_t saved_ptep;
>>   	pgprot_t prot;
>>   	phys_addr_t paddr;
>> -	unsigned long vaddr, pte_aligned, pmd_aligned;
>> +	unsigned long vaddr, pmd_aligned;
>>   	unsigned long pud_aligned;
>>   	spinlock_t *ptl = NULL;
>>   	int idx, ret;
>> @@ -1189,10 +1194,8 @@ static int __init debug_vm_pgtable(void)
>>   	 */
>>   	paddr = __pa_symbol(&start_kernel);
>>   
>> -	pte_aligned = (paddr & PAGE_MASK) >> PAGE_SHIFT;
> 
> Please dont drop pte_aligned just yet.
> 

We need to drop the variable. Otherwise, there is build warning to
complain: 'unused variable'.

>>   	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>>   	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>> -	WARN_ON(!pfn_valid(pte_aligned));
> 
> This should go into init_args() at the right place as the following,
> after evaluating it from 'start_kernel' symbol - just to be sure.
> 
> WARN_ON(!pfn_valid(args->fixed_pte_pfn))
> 

Yes.

>>   
>>   	pgdp = pgd_offset(mm, vaddr);
>>   	p4dp = p4d_alloc(mm, pgdp, vaddr);
>> @@ -1272,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>>   	 * Page table modifying tests. They need to hold
>>   	 * proper page table lock.
>>   	 */
>> -
>> -	ptep = pte_offset_map_lock(mm, pmdp, vaddr, &ptl);
>> -	pte_clear_tests(mm, ptep, pte_aligned, vaddr, prot);
>> -	pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>> -	pte_unmap_unlock(ptep, ptl);
>> +	ptl = pte_lockptr(args.mm, args.pmdp);
>> +	spin_lock(ptl);
>> +	pte_clear_tests(&args);
>> +	pte_advanced_tests(&args);
>> +	spin_unlock(ptl);
> 
> Why pte_offset_map_lock()/pte_unmap_unlock() has been dropped and
> spin_lock()/spin_unlock() sequence has been added ? Please dont
> change the tests in these patches.
> 

The semantics of pte_offset_map_lock() is to grab and take the lock
and return the PTE entry, which is mapped if needed. We already had
the PTE entry tracked by args->ptep in init_args(). So some of the
operations covered by pte_offset_map_lock() isn't needed any more.

>>   
>>   	ptl = pmd_lock(mm, pmdp);
>>   	pmd_clear_tests(mm, pmdp);
>>
> 

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-22  5:45   ` Anshuman Khandual
@ 2021-07-22  6:41     ` Gavin Shan
  2021-07-22  7:11       ` Anshuman Khandual
  0 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:41 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 3:45 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> This uses struct pgtable_debug_args in PMD modifying tests. The allocated
>> huge page is used when set_pmd_at() is used. The corresponding tests
>> are skipped if the huge page doesn't exist. Besides, the unused variable
>> @pmd_aligned in debug_vm_pgtable() is dropped.
> 
> Please dont drop @pmd_aligned just yet.
> 

We need do so. Otherwise, there is build warning to complain
something like 'unused variable' after this patch is applied.

>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
>>   1 file changed, 48 insertions(+), 54 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index eb6dda88e0d9..cec3cbf99a6b 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>>   	WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>>   }
>>   
>> -static void __init pmd_advanced_tests(struct mm_struct *mm,
>> -				      struct vm_area_struct *vma, pmd_t *pmdp,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot, pgtable_t pgtable)
>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
>>   {
>>   	pmd_t pmd;
>> +	unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
>>   
>>   	if (!has_transparent_hugepage())
>>   		return;
>>   
>>   	pr_debug("Validating PMD advanced\n");
>> -	/* Align the address wrt HPAGE_PMD_SIZE */
>> -	vaddr &= HPAGE_PMD_MASK;
> 
> Please just leave these unchanged. If has_transparent_hugepage() evaluates
> negative, it skips the masking operation. As mentioned earlier please avoid
> changing the test in any manner during these transition patches.
> 

Ok.

>> +	if (args->pmd_pfn == ULONG_MAX) {
>> +		pr_debug("%s: Skipped\n", __func__);
>> +		return;
>> +	}
> 
> Just return. Please dont call out "Skipped".
> 

Ok.

>>   
>> -	pgtable_trans_huge_deposit(mm, pmdp, pgtable);
>> +	pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
>>   
>> -	pmd = pfn_pmd(pfn, prot);
>> -	set_pmd_at(mm, vaddr, pmdp, pmd);
>> -	pmdp_set_wrprotect(mm, vaddr, pmdp);
>> -	pmd = READ_ONCE(*pmdp);
>> +	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>> +	pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(pmd_write(pmd));
>> -	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>> -	pmd = READ_ONCE(*pmdp);
>> +	pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(!pmd_none(pmd));
>>   
>> -	pmd = pfn_pmd(pfn, prot);
>> +	pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>   	pmd = pmd_wrprotect(pmd);
>>   	pmd = pmd_mkclean(pmd);
>> -	set_pmd_at(mm, vaddr, pmdp, pmd);
>> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>   	pmd = pmd_mkwrite(pmd);
>>   	pmd = pmd_mkdirty(pmd);
>> -	pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
>> -	pmd = READ_ONCE(*pmdp);
>> +	pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
>> -	pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
>> -	pmd = READ_ONCE(*pmdp);
>> +	pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(!pmd_none(pmd));
>>   
>> -	pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
>> +	pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
>>   	pmd = pmd_mkyoung(pmd);
>> -	set_pmd_at(mm, vaddr, pmdp, pmd);
>> -	pmdp_test_and_clear_young(vma, vaddr, pmdp);
>> -	pmd = READ_ONCE(*pmdp);
>> +	set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>> +	pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(pmd_young(pmd));
>>   
>>   	/*  Clear the pte entries  */
>> -	pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>> -	pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
>> +	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)
>> @@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
>>   #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 mm_struct *mm,
>> -				      struct vm_area_struct *vma, pmd_t *pmdp,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot, pgtable_t pgtable)
>> -{
>> -}
>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
>>   static void __init pud_advanced_tests(struct mm_struct *mm,
>>   				      struct vm_area_struct *vma, pud_t *pudp,
>>   				      unsigned long pfn, unsigned long vaddr,
>> @@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>>   #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
>>   
>>   #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>>   {
>>   	pmd_t pmd;
>>   
>> -	if (!arch_vmap_pmd_supported(prot))
>> +	if (!arch_vmap_pmd_supported(args->page_prot))
>>   		return;
>>   
>>   	pr_debug("Validating PMD huge\n");
>> @@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>   	 * X86 defined pmd_set_huge() verifies that the given
>>   	 * PMD is not a populated non-leaf entry.
>>   	 */
>> -	WRITE_ONCE(*pmdp, __pmd(0));
>> -	WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
>> -	WARN_ON(!pmd_clear_huge(pmdp));
>> -	pmd = READ_ONCE(*pmdp);
>> +	WRITE_ONCE(*(args->pmdp), __pmd(0));
> 
> Possible extra braces.
> 

Will drop it in v4, thanks!

>> +	WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn),
>> +			      args->page_prot));
> 
> Dont break the line.
> 

Ok.

>> +	WARN_ON(!pmd_clear_huge(args->pmdp));
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(!pmd_none(pmd));
>>   }
>>   
>> @@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>>   	WARN_ON(!pud_none(pud));
>>   }
>>   #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
>>   static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>>   #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>   
>> @@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
>>   	WARN_ON(!pte_none(pte));
>>   }
>>   
>> -static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
>> +static void __init pmd_clear_tests(struct pgtable_debug_args *args)
>>   {
>> -	pmd_t pmd = READ_ONCE(*pmdp);
>> +	pmd_t pmd = READ_ONCE(*(args->pmdp));
>>   
>>   	pr_debug("Validating PMD clear\n");
>>   	pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
>> -	WRITE_ONCE(*pmdp, pmd);
>> -	pmd_clear(pmdp);
>> -	pmd = READ_ONCE(*pmdp);
>> +	WRITE_ONCE(*(args->pmdp), pmd);
>> +	pmd_clear(args->pmdp);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(!pmd_none(pmd));
>>   }
>>   
>> -static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>> -				      pgtable_t pgtable)
>> +static void __init pmd_populate_tests(struct pgtable_debug_args *args)
>>   {
>>   	pmd_t pmd;
>>   
>> @@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>   	 * This entry points to next level page table page.
>>   	 * Hence this must not qualify as pmd_bad().
>>   	 */
>> -	pmd_populate(mm, pmdp, pgtable);
>> -	pmd = READ_ONCE(*pmdp);
>> +	pmd_populate(args->mm, args->pmdp, args->start_ptep);
>> +	pmd = READ_ONCE(*(args->pmdp));
>>   	WARN_ON(pmd_bad(pmd));
>>   }
>>   
>> @@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>>   	pgtable_t saved_ptep;
>>   	pgprot_t prot;
>>   	phys_addr_t paddr;
>> -	unsigned long vaddr, pmd_aligned;
>> +	unsigned long vaddr;
>>   	unsigned long pud_aligned;
>>   	spinlock_t *ptl = NULL;
>>   	int idx, ret;
>> @@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
>>   	 */
>>   	paddr = __pa_symbol(&start_kernel);
>>   
>> -	pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
> 
> Please dont drop these just yet and wait until [PATCH 11/12].
> 

Otherwise, it causes build warning: 'unused variable'.

>>   	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>>   
>>   	pgdp = pgd_offset(mm, vaddr);
>> @@ -1281,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>>   	pte_advanced_tests(&args);
>>   	spin_unlock(ptl);
>>   
>> -	ptl = pmd_lock(mm, pmdp);
>> -	pmd_clear_tests(mm, pmdp);
>> -	pmd_advanced_tests(mm, vma, pmdp, pmd_aligned, vaddr, prot, saved_ptep);
>> -	pmd_huge_tests(pmdp, pmd_aligned, prot);
>> -	pmd_populate_tests(mm, pmdp, saved_ptep);
>> +	ptl = pmd_lock(args.mm, args.pmdp);
>> +	pmd_clear_tests(&args);
>> +	pmd_advanced_tests(&args);
>> +	pmd_huge_tests(&args);
>> +	pmd_populate_tests(&args);
>>   	spin_unlock(ptl);
>>   
>>   	ptl = pud_lock(mm, pudp);
>>
> 

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests
  2021-07-22  5:39   ` Anshuman Khandual
@ 2021-07-22  6:47     ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:47 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 3:39 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> This uses struct pgtable_debug_args in PUD modifying tests. The allocated
>> huge page is used when set_pud_at() is used. The corresponding tests
>> are skipped if the huge page doesn't exist. Besides, the following unused
>> variables in debug_vm_pgtable() are dropped: @prot, @paddr, @pud_aligned.
> 
> Please dont drop @prot, @paddr, @pud_aligned just yet.
> 

Otherwise it will cause build warning ("unused variable") as
explain in previous reply.

>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 130 ++++++++++++++++--------------------------
>>   1 file changed, 50 insertions(+), 80 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index cec3cbf99a6b..57b7ead0708b 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -338,55 +338,55 @@ static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx)
>>   	WARN_ON(!pud_bad(pud_mkhuge(pud)));
>>   }
>>   
>> -static void __init pud_advanced_tests(struct mm_struct *mm,
>> -				      struct vm_area_struct *vma, pud_t *pudp,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot)
>> +static void __init pud_advanced_tests(struct pgtable_debug_args *args)
>>   {
>> +	unsigned long vaddr = (args->vaddr & HPAGE_PUD_MASK);
>>   	pud_t pud;
>>   
>>   	if (!has_transparent_hugepage())
>>   		return;
>>   
>>   	pr_debug("Validating PUD advanced\n");
>> -	/* Align the address wrt HPAGE_PUD_SIZE */
>> -	vaddr &= HPAGE_PUD_MASK;
> 
> Please just leave these unchanged. If has_transparent_hugepage() evaluates
> negative, it skips the masking operation. As mentioned earlier please avoid
> changing the test in any manner during these transition patches.
> 

Ok.

>> +	if (args->pud_pfn == ULONG_MAX) {
>> +		pr_debug("%s: Skipped\n", __func__);
> 
> Just return. Please dont call out "Skipped". Applicable for all transition
> patches here.
> 

Ok.

>> +		return;
>> +	}
>>   
>> -	pud = pfn_pud(pfn, prot);
>> -	set_pud_at(mm, vaddr, pudp, pud);
>> -	pudp_set_wrprotect(mm, vaddr, pudp);
>> -	pud = READ_ONCE(*pudp);
>> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
>> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
>> +	pudp_set_wrprotect(args->mm, vaddr, args->pudp);
>> +	pud = READ_ONCE(*(args->pudp));
> 
> Seems like an extra braces while de-referencing arg->pudp. Could these be
> dropped. Just READ_ONCE(*args->pudp).
> 

Ok. Will be dropped in v4.

>>   	WARN_ON(pud_write(pud));
>>   
>>   #ifndef __PAGETABLE_PMD_FOLDED
>> -	pudp_huge_get_and_clear(mm, vaddr, pudp);
>> -	pud = READ_ONCE(*pudp);
>> +	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(!pud_none(pud));
>>   #endif /* __PAGETABLE_PMD_FOLDED */
>> -	pud = pfn_pud(pfn, prot);
>> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
>>   	pud = pud_wrprotect(pud);
>>   	pud = pud_mkclean(pud);
>> -	set_pud_at(mm, vaddr, pudp, pud);
>> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
>>   	pud = pud_mkwrite(pud);
>>   	pud = pud_mkdirty(pud);
>> -	pudp_set_access_flags(vma, vaddr, pudp, pud, 1);
>> -	pud = READ_ONCE(*pudp);
>> +	pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(!(pud_write(pud) && pud_dirty(pud)));
>>   
>>   #ifndef __PAGETABLE_PMD_FOLDED
>> -	pudp_huge_get_and_clear_full(mm, vaddr, pudp, 1);
>> -	pud = READ_ONCE(*pudp);
>> +	pudp_huge_get_and_clear_full(args->mm, vaddr, args->pudp, 1);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(!pud_none(pud));
>>   #endif /* __PAGETABLE_PMD_FOLDED */
>>   
>> -	pud = pfn_pud(pfn, prot);
>> +	pud = pfn_pud(args->pud_pfn, args->page_prot);
>>   	pud = pud_mkyoung(pud);
>> -	set_pud_at(mm, vaddr, pudp, pud);
>> -	pudp_test_and_clear_young(vma, vaddr, pudp);
>> -	pud = READ_ONCE(*pudp);
>> +	set_pud_at(args->mm, vaddr, args->pudp, pud);
>> +	pudp_test_and_clear_young(args->vma, vaddr, args->pudp);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(pud_young(pud));
>>   
>> -	pudp_huge_get_and_clear(mm, vaddr, pudp);
>> +	pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);
>>   }
>>   
>>   static void __init pud_leaf_tests(struct pgtable_debug_args *args)
>> @@ -406,24 +406,14 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args)
>>   }
>>   #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 mm_struct *mm,
>> -				      struct vm_area_struct *vma, pud_t *pudp,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot)
>> -{
>> -}
>> +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 mm_struct *mm,
>> -				      struct vm_area_struct *vma, pud_t *pudp,
>> -				      unsigned long pfn, unsigned long vaddr,
>> -				      pgprot_t prot)
>> -{
>> -}
>> +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) { }
>>   static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>> @@ -450,11 +440,11 @@ static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>>   	WARN_ON(!pmd_none(pmd));
>>   }
>>   
>> -static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>> +static void __init pud_huge_tests(struct pgtable_debug_args *args)
>>   {
>>   	pud_t pud;
>>   
>> -	if (!arch_vmap_pud_supported(prot))
>> +	if (!arch_vmap_pud_supported(args->page_prot))
>>   		return;
>>   
>>   	pr_debug("Validating PUD huge\n");
>> @@ -462,15 +452,16 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>>   	 * X86 defined pud_set_huge() verifies that the given
>>   	 * PUD is not a populated non-leaf entry.
>>   	 */
>> -	WRITE_ONCE(*pudp, __pud(0));
>> -	WARN_ON(!pud_set_huge(pudp, __pfn_to_phys(pfn), prot));
>> -	WARN_ON(!pud_clear_huge(pudp));
>> -	pud = READ_ONCE(*pudp);
>> +	WRITE_ONCE(*(args->pudp), __pud(0));
>> +	WARN_ON(!pud_set_huge(args->pudp, __pfn_to_phys(args->fixed_pud_pfn),
>> +			      args->page_prot));
> 
> Please dont break the line, we could go upto 100 width. Please do
> the same for the entire series. Improves the readability.
> 
>> +	WARN_ON(!pud_clear_huge(args->pudp));
>> +	pud = READ_ONCE(*(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(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>> +static void __init pud_huge_tests(struct pgtable_debug_args *args) { }
>>   #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>   
>>   static void __init p4d_basic_tests(void)
>> @@ -492,27 +483,26 @@ static void __init pgd_basic_tests(void)
>>   }
>>   
>>   #ifndef __PAGETABLE_PUD_FOLDED
>> -static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp)
>> +static void __init pud_clear_tests(struct pgtable_debug_args *args)
>>   {
>> -	pud_t pud = READ_ONCE(*pudp);
>> +	pud_t pud = READ_ONCE(*(args->pudp));
>>   
>> -	if (mm_pmd_folded(mm))
>> +	if (mm_pmd_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating PUD clear\n");
>>   	pud = __pud(pud_val(pud) | RANDOM_ORVALUE);
>> -	WRITE_ONCE(*pudp, pud);
>> -	pud_clear(pudp);
>> -	pud = READ_ONCE(*pudp);
>> +	WRITE_ONCE(*(args->pudp), pud);
>> +	pud_clear(args->pudp);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(!pud_none(pud));
>>   }
>>   
>> -static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
>> -				      pmd_t *pmdp)
>> +static void __init pud_populate_tests(struct pgtable_debug_args *args)
>>   {
>>   	pud_t pud;
>>   
>> -	if (mm_pmd_folded(mm))
>> +	if (mm_pmd_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating PUD populate\n");
>> @@ -520,16 +510,13 @@ static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
>>   	 * This entry points to next level page table page.
>>   	 * Hence this must not qualify as pud_bad().
>>   	 */
>> -	pud_populate(mm, pudp, pmdp);
>> -	pud = READ_ONCE(*pudp);
>> +	pud_populate(args->mm, args->pudp, args->start_pmdp);
>> +	pud = READ_ONCE(*(args->pudp));
>>   	WARN_ON(pud_bad(pud));
>>   }
>>   #else  /* !__PAGETABLE_PUD_FOLDED */
>> -static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp) { }
>> -static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp,
>> -				      pmd_t *pmdp)
>> -{
>> -}
>> +static void __init pud_clear_tests(struct pgtable_debug_args *args) { }
>> +static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
>>   #endif /* PAGETABLE_PUD_FOLDED */
>>   
>>   #ifndef __PAGETABLE_P4D_FOLDED
>> @@ -1152,10 +1139,7 @@ static int __init debug_vm_pgtable(void)
>>   	pud_t *pudp, *saved_pudp;
>>   	pmd_t *pmdp, *saved_pmdp, pmd;
>>   	pgtable_t saved_ptep;
>> -	pgprot_t prot;
>> -	phys_addr_t paddr;
>>   	unsigned long vaddr;
>> -	unsigned long pud_aligned;
>>   	spinlock_t *ptl = NULL;
>>   	int idx, ret;
>>   
>> @@ -1164,7 +1148,6 @@ static int __init debug_vm_pgtable(void)
>>   	if (ret)
>>   		return ret;
>>   
>> -	prot = vm_get_page_prot(VMFLAGS);
> 
> Please dont drop these just yet and wait until [PATCH 11/12].
> 
>>   	vaddr = get_random_vaddr();
>>   	mm = mm_alloc();
>>   	if (!mm) {
>> @@ -1178,19 +1161,6 @@ static int __init debug_vm_pgtable(void)
>>   		return 1;
>>   	}
>>   
>> -	/*
>> -	 * PFN for mapping at PTE level is determined from a standard kernel
>> -	 * text symbol. But pfns for higher page table levels are derived by
>> -	 * masking lower bits of this real pfn. These derived pfns might not
>> -	 * exist on the platform but that does not really matter as pfn_pxx()
>> -	 * helpers will still create appropriate entries for the test. This
>> -	 * helps avoid large memory block allocations to be used for mapping
>> -	 * at higher page table levels.
>> -	 */
> 
> Please move this comment as is to the right place inside init_args()
> in the first patch itself. If possible all comments should be moved
> during the first patch and just the code remains till [PATCH 11/12].
> 

I will include all comments in PATCH[01/12]. In that case, we won't
introduce more changes to unrelated function (init_args()).

>> -	paddr = __pa_symbol(&start_kernel);
>> -
>> -	pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>> -
> 
> Please dont drop these just yet and wait until [PATCH 11/12].
> 

Otherwise, it will cause build warning.

>>   	pgdp = pgd_offset(mm, vaddr);
>>   	p4dp = p4d_alloc(mm, pgdp, vaddr);
>>   	pudp = pud_alloc(mm, p4dp, vaddr);
>> @@ -1282,11 +1252,11 @@ static int __init debug_vm_pgtable(void)
>>   	pmd_populate_tests(&args);
>>   	spin_unlock(ptl);
>>   
>> -	ptl = pud_lock(mm, pudp);
>> -	pud_clear_tests(mm, pudp);
>> -	pud_advanced_tests(mm, vma, pudp, pud_aligned, vaddr, prot);
>> -	pud_huge_tests(pudp, pud_aligned, prot);
>> -	pud_populate_tests(mm, pudp, saved_pmdp);
>> +	ptl = pud_lock(args.mm, args.pudp);
>> +	pud_clear_tests(&args);
>> +	pud_advanced_tests(&args);
>> +	pud_huge_tests(&args);
>> +	pud_populate_tests(&args);
>>   	spin_unlock(ptl);
>>   
>>   	spin_lock(&mm->page_table_lock);
>>
> 

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests
  2021-07-22  5:09   ` Anshuman Khandual
@ 2021-07-22  6:50     ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:50 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 3:09 PM, Anshuman Khandual wrote:
> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> This uses struct pgtable_debug_args in PGD/P4D modifying tests. No
>> allocated huge page is used in these tests. Besides, the unused
>> variable @saved_p4dp and @saved_pudp are dropped.
> 
> Please dont drop  @saved_p4dp and @saved_pudp just yet.
> 

It seems you have concern why I drop unused variables in individual
patches. There is build warning 'unused variable', reported by 0-day.
So we need to drop these unused variables in individual patches, but
it make the review a bit harder :)

>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 86 +++++++++++++++++++------------------------
>>   1 file changed, 38 insertions(+), 48 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index 57b7ead0708b..5ebacc940b68 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -520,27 +520,26 @@ static void __init pud_populate_tests(struct pgtable_debug_args *args) { }
>>   #endif /* PAGETABLE_PUD_FOLDED */
>>   
>>   #ifndef __PAGETABLE_P4D_FOLDED
>> -static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp)
>> +static void __init p4d_clear_tests(struct pgtable_debug_args *args)
>>   {
>> -	p4d_t p4d = READ_ONCE(*p4dp);
>> +	p4d_t p4d = READ_ONCE(*(args->p4dp));
>>   
>> -	if (mm_pud_folded(mm))
>> +	if (mm_pud_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating P4D clear\n");
>>   	p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE);
>> -	WRITE_ONCE(*p4dp, p4d);
>> -	p4d_clear(p4dp);
>> -	p4d = READ_ONCE(*p4dp);
>> +	WRITE_ONCE(*(args->p4dp), p4d);
>> +	p4d_clear(args->p4dp);
>> +	p4d = READ_ONCE(*(args->p4dp));
>>   	WARN_ON(!p4d_none(p4d));
>>   }
>>   
>> -static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
>> -				      pud_t *pudp)
>> +static void __init p4d_populate_tests(struct pgtable_debug_args *args)
>>   {
>>   	p4d_t p4d;
>>   
>> -	if (mm_pud_folded(mm))
>> +	if (mm_pud_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating P4D populate\n");
>> @@ -548,34 +547,33 @@ static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
>>   	 * This entry points to next level page table page.
>>   	 * Hence this must not qualify as p4d_bad().
>>   	 */
>> -	pud_clear(pudp);
>> -	p4d_clear(p4dp);
>> -	p4d_populate(mm, p4dp, pudp);
>> -	p4d = READ_ONCE(*p4dp);
>> +	pud_clear(args->pudp);
>> +	p4d_clear(args->p4dp);
>> +	p4d_populate(args->mm, args->p4dp, args->start_pudp);
>> +	p4d = READ_ONCE(*(args->p4dp));
>>   	WARN_ON(p4d_bad(p4d));
>>   }
>>   
>> -static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp)
>> +static void __init pgd_clear_tests(struct pgtable_debug_args *args)
>>   {
>> -	pgd_t pgd = READ_ONCE(*pgdp);
>> +	pgd_t pgd = READ_ONCE(*(args->pgdp));
>>   
>> -	if (mm_p4d_folded(mm))
>> +	if (mm_p4d_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating PGD clear\n");
>>   	pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE);
>> -	WRITE_ONCE(*pgdp, pgd);
>> -	pgd_clear(pgdp);
>> -	pgd = READ_ONCE(*pgdp);
>> +	WRITE_ONCE(*(args->pgdp), pgd);
>> +	pgd_clear(args->pgdp);
>> +	pgd = READ_ONCE(*(args->pgdp));
>>   	WARN_ON(!pgd_none(pgd));
>>   }
>>   
>> -static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>> -				      p4d_t *p4dp)
>> +static void __init pgd_populate_tests(struct pgtable_debug_args *args)
>>   {
>>   	pgd_t pgd;
>>   
>> -	if (mm_p4d_folded(mm))
>> +	if (mm_p4d_folded(args->mm))
>>   		return;
>>   
>>   	pr_debug("Validating PGD populate\n");
>> @@ -583,23 +581,17 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>>   	 * This entry points to next level page table page.
>>   	 * Hence this must not qualify as pgd_bad().
>>   	 */
>> -	p4d_clear(p4dp);
>> -	pgd_clear(pgdp);
>> -	pgd_populate(mm, pgdp, p4dp);
>> -	pgd = READ_ONCE(*pgdp);
>> +	p4d_clear(args->p4dp);
>> +	pgd_clear(args->pgdp);
>> +	pgd_populate(args->mm, args->pgdp, args->start_p4dp);
>> +	pgd = READ_ONCE(*(args->pgdp));
>>   	WARN_ON(pgd_bad(pgd));
>>   }
>>   #else  /* !__PAGETABLE_P4D_FOLDED */
>> -static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp) { }
>> -static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) { }
>> -static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp,
>> -				      pud_t *pudp)
>> -{
>> -}
>> -static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp,
>> -				      p4d_t *p4dp)
>> -{
>> -}
>> +static void __init p4d_clear_tests(struct pgtable_debug_args *args) { }
>> +static void __init pgd_clear_tests(struct pgtable_debug_args *args) { }
>> +static void __init p4d_populate_tests(struct pgtable_debug_args *args) { }
>> +static void __init pgd_populate_tests(struct pgtable_debug_args *args) { }
>>   #endif /* PAGETABLE_P4D_FOLDED */
>>   
>>   static void __init pte_clear_tests(struct pgtable_debug_args *args)
>> @@ -1135,8 +1127,8 @@ static int __init debug_vm_pgtable(void)
>>   	struct vm_area_struct *vma;
>>   	struct mm_struct *mm;
>>   	pgd_t *pgdp;
>> -	p4d_t *p4dp, *saved_p4dp;
>> -	pud_t *pudp, *saved_pudp;
>> +	p4d_t *p4dp;
>> +	pud_t *pudp;
>>   	pmd_t *pmdp, *saved_pmdp, pmd;
>>   	pgtable_t saved_ptep;
>>   	unsigned long vaddr;
>> @@ -1180,8 +1172,6 @@ static int __init debug_vm_pgtable(void)
>>   	 * page table pages.
>>   	 */
>>   	pmd = READ_ONCE(*pmdp);
>> -	saved_p4dp = p4d_offset(pgdp, 0UL);
>> -	saved_pudp = pud_offset(p4dp, 0UL);
>>   	saved_pmdp = pmd_offset(pudp, 0UL);
>>   	saved_ptep = pmd_pgtable(pmd);
>>   
>> @@ -1259,15 +1249,15 @@ static int __init debug_vm_pgtable(void)
>>   	pud_populate_tests(&args);
>>   	spin_unlock(ptl);
>>   
>> -	spin_lock(&mm->page_table_lock);
>> -	p4d_clear_tests(mm, p4dp);
>> -	pgd_clear_tests(mm, pgdp);
>> -	p4d_populate_tests(mm, p4dp, saved_pudp);
>> -	pgd_populate_tests(mm, pgdp, saved_p4dp);
>> -	spin_unlock(&mm->page_table_lock);
>> +	spin_lock(&(args.mm->page_table_lock));
>> +	p4d_clear_tests(&args);
>> +	pgd_clear_tests(&args);
>> +	p4d_populate_tests(&args);
>> +	pgd_populate_tests(&args);
>> +	spin_unlock(&(args.mm->page_table_lock));
>>   
>> -	p4d_free(mm, saved_p4dp);
>> -	pud_free(mm, saved_pudp);
>> +	p4d_free(mm, p4d_offset(pgdp, 0UL));
>> +	pud_free(mm, pud_offset(p4dp, 0UL));
> 
> Please keep @saved_pudp and @saved_p4dp declaration, assignment and
> usage unchanged for now. Drop them only during [PATCH 11/12]. So in
> each patch like these, drop the elements only if there is an unused
> warning during build.
> 
> There are two set of page table debug elements i.e old and new. The
> test is transitioning from old to new. Even after the transition is
> complete, the old elements are properly declared, initialized and
> freed up. Entire old set should be dropped only in [PATCH 11/12].
> 

As explained at the beginning and we need to drop the unused variable.

>>   	pmd_free(mm, saved_pmdp);
>>   	pte_free(mm, saved_ptep);
>>   
>>

Thanks,
Gavin
  


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code
  2021-07-22  4:51   ` Anshuman Khandual
@ 2021-07-22  6:53     ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:53 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 2:51 PM, Anshuman Khandual wrote:
> Small nit for the subject line.
> 
> s/Remove unused code/Remove unused page table debug elements/
> 

Ok. Will apply the replacement in v4, thanks!

> On 7/19/21 6:36 PM, Gavin Shan wrote:
>> The variables used by old implementation isn't needed as we switched
>> to "struct pgtable_debug_args". Lets remove them and related code in
>> debug_vm_pgtable().
>>
>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>> ---
>>   mm/debug_vm_pgtable.c | 54 -------------------------------------------
>>   1 file changed, 54 deletions(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index 5ebacc940b68..4f7bf1c9724a 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -1124,14 +1124,6 @@ static int __init init_args(struct pgtable_debug_args *args)
>>   static int __init debug_vm_pgtable(void)
>>   {
>>   	struct pgtable_debug_args args;
>> -	struct vm_area_struct *vma;
>> -	struct mm_struct *mm;
>> -	pgd_t *pgdp;
>> -	p4d_t *p4dp;
>> -	pud_t *pudp;
>> -	pmd_t *pmdp, *saved_pmdp, pmd;
>> -	pgtable_t saved_ptep;
>> -	unsigned long vaddr;
>>   	spinlock_t *ptl = NULL;
>>   	int idx, ret;
>>   
>> @@ -1140,41 +1132,6 @@ static int __init debug_vm_pgtable(void)
>>   	if (ret)
>>   		return ret;
>>   
>> -	vaddr = get_random_vaddr();
>> -	mm = mm_alloc();
>> -	if (!mm) {
>> -		pr_err("mm_struct allocation failed\n");
>> -		return 1;
>> -	}
>> -
>> -	vma = vm_area_alloc(mm);
>> -	if (!vma) {
>> -		pr_err("vma allocation failed\n");
>> -		return 1;
>> -	}
>> -
>> -	pgdp = pgd_offset(mm, vaddr);
>> -	p4dp = p4d_alloc(mm, pgdp, vaddr);
>> -	pudp = pud_alloc(mm, p4dp, vaddr);
>> -	pmdp = pmd_alloc(mm, pudp, vaddr);
>> -	/*
>> -	 * Allocate pgtable_t
>> -	 */
>> -	if (pte_alloc(mm, pmdp)) {
>> -		pr_err("pgtable allocation failed\n");
>> -		return 1;
>> -	}
>> -
>> -	/*
>> -	 * Save all the page table page addresses as the page table
>> -	 * entries will be used for testing with random or garbage
>> -	 * values. These saved addresses will be used for freeing
>> -	 * page table pages.
>> -	 */
> 
> Please move this comment as is to the right place inside init_args()
> in the first patch itself. If possible all comments should be moved
> during the first patch and just the code gets dropped here.
> 

In PATCH[v3 01/12], We already had the comments in init_args() as below:

        /*
         * The above page table entries will be modified. Lets save the
         * page table entries so that they can be released when the tests
         * are completed.
         */


>> -	pmd = READ_ONCE(*pmdp);
>> -	saved_pmdp = pmd_offset(pudp, 0UL);
>> -	saved_ptep = pmd_pgtable(pmd);
>> -
>>   	/*
>>   	 * Iterate over the protection_map[] to make sure that all
>>   	 * the basic page table transformation validations just hold
>> @@ -1256,17 +1213,6 @@ static int __init debug_vm_pgtable(void)
>>   	pgd_populate_tests(&args);
>>   	spin_unlock(&(args.mm->page_table_lock));
>>   
>> -	p4d_free(mm, p4d_offset(pgdp, 0UL));
>> -	pud_free(mm, pud_offset(p4dp, 0UL));
>> -	pmd_free(mm, saved_pmdp);
>> -	pte_free(mm, saved_ptep);
>> -
>> -	vm_area_free(vma);
>> -	mm_dec_nr_puds(mm);
>> -	mm_dec_nr_pmds(mm);
>> -	mm_dec_nr_ptes(mm);
>> -	mmdrop(mm);
>> -
>>   	destroy_args(&args);
>>   	return 0;
>>   }
>>

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag
  2021-07-22  3:51       ` Anshuman Khandual
@ 2021-07-22  6:54         ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-22  6:54 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 1:51 PM, Anshuman Khandual wrote:
> Small nit:
> 
> s/Fix corrupted page flag/Fix page flag corruption on arm64/
> 

Sure, The replacement will be applied in v4, thanks!

> On 7/21/21 5:33 PM, Gavin Shan wrote:
>> Hi Anshuman,
>>
>> On 7/21/21 8:18 PM, Anshuman Khandual wrote:
>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>> In page table entry modifying tests, set_xxx_at() are used to populate
>>>> the page table entries. On ARM64, PG_arch_1 is set to the target page
>>>> flag if execution permission is given. The page flag is kept when the
>>>> page is free'd to buddy's free area list. However, it will trigger page
>>>> checking failure when it's pulled from the buddy's free area list, as
>>>> the following warning messages indicate.
>>>>
>>>>      BUG: Bad page state in process memhog  pfn:08000
>>>>      page:0000000015c0a628 refcount:0 mapcount:0 \
>>>>           mapping:0000000000000000 index:0x1 pfn:0x8000
>>>>      flags: 0x7ffff8000000800(arch_1|node=0|zone=0|lastcpupid=0xfffff)
>>>>      raw: 07ffff8000000800 dead000000000100 dead000000000122 0000000000000000
>>>>      raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
>>>>      page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag(s) set
>>>>
>>>> This fixes the issue by clearing PG_arch_1 through flush_dcache_page()
>>>> after set_xxx_at() is called.
>>>
>>> Could you please add comments before each flush_dcache_page() instance
>>> explaining why this is needed for arm64 platforms with relevant PG_arch_1
>>> context and how this does not have any adverse effect on other platforms ?
>>> It should be easy for some one looking at this code after a while to figure
>>> out from where flush_dcache_page() came from.
>>>
>>
>> Good point. I will improve chage log to include the commit ID in v4 where the
>> page flag (PG_arch_1) is used and explain how. In that case, it's much clearer
>> to understand the reason why we need flush_dcache_page() after set_xxx_at() on
>> ARM64.
> 
> But also some in code comments where flush_dcache_page() is being called.
> 

Yes, I will add some comments where flush_dcache_page() is called in v4.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-22  6:23         ` Gavin Shan
@ 2021-07-22  7:08           ` Anshuman Khandual
  2021-07-23  0:43             ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  7:08 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/22/21 11:53 AM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/22/21 2:41 PM, Anshuman Khandual wrote:
>> On 7/21/21 3:50 PM, Gavin Shan wrote:
>>> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>>> In debug_vm_pgtable(), there are many local variables introduced to
>>>>> track the needed information and they are passed to the functions for
>>>>> various test cases. It'd better to introduce a struct as place holder
>>>>> for these information. With it, what the functions for various test
>>>>> cases need is the struct, to simplify the code. It also makes code
>>>>> easier to be maintained.
>>>>>
>>>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>>>> in the page table modifying tests. So the accessed pages in the tests
>>>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>>>> that aren't owned by us. This causes issues like page flag corruption.
>>>>>
>>>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>>>> and destroyed, but the information in the struct isn't used yet. They
>>>>> will be used in subsequent patches.
>>>>>
>>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>>> ---
>>>>>    mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>>>    1 file changed, 196 insertions(+), 1 deletion(-)
>>>>>
>>>
>>> I saw you've finished the review on PATCH[v3 01/12] and PATCH[v3 02/12].
>>> I will wait to integrate your comments to v4 until you finish the review
>>> on all patches in v3 series.
>>>
>>>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>>>> index 1c922691aa61..ea153ff40d23 100644
>>>>> --- a/mm/debug_vm_pgtable.c
>>>>> +++ b/mm/debug_vm_pgtable.c
>>>>> @@ -58,6 +58,36 @@
>>>>>    #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
>>>>>    #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;
>>>>> +
>>>>> +    unsigned long        pud_pfn;
>>>>> +    unsigned long        pmd_pfn;
>>>>> +    unsigned long        pte_pfn;
>>>>> +
>>>>> +    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(unsigned long pfn, int idx)
>>>>>    {
>>>>>        pgprot_t prot = protection_map[idx];
>>>>> @@ -955,8 +985,167 @@ static unsigned long __init get_random_vaddr(void)
>>>>>        return random_vaddr;
>>>>>    }
>>>>>    +static void __init destroy_args(struct pgtable_debug_args *args)
>>>>> +{
>>>>> +    struct page *page = NULL;
>>>>> +
>>>>> +    /* Free (huge) page */
>>>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>>> +        has_transparent_hugepage() &&
>>>>> +        args->pud_pfn != ULONG_MAX) {
>>>>> +        page = pfn_to_page(args->pud_pfn);
>>>>> +        __free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>>>> +    } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>>> +           has_transparent_hugepage() &&
>>>>> +           args->pmd_pfn != ULONG_MAX) {
>>>>> +        page = pfn_to_page(args->pmd_pfn);
>>>>> +        __free_pages(page, HPAGE_PMD_ORDER);
>>>>> +    } else if (args->pte_pfn != ULONG_MAX) {
>>>>> +        page = pfn_to_page(args->pte_pfn);
>>>>> +        __free_pages(page, 0);
>>>>> +    }
>>>>> +
>>>>> +    /* Free page table */
>>>>> +    if (args->start_ptep) {
>>>>> +        pte_free(args->mm, args->start_ptep);
>>>>> +        mm_dec_nr_ptes(args->mm);
>>>>> +    }
>>>>> +
>>>>> +    if (args->start_pmdp) {
>>>>> +        pmd_free(args->mm, args->start_pmdp);
>>>>> +        mm_dec_nr_pmds(args->mm);
>>>>> +    }
>>>>> +
>>>>> +    if (args->start_pudp) {
>>>>> +        pud_free(args->mm, args->start_pudp);
>>>>> +        mm_dec_nr_puds(args->mm);
>>>>> +    }
>>>>> +
>>>>> +    if (args->start_p4dp)
>>>>> +        p4d_free(args->mm, args->p4dp);
>>>>> +
>>>>> +    /* Free vma and mm struct */
>>>>> +    if (args->vma)
>>>>> +        vm_area_free(args->vma);
>>>>> +    if (args->mm)
>>>>> +        mmdrop(args->mm);
>>>>> +}
>>>>> +
>>>>> +static int __init init_args(struct pgtable_debug_args *args)
>>>>> +{
>>>>> +    struct page *page = NULL;
>>>>> +    phys_addr_t phys;
>>>>> +    int ret = 0;
>>>>> +
>>>>> +    /* Initialize the debugging data */
>>>>> +    memset(args, 0, sizeof(*args));
>>>>> +    args->page_prot      = vm_get_page_prot(VMFLAGS);
>>>>> +    args->page_prot_none = __P000;
>>>>
>>>> Please preserve the existing comments before this assignment.
>>>>
>>>>           /*
>>>>            * __P000 (or even __S000) will help create page table entries with
>>>>            * PROT_NONE permission as required for pxx_protnone_tests().
>>>>            */
>>>>
>>>
>>> Sure. I will combine the comments in v4 as below:
>>>
>>>      /*
>>>       * Initialize the debugging arguments.
>>>       *
>>>       * __P000 (or even __S000) will help create page table entries with
>>>           * PROT_NONE permission as required for pxx_protnone_tests().
>>>           */
>>>
>>>
>>>>> +    args->pud_pfn        = ULONG_MAX;
>>>>> +    args->pmd_pfn        = ULONG_MAX;
>>>>> +    args->pte_pfn        = ULONG_MAX;
>>>>> +    args->fixed_pgd_pfn  = ULONG_MAX;
>>>>> +    args->fixed_p4d_pfn  = ULONG_MAX;
>>>>> +    args->fixed_pud_pfn  = ULONG_MAX;
>>>>> +    args->fixed_pmd_pfn  = ULONG_MAX;
>>>>> +    args->fixed_pte_pfn  = ULONG_MAX;
>>>>> +
>>>>> +    /* Allocate mm and vma */
>>>>> +    args->mm = mm_alloc();
>>>>> +    if (!args->mm) {
>>>>> +        pr_err("Failed to allocate mm struct\n");
>>>>> +        ret = -ENOMEM;
>>>>> +        goto error;
>>>>> +    }
>>>>> +
>>>>> +    args->vma = vm_area_alloc(args->mm);
>>>>> +    if (!args->vma) {
>>>>> +        pr_err("Failed to allocate vma\n");
>>>>> +        ret = -ENOMEM;
>>>>> +        goto error;
>>>>> +    }
>>>>> +
>>>>> +    /* Figure out the virtual address and allocate page table entries */
>>>>> +    args->vaddr = get_random_vaddr();
>>>>
>>>> Please group args->vaddr's init with page_prot and page_prot_none above.
>>>>
>>>
>>> Yes, It will make the code tidy. I'll move this line accordingly in v4,
>>> but the related comments will be dropped as the code is self-explanatory.
>>>
>>>          /* Allocate page table entries */
>>>
>>>>> +    args->pgdp = pgd_offset(args->mm, args->vaddr);
>>>>> +    args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>>>>> +    args->pudp = args->p4dp ?
>>>>> +             pud_alloc(args->mm, args->p4dp, args->vaddr) : NULL;
>>>>> +    args->pmdp = args->pudp ?
>>>>> +             pmd_alloc(args->mm, args->pudp, args->vaddr) : NULL;
>>>>> +    args->ptep = args->pmdp ?
>>>>> +             pte_alloc_map(args->mm, args->pmdp, args->vaddr) : NULL;
>>>>> +    if (!args->ptep) {
>>>>> +        pr_err("Failed to allocate page table\n");
>>>>> +        ret = -ENOMEM;
>>>>> +        goto error;
>>>>> +    }
>>>>
>>>> Why not just assert that all page table level pointers are allocated
>>>> successfully, otherwise bail out the test completely. Something like
>>>> this at each level.
>>>>
>>>>      if (!args->p4dp) {
>>>>          pr_err("Failed to allocate page table\n");
>>>>          ret = -ENOMEM;
>>>>          goto error;
>>>>      }
>>>>
>>>> Is there any value in proceeding with the test when some page table
>>>> pointers have not been allocated. Also individual tests do not cross
>>>> check these pointers. Also asserting successful allocations will
>>>> make the freeing path simpler, as I had mentioned earlier.
>>>>
>>>
>>> There is no tests will be carried out if we fail to allocate any level
>>> of page table entries. For other questions, please refer below response.
>>> In summary, this snippet needs to be combined with next snippet, as below.
>>>
>>>>> +
>>>>> +    /*
>>>>> +     * The above page table entries will be modified. Lets save the
>>>>> +     * page table entries so that they can be released when the tests
>>>>> +     * are completed.
>>>>> +     */
>>>>> +    args->start_p4dp = p4d_offset(args->pgdp, 0UL);
>>>>> +    args->start_pudp = pud_offset(args->p4dp, 0UL);
>>>>> +    args->start_pmdp = pmd_offset(args->pudp, 0UL);
>>>>> +    args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
>>>>
>>>> If the above page table pointers have been validated to be allocated
>>>> successfully, we could add these here.
>>>>
>>>>      WARN_ON(!args->start_p4dp)
>>>>      WARN_ON(!args->start_pudp)
>>>>      WARN_ON(!args->start_pmdp)
>>>>      WARN_ON(!args->start_ptep)
>>>>
>>>> Afterwards all those if (args->start_pxdp) checks in the freeing path
>>>> will not be required anymore.
>>>>
>>>
>>> The check on @args->start_pxdp is still needed in destroy_args() for
>>> couple of cases: (1) destroy_args() is called on failing to allocate
>>> @args->mm or @args->vma. That time, no page table entries are allocated.
>>> (2) It's possible to fail allocating current level of page table entries
>>> even the previous levels of page table entries are allocated successfully.
>>
>> This makes sense as destroy_args() is getting called if any of these
>> allocations fails during init_args(). Did not realize that earlier.
>>
>>>
>>> So Lets change these (above) two snippets as below in v4:
>>>
>>>      /*
>>>       * Allocate page table entries. The allocated page table entries
>>>       * will be modified in the tests. Lets save the page table entries
>>>       * so that they can be released when the tests are completed.
>>>       */
>>>      args->pgdp = pgd_offset(args->mm, args->vaddr);
>>>      args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
>>>      if (!args->p4dp) {
>>>          pr_err("Failed to allocate p4d entries\n");
>>>          ret = -ENOMEM;
>>>          goto error;
>>>      }
>>>
>>>      args->start_p4dp = p4d_offset(args->pgdp, 0UL);
>>
>> Dont bring the arg->start_pxdp assignments here. If all page table level
>> pointer allocations succeed, they all get assigned together like we have
>> right now. Although a sanity check afterwards like the following, might
>> still be better.
>>
>> WARN_ON(!args->start_p4dp)
>> WARN_ON(!args->start_pudp)
>> WARN_ON(!args->start_pmdp)
>> WARN_ON(!args->start_ptep)
>>
> 
> We have to assign arg->start_pxdp here because destroy_args() relies
> it to release the corresponding page tables in failing path. For example,
> the args->start_p4dp is going to be release if we fail to populate
> args->start_pudp.

Okay.

> 
> Ok. I will add WARN_ON() for each level of page table entries right after
> they are assigned in v4.
> 
>>>      args->pudp = pud_alloc(args->mm, args->p4dp, args->vaddr);
>>>      if (!args->pudp) {
>>>          pr_err("Failed to allocate pud entries\n");
>>>          ret = -ENOMEM;
>>>          goto error;
>>>      }
>>>
>>>      args->pmdp = pmd_alloc(args->mm, args->pudp, args->vaddr);
>>>      if (!args->pmdp) {
>>>          pr_err("Failed to allocate PMD entries\n");
>>>          ret = -ENOMEM;
>>>          goto error;
>>>      }
>>>
>>>      args->start_pmdp = pmd_offset(args->pudp, 0UL);
>>>      args->ptep = pte_alloc_map(args->mm, args->pmdp, args->vaddr);
>>>      if (!args->ptep) {
>>>          pr_err("Failed to allocate page table\n");
>>>          ret = -ENOMEM;
>>>          goto error;
>>>      }
>>>
>>>      args->start_ptep = pmd_pgtable(READ_ONCE(*(args->pmdp)));
>>>
>>>>> +
>>>>> +    /*
>>>>> +     * Figure out the fixed addresses, which are all around the kernel
>>>>> +     * symbol (@start_kernel). The corresponding PFNs might be invalid,
>>>>> +     * but it's fine as the following tests won't access the pages.
>>>>> +     */
>>>>> +    phys = __pa_symbol(&start_kernel);
>>>>> +    args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
>>>>> +    args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
>>>>> +    args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
>>>>> +    args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
>>>>> +    args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
>>>>> +
>>>>> +    /*
>>>>> +     * Allocate (huge) pages because some of the tests need to access
>>>>> +     * the data in the pages. The corresponding tests will be skipped
>>>>> +     * if we fail to allocate (huge) pages.
>>>>> +     */
>>>>> +    if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>>> +        IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>>> +        has_transparent_hugepage()) {
>>>>> +        page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>>>>> +                   HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>>>
>>>> Please drop __GFP_NOWARN and instead use something like alloc_contig_pages()
>>>> when required allocation order exceed (MAX_ORDER - 1). Else the test might
>>>> not be able to execute on platform configurations, where PUD THP is enabled.
>>>>
>>>
>>> Yes, It's correct that alloc_contig_pages() should be used here, depending
>>> on CONFIG_CONTIG_ALLOC. Otherwise, alloc_pages(...__GFP_NOWARN...) is still
>>> used as we're doing. This snippet will be changed like below in v4:
>>
>> First 'order > (MAX_ORDER - 1)' needs to be established before calling into
>> alloc_contig_pages() without __GFP_NOWARN and set a new flag indicating that
>> there is contig page allocated. But if 'order <= (MAX_ORDER - 1)', then call
>> alloc_pages(..) without  __GFP_NOWARN. There is no need to add  __GFP_NOWARN
>> in any case. In case CONFIG_CONTIG_ALLOC is not available, directly return a
>> NULL as that would have been the case with alloc_pages(...__GFP_NOWARN...) as
>> well.
>>
>> Symbol alloc_contig_pages() is not available outside CONFIG_CONTIG_ALLOC. So
>> IS_ENABLED() construct will not work, unless there is an empty stub added in
>> the header. Otherwise #ifdef CONFIG_CONTIG_ALLOC needs to be used instead.
>>
>> Regardless please do test this on a x86 platform with PUD based THP in order
>> to make sure every thing works as expected.
>>
> 
> Thanks, I will change the code accordingly in v4 and test it on x86
> before posting it.
> 
>>>
>>>      /*
>>>       * Allocate (huge) pages because some of the tests need to access
>>>       * the data in the pages. The corresponding tests will be skipped
>>>       * if we fail to allocate (huge) pages.
>>>       */
>>>      if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>          IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>          IS_ENABLED(CONFIG_CONTIG_ALLOC)) &&
>>>          has_transparent_hugepage()) {
>>>          page = alloc_contig_pages((1 << (HPAGE_PUD_SHIFT - PAGE_SHIFT)),
>>>                        GFP_KERNEL | __GFP_NOWARN,
>>>                        first_online_node, NULL);
>>>          if (page) {
>>>              args->is_contiguous_pud_page = true;
>>>              args->pud_pfn = page_to_pfn(page);
>>>              args->pmd_pfn = args->pud_pfn;
>>>              args->pte_pfn = args->pud_pfn;
>>>              return 0;
>>>          }
>>>      }
>>>
>>>      if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
>>>          IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
>>>          has_transparent_hugepage()) {
>>>          page = alloc_pages(GFP_KERNEL | __GFP_NOWARN,
>>>                     HPAGE_PUD_SHIFT - PAGE_SHIFT);
>>>          if (page) {
>>>              args->is_contiguous_pud_page = false;
>>>              args->pud_pfn = page_to_pfn(page);
>>>              args->pmd_pfn = args->pud_pfn;
>>>              args->pte_pfn = args->pud_pfn;
>>>              return 0;
>>>          }
>>>      }
>>>
>>>      [... The logic to allocate PMD huge page or page is kept as of being]
>>
>> IIRC it is also not guaranteed that PMD_SHIFT <= (MAX_ORDER - 1). Hence
>> this same scheme should be followed for PMD level allocation as well.
>>
> 
> In theory, it's possible to have PMD_SHIFT <= (MAX_ORDER - 1) with misconfigured
> kernel. I will apply the similar logic to PMD huge page in v4.
> 
>>>      [... The code to release the PUD huge page needs changes based on @args->is_contiguous_pud_page]
>>
>> Right, a flag would be needed to call the appropriate free function.
>>
> 
> Yes. We need two falgs for PUD and PMD huge pages separately.

A single flag should be enough, the order would be dependent on
whether args->pud_pfn or args->pmd_pfn is valid.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-22  6:41     ` Gavin Shan
@ 2021-07-22  7:11       ` Anshuman Khandual
  2021-07-23  1:00         ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-22  7:11 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/22/21 12:11 PM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/22/21 3:45 PM, Anshuman Khandual wrote:
>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>> This uses struct pgtable_debug_args in PMD modifying tests. The allocated
>>> huge page is used when set_pmd_at() is used. The corresponding tests
>>> are skipped if the huge page doesn't exist. Besides, the unused variable
>>> @pmd_aligned in debug_vm_pgtable() is dropped.
>>
>> Please dont drop @pmd_aligned just yet.
>>
> 
> We need do so. Otherwise, there is build warning to complain
> something like 'unused variable' after this patch is applied.
> 
>>>
>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>> ---
>>>   mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
>>>   1 file changed, 48 insertions(+), 54 deletions(-)
>>>
>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>> index eb6dda88e0d9..cec3cbf99a6b 100644
>>> --- a/mm/debug_vm_pgtable.c
>>> +++ b/mm/debug_vm_pgtable.c
>>> @@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>>>       WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>>>   }
>>>   -static void __init pmd_advanced_tests(struct mm_struct *mm,
>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>> -                      unsigned long pfn, unsigned long vaddr,
>>> -                      pgprot_t prot, pgtable_t pgtable)
>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
>>>   {
>>>       pmd_t pmd;
>>> +    unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
>>>         if (!has_transparent_hugepage())
>>>           return;
>>>         pr_debug("Validating PMD advanced\n");
>>> -    /* Align the address wrt HPAGE_PMD_SIZE */
>>> -    vaddr &= HPAGE_PMD_MASK;
>>
>> Please just leave these unchanged. If has_transparent_hugepage() evaluates
>> negative, it skips the masking operation. As mentioned earlier please avoid
>> changing the test in any manner during these transition patches.
>>
> 
> Ok.
> 
>>> +    if (args->pmd_pfn == ULONG_MAX) {
>>> +        pr_debug("%s: Skipped\n", __func__);
>>> +        return;
>>> +    }
>>
>> Just return. Please dont call out "Skipped".
>>
> 
> Ok.
> 
>>>   -    pgtable_trans_huge_deposit(mm, pmdp, pgtable);
>>> +    pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
>>>   -    pmd = pfn_pmd(pfn, prot);
>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>> -    pmdp_set_wrprotect(mm, vaddr, pmdp);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>> +    pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(pmd_write(pmd));
>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(!pmd_none(pmd));
>>>   -    pmd = pfn_pmd(pfn, prot);
>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>>       pmd = pmd_wrprotect(pmd);
>>>       pmd = pmd_mkclean(pmd);
>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>       pmd = pmd_mkwrite(pmd);
>>>       pmd = pmd_mkdirty(pmd);
>>> -    pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
>>> -    pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(!pmd_none(pmd));
>>>   -    pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
>>> +    pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
>>>       pmd = pmd_mkyoung(pmd);
>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>> -    pmdp_test_and_clear_young(vma, vaddr, pmdp);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>> +    pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(pmd_young(pmd));
>>>         /*  Clear the pte entries  */
>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>> -    pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
>>> +    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)
>>> @@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
>>>   #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 mm_struct *mm,
>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>> -                      unsigned long pfn, unsigned long vaddr,
>>> -                      pgprot_t prot, pgtable_t pgtable)
>>> -{
>>> -}
>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
>>>   static void __init pud_advanced_tests(struct mm_struct *mm,
>>>                         struct vm_area_struct *vma, pud_t *pudp,
>>>                         unsigned long pfn, unsigned long vaddr,
>>> @@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>>>   #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
>>>     #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>>>   {
>>>       pmd_t pmd;
>>>   -    if (!arch_vmap_pmd_supported(prot))
>>> +    if (!arch_vmap_pmd_supported(args->page_prot))
>>>           return;
>>>         pr_debug("Validating PMD huge\n");
>>> @@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>>        * X86 defined pmd_set_huge() verifies that the given
>>>        * PMD is not a populated non-leaf entry.
>>>        */
>>> -    WRITE_ONCE(*pmdp, __pmd(0));
>>> -    WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
>>> -    WARN_ON(!pmd_clear_huge(pmdp));
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    WRITE_ONCE(*(args->pmdp), __pmd(0));
>>
>> Possible extra braces.
>>
> 
> Will drop it in v4, thanks!
> 
>>> +    WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn),
>>> +                  args->page_prot));
>>
>> Dont break the line.
>>
> 
> Ok.
> 
>>> +    WARN_ON(!pmd_clear_huge(args->pmdp));
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(!pmd_none(pmd));
>>>   }
>>>   @@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>>>       WARN_ON(!pud_none(pud));
>>>   }
>>>   #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
>>>   static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>>>   #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>>   @@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
>>>       WARN_ON(!pte_none(pte));
>>>   }
>>>   -static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
>>> +static void __init pmd_clear_tests(struct pgtable_debug_args *args)
>>>   {
>>> -    pmd_t pmd = READ_ONCE(*pmdp);
>>> +    pmd_t pmd = READ_ONCE(*(args->pmdp));
>>>         pr_debug("Validating PMD clear\n");
>>>       pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
>>> -    WRITE_ONCE(*pmdp, pmd);
>>> -    pmd_clear(pmdp);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    WRITE_ONCE(*(args->pmdp), pmd);
>>> +    pmd_clear(args->pmdp);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(!pmd_none(pmd));
>>>   }
>>>   -static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>> -                      pgtable_t pgtable)
>>> +static void __init pmd_populate_tests(struct pgtable_debug_args *args)
>>>   {
>>>       pmd_t pmd;
>>>   @@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>>        * This entry points to next level page table page.
>>>        * Hence this must not qualify as pmd_bad().
>>>        */
>>> -    pmd_populate(mm, pmdp, pgtable);
>>> -    pmd = READ_ONCE(*pmdp);
>>> +    pmd_populate(args->mm, args->pmdp, args->start_ptep);
>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>       WARN_ON(pmd_bad(pmd));
>>>   }
>>>   @@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>>>       pgtable_t saved_ptep;
>>>       pgprot_t prot;
>>>       phys_addr_t paddr;
>>> -    unsigned long vaddr, pmd_aligned;
>>> +    unsigned long vaddr;
>>>       unsigned long pud_aligned;
>>>       spinlock_t *ptl = NULL;
>>>       int idx, ret;
>>> @@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
>>>        */
>>>       paddr = __pa_symbol(&start_kernel);
>>>   -    pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>>
>> Please dont drop these just yet and wait until [PATCH 11/12].
>>
> 
> Otherwise, it causes build warning: 'unused variable'.

Why ? Just the evaluation of 'pmd_aligned' from 'paddr' should
be enough to avoid such warning. 'pmd_aligned' need not be used
afterwards.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args
  2021-07-22  7:08           ` Anshuman Khandual
@ 2021-07-23  0:43             ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-23  0:43 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 5:08 PM, Anshuman Khandual wrote:
> On 7/22/21 11:53 AM, Gavin Shan wrote:
>> On 7/22/21 2:41 PM, Anshuman Khandual wrote:
>>> On 7/21/21 3:50 PM, Gavin Shan wrote:
>>>> On 7/21/21 3:44 PM, Anshuman Khandual wrote:
>>>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>>>> In debug_vm_pgtable(), there are many local variables introduced to
>>>>>> track the needed information and they are passed to the functions for
>>>>>> various test cases. It'd better to introduce a struct as place holder
>>>>>> for these information. With it, what the functions for various test
>>>>>> cases need is the struct, to simplify the code. It also makes code
>>>>>> easier to be maintained.
>>>>>>
>>>>>> Besides, set_xxx_at() could access the data on the corresponding pages
>>>>>> in the page table modifying tests. So the accessed pages in the tests
>>>>>> should have been allocated from buddy. Otherwise, we're accessing pages
>>>>>> that aren't owned by us. This causes issues like page flag corruption.
>>>>>>
>>>>>> This introduces "struct pgtable_debug_args". The struct is initialized
>>>>>> and destroyed, but the information in the struct isn't used yet. They
>>>>>> will be used in subsequent patches.
>>>>>>
>>>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>>>> ---
>>>>>>     mm/debug_vm_pgtable.c | 197 +++++++++++++++++++++++++++++++++++++++++-
>>>>>>     1 file changed, 196 insertions(+), 1 deletion(-)
>>>>>>

[...]

>>>
>>> IIRC it is also not guaranteed that PMD_SHIFT <= (MAX_ORDER - 1). Hence
>>> this same scheme should be followed for PMD level allocation as well.
>>>
>>
>> In theory, it's possible to have PMD_SHIFT <= (MAX_ORDER - 1) with misconfigured
>> kernel. I will apply the similar logic to PMD huge page in v4.
>>
>>>>       [... The code to release the PUD huge page needs changes based on @args->is_contiguous_pud_page]
>>>
>>> Right, a flag would be needed to call the appropriate free function.
>>>
>>
>> Yes. We need two falgs for PUD and PMD huge pages separately.
> 
> A single flag should be enough, the order would be dependent on
> whether args->pud_pfn or args->pmd_pfn is valid.
> 

Yes, it's correct that one flag is enough as we're sharing the PUD
or PMD huge page.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-22  7:11       ` Anshuman Khandual
@ 2021-07-23  1:00         ` Gavin Shan
  2021-07-23  2:33           ` Anshuman Khandual
  0 siblings, 1 reply; 43+ messages in thread
From: Gavin Shan @ 2021-07-23  1:00 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/22/21 5:11 PM, Anshuman Khandual wrote:
> On 7/22/21 12:11 PM, Gavin Shan wrote:
>> On 7/22/21 3:45 PM, Anshuman Khandual wrote:
>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>> This uses struct pgtable_debug_args in PMD modifying tests. The allocated
>>>> huge page is used when set_pmd_at() is used. The corresponding tests
>>>> are skipped if the huge page doesn't exist. Besides, the unused variable
>>>> @pmd_aligned in debug_vm_pgtable() is dropped.
>>>
>>> Please dont drop @pmd_aligned just yet.
>>>
>>
>> We need do so. Otherwise, there is build warning to complain
>> something like 'unused variable' after this patch is applied.
>>
>>>>
>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>> ---
>>>>    mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
>>>>    1 file changed, 48 insertions(+), 54 deletions(-)
>>>>
>>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>>> index eb6dda88e0d9..cec3cbf99a6b 100644
>>>> --- a/mm/debug_vm_pgtable.c
>>>> +++ b/mm/debug_vm_pgtable.c
>>>> @@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>>>>        WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>>>>    }
>>>>    -static void __init pmd_advanced_tests(struct mm_struct *mm,
>>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>>> -                      unsigned long pfn, unsigned long vaddr,
>>>> -                      pgprot_t prot, pgtable_t pgtable)
>>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
>>>>    {
>>>>        pmd_t pmd;
>>>> +    unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
>>>>          if (!has_transparent_hugepage())
>>>>            return;
>>>>          pr_debug("Validating PMD advanced\n");
>>>> -    /* Align the address wrt HPAGE_PMD_SIZE */
>>>> -    vaddr &= HPAGE_PMD_MASK;
>>>
>>> Please just leave these unchanged. If has_transparent_hugepage() evaluates
>>> negative, it skips the masking operation. As mentioned earlier please avoid
>>> changing the test in any manner during these transition patches.
>>>
>>
>> Ok.
>>
>>>> +    if (args->pmd_pfn == ULONG_MAX) {
>>>> +        pr_debug("%s: Skipped\n", __func__);
>>>> +        return;
>>>> +    }
>>>
>>> Just return. Please dont call out "Skipped".
>>>
>>
>> Ok.
>>
>>>>    -    pgtable_trans_huge_deposit(mm, pmdp, pgtable);
>>>> +    pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
>>>>    -    pmd = pfn_pmd(pfn, prot);
>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>> -    pmdp_set_wrprotect(mm, vaddr, pmdp);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>> +    pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(pmd_write(pmd));
>>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(!pmd_none(pmd));
>>>>    -    pmd = pfn_pmd(pfn, prot);
>>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>>>        pmd = pmd_wrprotect(pmd);
>>>>        pmd = pmd_mkclean(pmd);
>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>>        pmd = pmd_mkwrite(pmd);
>>>>        pmd = pmd_mkdirty(pmd);
>>>> -    pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
>>>> -    pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(!pmd_none(pmd));
>>>>    -    pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
>>>> +    pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
>>>>        pmd = pmd_mkyoung(pmd);
>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>> -    pmdp_test_and_clear_young(vma, vaddr, pmdp);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>> +    pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(pmd_young(pmd));
>>>>          /*  Clear the pte entries  */
>>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>>> -    pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
>>>> +    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)
>>>> @@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
>>>>    #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 mm_struct *mm,
>>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>>> -                      unsigned long pfn, unsigned long vaddr,
>>>> -                      pgprot_t prot, pgtable_t pgtable)
>>>> -{
>>>> -}
>>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
>>>>    static void __init pud_advanced_tests(struct mm_struct *mm,
>>>>                          struct vm_area_struct *vma, pud_t *pudp,
>>>>                          unsigned long pfn, unsigned long vaddr,
>>>> @@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>>>>    #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
>>>>      #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
>>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>>>>    {
>>>>        pmd_t pmd;
>>>>    -    if (!arch_vmap_pmd_supported(prot))
>>>> +    if (!arch_vmap_pmd_supported(args->page_prot))
>>>>            return;
>>>>          pr_debug("Validating PMD huge\n");
>>>> @@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>>>         * X86 defined pmd_set_huge() verifies that the given
>>>>         * PMD is not a populated non-leaf entry.
>>>>         */
>>>> -    WRITE_ONCE(*pmdp, __pmd(0));
>>>> -    WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
>>>> -    WARN_ON(!pmd_clear_huge(pmdp));
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    WRITE_ONCE(*(args->pmdp), __pmd(0));
>>>
>>> Possible extra braces.
>>>
>>
>> Will drop it in v4, thanks!
>>
>>>> +    WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn),
>>>> +                  args->page_prot));
>>>
>>> Dont break the line.
>>>
>>
>> Ok.
>>
>>>> +    WARN_ON(!pmd_clear_huge(args->pmdp));
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(!pmd_none(pmd));
>>>>    }
>>>>    @@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>>>>        WARN_ON(!pud_none(pud));
>>>>    }
>>>>    #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
>>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
>>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
>>>>    static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>>>>    #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>>>    @@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
>>>>        WARN_ON(!pte_none(pte));
>>>>    }
>>>>    -static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
>>>> +static void __init pmd_clear_tests(struct pgtable_debug_args *args)
>>>>    {
>>>> -    pmd_t pmd = READ_ONCE(*pmdp);
>>>> +    pmd_t pmd = READ_ONCE(*(args->pmdp));
>>>>          pr_debug("Validating PMD clear\n");
>>>>        pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
>>>> -    WRITE_ONCE(*pmdp, pmd);
>>>> -    pmd_clear(pmdp);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    WRITE_ONCE(*(args->pmdp), pmd);
>>>> +    pmd_clear(args->pmdp);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(!pmd_none(pmd));
>>>>    }
>>>>    -static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>>> -                      pgtable_t pgtable)
>>>> +static void __init pmd_populate_tests(struct pgtable_debug_args *args)
>>>>    {
>>>>        pmd_t pmd;
>>>>    @@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>>>         * This entry points to next level page table page.
>>>>         * Hence this must not qualify as pmd_bad().
>>>>         */
>>>> -    pmd_populate(mm, pmdp, pgtable);
>>>> -    pmd = READ_ONCE(*pmdp);
>>>> +    pmd_populate(args->mm, args->pmdp, args->start_ptep);
>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>        WARN_ON(pmd_bad(pmd));
>>>>    }
>>>>    @@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>>>>        pgtable_t saved_ptep;
>>>>        pgprot_t prot;
>>>>        phys_addr_t paddr;
>>>> -    unsigned long vaddr, pmd_aligned;
>>>> +    unsigned long vaddr;
>>>>        unsigned long pud_aligned;
>>>>        spinlock_t *ptl = NULL;
>>>>        int idx, ret;
>>>> @@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
>>>>         */
>>>>        paddr = __pa_symbol(&start_kernel);
>>>>    -    pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>>>
>>> Please dont drop these just yet and wait until [PATCH 11/12].
>>>
>>
>> Otherwise, it causes build warning: 'unused variable'.
> 
> Why ? Just the evaluation of 'pmd_aligned' from 'paddr' should
> be enough to avoid such warning. 'pmd_aligned' need not be used
> afterwards.
> 

It's not enough to avoid the warning. I apply the patches till
this one (PATCH[v3 08/12]) and have additional code to keep
@pmd_aligned, then I run into build warning as below:

diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index cec3cbf99a6b..961c9bb6fc7c 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -1154,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
         pgtable_t saved_ptep;
         pgprot_t prot;
         phys_addr_t paddr;
-       unsigned long vaddr;
+       unsigned long vaddr, pmd_aligned;
         unsigned long pud_aligned;
         spinlock_t *ptl = NULL;
         int idx, ret;
@@ -1189,6 +1189,7 @@ static int __init debug_vm_pgtable(void)
          */
         paddr = __pa_symbol(&start_kernel);
  
+       pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
         pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
  
         pgdp = pgd_offset(mm, vaddr);

[gwshan@gshan l]$ make W=1 mm/debug_vm_pgtable.o
      :
mm/debug_vm_pgtable.c: In function ‘debug_vm_pgtable’:
mm/debug_vm_pgtable.c:1157:23: warning: variable ‘pmd_aligned’ set but not used [-Wunused-but-set-variable]
  1157 |  unsigned long vaddr, pmd_aligned;
       |                       ^~~~~~~~~~~


By the way, 0-day is trying to build kernel with "W=1". It means the
build warnings will be reported by 0-day if the unused variables aren't
dropped from individual patches. It makes the review a bit harder. However,
we still need to keep each individual patch complete to make 'git bisect'
friendly.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests
  2021-07-23  1:00         ` Gavin Shan
@ 2021-07-23  2:33           ` Anshuman Khandual
  0 siblings, 0 replies; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-23  2:33 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/23/21 6:30 AM, Gavin Shan wrote:
> Hi Anshuman,
> 
> On 7/22/21 5:11 PM, Anshuman Khandual wrote:
>> On 7/22/21 12:11 PM, Gavin Shan wrote:
>>> On 7/22/21 3:45 PM, Anshuman Khandual wrote:
>>>> On 7/19/21 6:36 PM, Gavin Shan wrote:
>>>>> This uses struct pgtable_debug_args in PMD modifying tests. The allocated
>>>>> huge page is used when set_pmd_at() is used. The corresponding tests
>>>>> are skipped if the huge page doesn't exist. Besides, the unused variable
>>>>> @pmd_aligned in debug_vm_pgtable() is dropped.
>>>>
>>>> Please dont drop @pmd_aligned just yet.
>>>>
>>>
>>> We need do so. Otherwise, there is build warning to complain
>>> something like 'unused variable' after this patch is applied.
>>>
>>>>>
>>>>> Signed-off-by: Gavin Shan <gshan@redhat.com>
>>>>> ---
>>>>>    mm/debug_vm_pgtable.c | 102 ++++++++++++++++++++----------------------
>>>>>    1 file changed, 48 insertions(+), 54 deletions(-)
>>>>>
>>>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>>>> index eb6dda88e0d9..cec3cbf99a6b 100644
>>>>> --- a/mm/debug_vm_pgtable.c
>>>>> +++ b/mm/debug_vm_pgtable.c
>>>>> @@ -213,54 +213,54 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
>>>>>        WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));
>>>>>    }
>>>>>    -static void __init pmd_advanced_tests(struct mm_struct *mm,
>>>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>>>> -                      unsigned long pfn, unsigned long vaddr,
>>>>> -                      pgprot_t prot, pgtable_t pgtable)
>>>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
>>>>>    {
>>>>>        pmd_t pmd;
>>>>> +    unsigned long vaddr = (args->vaddr & HPAGE_PMD_MASK);
>>>>>          if (!has_transparent_hugepage())
>>>>>            return;
>>>>>          pr_debug("Validating PMD advanced\n");
>>>>> -    /* Align the address wrt HPAGE_PMD_SIZE */
>>>>> -    vaddr &= HPAGE_PMD_MASK;
>>>>
>>>> Please just leave these unchanged. If has_transparent_hugepage() evaluates
>>>> negative, it skips the masking operation. As mentioned earlier please avoid
>>>> changing the test in any manner during these transition patches.
>>>>
>>>
>>> Ok.
>>>
>>>>> +    if (args->pmd_pfn == ULONG_MAX) {
>>>>> +        pr_debug("%s: Skipped\n", __func__);
>>>>> +        return;
>>>>> +    }
>>>>
>>>> Just return. Please dont call out "Skipped".
>>>>
>>>
>>> Ok.
>>>
>>>>>    -    pgtable_trans_huge_deposit(mm, pmdp, pgtable);
>>>>> +    pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep);
>>>>>    -    pmd = pfn_pmd(pfn, prot);
>>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>>> -    pmdp_set_wrprotect(mm, vaddr, pmdp);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>>> +    pmdp_set_wrprotect(args->mm, vaddr, args->pmdp);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(pmd_write(pmd));
>>>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(!pmd_none(pmd));
>>>>>    -    pmd = pfn_pmd(pfn, prot);
>>>>> +    pmd = pfn_pmd(args->pmd_pfn, args->page_prot);
>>>>>        pmd = pmd_wrprotect(pmd);
>>>>>        pmd = pmd_mkclean(pmd);
>>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>>>        pmd = pmd_mkwrite(pmd);
>>>>>        pmd = pmd_mkdirty(pmd);
>>>>> -    pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
>>>>> -    pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(!pmd_none(pmd));
>>>>>    -    pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
>>>>> +    pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot));
>>>>>        pmd = pmd_mkyoung(pmd);
>>>>> -    set_pmd_at(mm, vaddr, pmdp, pmd);
>>>>> -    pmdp_test_and_clear_young(vma, vaddr, pmdp);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    set_pmd_at(args->mm, vaddr, args->pmdp, pmd);
>>>>> +    pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(pmd_young(pmd));
>>>>>          /*  Clear the pte entries  */
>>>>> -    pmdp_huge_get_and_clear(mm, vaddr, pmdp);
>>>>> -    pgtable = pgtable_trans_huge_withdraw(mm, pmdp);
>>>>> +    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)
>>>>> @@ -417,12 +417,7 @@ static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }
>>>>>    #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 mm_struct *mm,
>>>>> -                      struct vm_area_struct *vma, pmd_t *pmdp,
>>>>> -                      unsigned long pfn, unsigned long vaddr,
>>>>> -                      pgprot_t prot, pgtable_t pgtable)
>>>>> -{
>>>>> -}
>>>>> +static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }
>>>>>    static void __init pud_advanced_tests(struct mm_struct *mm,
>>>>>                          struct vm_area_struct *vma, pud_t *pudp,
>>>>>                          unsigned long pfn, unsigned long vaddr,
>>>>> @@ -435,11 +430,11 @@ static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }
>>>>>    #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
>>>>>      #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
>>>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args)
>>>>>    {
>>>>>        pmd_t pmd;
>>>>>    -    if (!arch_vmap_pmd_supported(prot))
>>>>> +    if (!arch_vmap_pmd_supported(args->page_prot))
>>>>>            return;
>>>>>          pr_debug("Validating PMD huge\n");
>>>>> @@ -447,10 +442,11 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot)
>>>>>         * X86 defined pmd_set_huge() verifies that the given
>>>>>         * PMD is not a populated non-leaf entry.
>>>>>         */
>>>>> -    WRITE_ONCE(*pmdp, __pmd(0));
>>>>> -    WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot));
>>>>> -    WARN_ON(!pmd_clear_huge(pmdp));
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    WRITE_ONCE(*(args->pmdp), __pmd(0));
>>>>
>>>> Possible extra braces.
>>>>
>>>
>>> Will drop it in v4, thanks!
>>>
>>>>> +    WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn),
>>>>> +                  args->page_prot));
>>>>
>>>> Dont break the line.
>>>>
>>>
>>> Ok.
>>>
>>>>> +    WARN_ON(!pmd_clear_huge(args->pmdp));
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(!pmd_none(pmd));
>>>>>    }
>>>>>    @@ -473,7 +469,7 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot)
>>>>>        WARN_ON(!pud_none(pud));
>>>>>    }
>>>>>    #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
>>>>> -static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) { }
>>>>> +static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }
>>>>>    static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) { }
>>>>>    #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
>>>>>    @@ -640,20 +636,19 @@ static void __init pte_clear_tests(struct pgtable_debug_args *args)
>>>>>        WARN_ON(!pte_none(pte));
>>>>>    }
>>>>>    -static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp)
>>>>> +static void __init pmd_clear_tests(struct pgtable_debug_args *args)
>>>>>    {
>>>>> -    pmd_t pmd = READ_ONCE(*pmdp);
>>>>> +    pmd_t pmd = READ_ONCE(*(args->pmdp));
>>>>>          pr_debug("Validating PMD clear\n");
>>>>>        pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE);
>>>>> -    WRITE_ONCE(*pmdp, pmd);
>>>>> -    pmd_clear(pmdp);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    WRITE_ONCE(*(args->pmdp), pmd);
>>>>> +    pmd_clear(args->pmdp);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(!pmd_none(pmd));
>>>>>    }
>>>>>    -static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>>>> -                      pgtable_t pgtable)
>>>>> +static void __init pmd_populate_tests(struct pgtable_debug_args *args)
>>>>>    {
>>>>>        pmd_t pmd;
>>>>>    @@ -662,8 +657,8 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
>>>>>         * This entry points to next level page table page.
>>>>>         * Hence this must not qualify as pmd_bad().
>>>>>         */
>>>>> -    pmd_populate(mm, pmdp, pgtable);
>>>>> -    pmd = READ_ONCE(*pmdp);
>>>>> +    pmd_populate(args->mm, args->pmdp, args->start_ptep);
>>>>> +    pmd = READ_ONCE(*(args->pmdp));
>>>>>        WARN_ON(pmd_bad(pmd));
>>>>>    }
>>>>>    @@ -1159,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>>>>>        pgtable_t saved_ptep;
>>>>>        pgprot_t prot;
>>>>>        phys_addr_t paddr;
>>>>> -    unsigned long vaddr, pmd_aligned;
>>>>> +    unsigned long vaddr;
>>>>>        unsigned long pud_aligned;
>>>>>        spinlock_t *ptl = NULL;
>>>>>        int idx, ret;
>>>>> @@ -1194,7 +1189,6 @@ static int __init debug_vm_pgtable(void)
>>>>>         */
>>>>>        paddr = __pa_symbol(&start_kernel);
>>>>>    -    pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>>>>
>>>> Please dont drop these just yet and wait until [PATCH 11/12].
>>>>
>>>
>>> Otherwise, it causes build warning: 'unused variable'.
>>
>> Why ? Just the evaluation of 'pmd_aligned' from 'paddr' should
>> be enough to avoid such warning. 'pmd_aligned' need not be used
>> afterwards.
>>
> 
> It's not enough to avoid the warning. I apply the patches till
> this one (PATCH[v3 08/12]) and have additional code to keep
> @pmd_aligned, then I run into build warning as below:
> 
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index cec3cbf99a6b..961c9bb6fc7c 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -1154,7 +1154,7 @@ static int __init debug_vm_pgtable(void)
>         pgtable_t saved_ptep;
>         pgprot_t prot;
>         phys_addr_t paddr;
> -       unsigned long vaddr;
> +       unsigned long vaddr, pmd_aligned;
>         unsigned long pud_aligned;
>         spinlock_t *ptl = NULL;
>         int idx, ret;
> @@ -1189,6 +1189,7 @@ static int __init debug_vm_pgtable(void)
>          */
>         paddr = __pa_symbol(&start_kernel);
>  
> +       pmd_aligned = (paddr & PMD_MASK) >> PAGE_SHIFT;
>         pud_aligned = (paddr & PUD_MASK) >> PAGE_SHIFT;
>  
>         pgdp = pgd_offset(mm, vaddr);
> 
> [gwshan@gshan l]$ make W=1 mm/debug_vm_pgtable.o
>      :
> mm/debug_vm_pgtable.c: In function ‘debug_vm_pgtable’:
> mm/debug_vm_pgtable.c:1157:23: warning: variable ‘pmd_aligned’ set but not used [-Wunused-but-set-variable]
>  1157 |  unsigned long vaddr, pmd_aligned;
>       |                       ^~~~~~~~~~~
> 
> 
> By the way, 0-day is trying to build kernel with "W=1". It means the
> build warnings will be reported by 0-day if the unused variables aren't
> dropped from individual patches. It makes the review a bit harder. However,

It is not about keeping the review process simple. But rather the proposed
patch changing as much what is really required and nothing else.

> we still need to keep each individual patch complete to make 'git bisect'
> friendly.

With W=1 if this throws up a build error, I guess we dont have choice here.
We need to keep the build clean for each individual patch applies.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests
  2021-07-22  6:37     ` Gavin Shan
@ 2021-07-23  2:39       ` Anshuman Khandual
  2021-07-23  4:23         ` Gavin Shan
  0 siblings, 1 reply; 43+ messages in thread
From: Anshuman Khandual @ 2021-07-23  2:39 UTC (permalink / raw)
  To: Gavin Shan, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin



On 7/22/21 12:07 PM, Gavin Shan wrote:
> 
>>>         pgdp = pgd_offset(mm, vaddr);
>>>       p4dp = p4d_alloc(mm, pgdp, vaddr);
>>> @@ -1272,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>>>        * Page table modifying tests. They need to hold
>>>        * proper page table lock.
>>>        */
>>> -
>>> -    ptep = pte_offset_map_lock(mm, pmdp, vaddr, &ptl);
>>> -    pte_clear_tests(mm, ptep, pte_aligned, vaddr, prot);
>>> -    pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>>> -    pte_unmap_unlock(ptep, ptl);
>>> +    ptl = pte_lockptr(args.mm, args.pmdp);
>>> +    spin_lock(ptl);
>>> +    pte_clear_tests(&args);
>>> +    pte_advanced_tests(&args);
>>> +    spin_unlock(ptl);
>>
>> Why pte_offset_map_lock()/pte_unmap_unlock() has been dropped and
>> spin_lock()/spin_unlock() sequence has been added ? Please dont
>> change the tests in these patches.
>>
> 
> The semantics of pte_offset_map_lock() is to grab and take the lock
> and return the PTE entry, which is mapped if needed. We already had
> the PTE entry tracked by args->ptep in init_args(). So some of the
> operations covered by pte_offset_map_lock() isn't needed any more

To keep the patch on purpose, please avoid this change here. But if
required, you could send a follow up patch later.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests
  2021-07-23  2:39       ` Anshuman Khandual
@ 2021-07-23  4:23         ` Gavin Shan
  0 siblings, 0 replies; 43+ messages in thread
From: Gavin Shan @ 2021-07-23  4:23 UTC (permalink / raw)
  To: Anshuman Khandual, linux-mm
  Cc: linux-kernel, catalin.marinas, will, akpm, chuhu, shan.gavin

Hi Anshuman,

On 7/23/21 12:39 PM, Anshuman Khandual wrote:
> On 7/22/21 12:07 PM, Gavin Shan wrote:
>>
>>>>          pgdp = pgd_offset(mm, vaddr);
>>>>        p4dp = p4d_alloc(mm, pgdp, vaddr);
>>>> @@ -1272,11 +1275,11 @@ static int __init debug_vm_pgtable(void)
>>>>         * Page table modifying tests. They need to hold
>>>>         * proper page table lock.
>>>>         */
>>>> -
>>>> -    ptep = pte_offset_map_lock(mm, pmdp, vaddr, &ptl);
>>>> -    pte_clear_tests(mm, ptep, pte_aligned, vaddr, prot);
>>>> -    pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>>>> -    pte_unmap_unlock(ptep, ptl);
>>>> +    ptl = pte_lockptr(args.mm, args.pmdp);
>>>> +    spin_lock(ptl);
>>>> +    pte_clear_tests(&args);
>>>> +    pte_advanced_tests(&args);
>>>> +    spin_unlock(ptl);
>>>
>>> Why pte_offset_map_lock()/pte_unmap_unlock() has been dropped and
>>> spin_lock()/spin_unlock() sequence has been added ? Please dont
>>> change the tests in these patches.
>>>
>>
>> The semantics of pte_offset_map_lock() is to grab and take the lock
>> and return the PTE entry, which is mapped if needed. We already had
>> the PTE entry tracked by args->ptep in init_args(). So some of the
>> operations covered by pte_offset_map_lock() isn't needed any more
> 
> To keep the patch on purpose, please avoid this change here. But if
> required, you could send a follow up patch later.
> 

In order to use pte_offset_map_lock() and pte_unmap_unlock(), we need
a temporary variable @ptep to store the return value from pte_offset_map_lock().
The temporary variable @ptep is passed to pte_unmap_unlock(). It means
we just need the temporary variable to keep the original implementation
in this regard. I will keep pte_offset_map_lock() and pte_unmap_unlock()
in v4.

If we really want to remove the temporary variable (@ptep), we can do it
after this series gets merged.

Thanks,
Gavin


^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2021-07-23  4:23 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-19 13:06 [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements Gavin Shan
2021-07-19 13:06 ` [PATCH v3 01/12] mm/debug_vm_pgtable: Introduce struct pgtable_debug_args Gavin Shan
2021-07-21  5:44   ` Anshuman Khandual
2021-07-21 10:20     ` Gavin Shan
2021-07-21 10:39       ` Anshuman Khandual
2021-07-21 10:59         ` Anshuman Khandual
2021-07-21 11:59           ` Gavin Shan
2021-07-22  4:41       ` Anshuman Khandual
2021-07-22  6:23         ` Gavin Shan
2021-07-22  7:08           ` Anshuman Khandual
2021-07-23  0:43             ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 02/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in basic tests Gavin Shan
2021-07-21  5:57   ` Anshuman Khandual
2021-07-21 11:57     ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 03/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in leaf and savewrite tests Gavin Shan
2021-07-19 13:06 ` [PATCH v3 04/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and devmap tests Gavin Shan
2021-07-19 13:06 ` [PATCH v3 05/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in soft_dirty and swap tests Gavin Shan
2021-07-19 13:06 ` [PATCH v3 06/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and thp tests Gavin Shan
2021-07-19 13:06 ` [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests Gavin Shan
2021-07-22  5:56   ` Anshuman Khandual
2021-07-22  6:37     ` Gavin Shan
2021-07-23  2:39       ` Anshuman Khandual
2021-07-23  4:23         ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD " Gavin Shan
2021-07-22  5:45   ` Anshuman Khandual
2021-07-22  6:41     ` Gavin Shan
2021-07-22  7:11       ` Anshuman Khandual
2021-07-23  1:00         ` Gavin Shan
2021-07-23  2:33           ` Anshuman Khandual
2021-07-19 13:06 ` [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD " Gavin Shan
2021-07-22  5:39   ` Anshuman Khandual
2021-07-22  6:47     ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D " Gavin Shan
2021-07-22  5:09   ` Anshuman Khandual
2021-07-22  6:50     ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code Gavin Shan
2021-07-22  4:51   ` Anshuman Khandual
2021-07-22  6:53     ` Gavin Shan
2021-07-19 13:06 ` [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag Gavin Shan
2021-07-21 10:18   ` Anshuman Khandual
2021-07-21 12:03     ` Gavin Shan
2021-07-22  3:51       ` Anshuman Khandual
2021-07-22  6:54         ` Gavin Shan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).