diff mbox series

[v3,07/12] mm/debug_vm_pgtable: Use struct pgtable_debug_args in PTE modifying tests

Message ID 20210719130613.334901-8-gshan@redhat.com (mailing list archive)
State New
Headers show
Series mm/debug_vm_pgtable: Enhancements | expand

Commit Message

Gavin Shan July 19, 2021, 1:06 p.m. UTC
This uses struct pgtable_debug_args in PTE modifying tests. The allocated
page is used as set_pte_at() is used there. The tests are skipped if
the allocated page doesn't exist. Besides, the unused variable @ptep
and @pte_aligned in debug_vm_pgtable() are dropped.

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

Comments

Anshuman Khandual July 22, 2021, 5:56 a.m. UTC | #1
On 7/19/21 6:36 PM, Gavin Shan wrote:
> This uses struct pgtable_debug_args in PTE modifying tests. The allocated
> page is used as set_pte_at() is used there. The tests are skipped if
> the allocated page doesn't exist. Besides, the unused variable @ptep
> and @pte_aligned in debug_vm_pgtable() are dropped.

Please dont drop @ptep and @pte_aligned just yet.

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

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

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

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

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

Please keep this unchanged and move to its original position.

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

Please dont drop pte_aligned just yet.

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

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

WARN_ON(!pfn_valid(args->fixed_pte_pfn))

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

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

>  
>  	ptl = pmd_lock(mm, pmdp);
>  	pmd_clear_tests(mm, pmdp);
>
Gavin Shan July 22, 2021, 6:37 a.m. UTC | #2
Hi Anshuman,

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

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

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

Sure.

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

Sure.

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

Ok.

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

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

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

Yes.

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

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

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

Thanks,
Gavin
Anshuman Khandual July 23, 2021, 2:39 a.m. UTC | #3
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.
Gavin Shan July 23, 2021, 4:23 a.m. UTC | #4
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
diff mbox series

Patch

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);