2021-07-19 13:08:47

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 00/12] mm/debug_vm_pgtable: Enhancements

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


2021-07-19 13:08:55

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 06/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in migration and thp tests

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 <[email protected]>
---
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

2021-07-19 13:09:07

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 04/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in protnone and devmap tests

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 <[email protected]>
---
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

2021-07-19 13:09:16

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests

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 <[email protected]>
---
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

2021-07-19 13:09:28

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code

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 <[email protected]>
---
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

2021-07-19 13:09:28

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag

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 <[email protected]>
---
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

2021-07-19 13:10:15

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests

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 <[email protected]>
---
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

2021-07-19 13:10:17

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests

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 <[email protected]>
---
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

2021-07-19 13:10:25

by Gavin Shan

[permalink] [raw]
Subject: [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests

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 <[email protected]>
---
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

2021-07-21 10:38:36

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag



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.

2021-07-21 12:08:48

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag

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

2021-07-22 03:54:31

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag

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.

2021-07-22 04:53:09

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code

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 <[email protected]>
> ---
> 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;
> }
>

2021-07-22 05:11:55

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests



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 <[email protected]>
> ---
> 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);
>
>

2021-07-22 05:43:03

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests


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 <[email protected]>
> ---
> 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);
>

2021-07-22 05:47:03

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests



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 <[email protected]>
> ---
> 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);
>

2021-07-22 05:58:06

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests



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 <[email protected]>
> ---
> 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);
>

2021-07-22 06:38:29

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests

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 <[email protected]>
>> ---
>> 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

2021-07-22 06:42:59

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests

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 <[email protected]>
>> ---
>> 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

2021-07-22 06:49:23

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 09/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PUD modifying tests

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 <[email protected]>
>> ---
>> 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

2021-07-22 06:52:09

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 10/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PGD and P4D modifying tests

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 <[email protected]>
>> ---
>> 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


2021-07-22 06:55:02

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 11/12] mm/debug_vm_pgtable: Remove unused code

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 <[email protected]>
>> ---
>> 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

2021-07-22 06:57:10

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 12/12] mm/debug_vm_pgtable: Fix corrupted page flag

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

2021-07-22 07:11:43

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests



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 <[email protected]>
>>> ---
>>>   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.

2021-07-23 01:04:37

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests

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 <[email protected]>
>>>> ---
>>>>   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

2021-07-23 02:34:17

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PMD modifying tests



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 <[email protected]>
>>>>> ---
>>>>>    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.

2021-07-23 02:40:47

by Anshuman Khandual

[permalink] [raw]
Subject: Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests



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.

2021-07-23 04:24:11

by Gavin Shan

[permalink] [raw]
Subject: Re: [PATCH v3 07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests

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