* [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
@ 2025-05-15 6:34 Dev Jain
2025-05-15 8:13 ` David Hildenbrand
0 siblings, 1 reply; 16+ messages in thread
From: Dev Jain @ 2025-05-15 6:34 UTC (permalink / raw)
To: catalin.marinas, will
Cc: david, ryan.roberts, anshuman.khandual, mark.rutland, yang,
linux-kernel, linux-arm-kernel, Dev Jain, stable
Commit 9c006972c3fe removes the pxd_present() checks because the caller
checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller only
checks pud_present(); pud_free_pmd_page() recurses on each pmd through
pmd_free_pte_page(), wherein the pmd may be none. Thus it is possible to
hit a warning in the latter, since pmd_none => !pmd_table(). Thus, enforce
these checks again through pxd_leaf().
This problem was found by code inspection.
The patch is based on 6.15-rc6.
Fixes: 9c006972c3fe (arm64: mmu: drop pXd_present() checks from pXd_free_pYd_table())
Cc: <stable@vger.kernel.org>
Reported-by: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: Dev Jain <dev.jain@arm.com>
---
arch/arm64/mm/mmu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index ea6695d53fb9..3d6789413a9b 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1255,7 +1255,7 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
pmd = READ_ONCE(*pmdp);
- if (!pmd_table(pmd)) {
+ if (pmd_leaf(pmd)) {
VM_WARN_ON(1);
return 1;
}
@@ -1276,7 +1276,7 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
pud = READ_ONCE(*pudp);
- if (!pud_table(pud)) {
+ if (pud_leaf(pud)) {
VM_WARN_ON(1);
return 1;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 6:34 [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables Dev Jain
@ 2025-05-15 8:13 ` David Hildenbrand
2025-05-15 8:22 ` Dev Jain
0 siblings, 1 reply; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 8:13 UTC (permalink / raw)
To: Dev Jain, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 08:34, Dev Jain wrote:
> Commit 9c006972c3fe removes the pxd_present() checks because the caller
> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller only
> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
> pmd_free_pte_page(), wherein the pmd may be none.
The commit states: "The core code already has a check for pXd_none()",
so I assume that assumption was not true in all cases?
Should that one problematic caller then check for pmd_none() instead?
If you were able to trigger this WARN, it's always a good idea to
include the splat in the commit.
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:13 ` David Hildenbrand
@ 2025-05-15 8:22 ` Dev Jain
2025-05-15 8:36 ` David Hildenbrand
0 siblings, 1 reply; 16+ messages in thread
From: Dev Jain @ 2025-05-15 8:22 UTC (permalink / raw)
To: David Hildenbrand, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/25 1:43 pm, David Hildenbrand wrote:
> On 15.05.25 08:34, Dev Jain wrote:
>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>> only
>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>> pmd_free_pte_page(), wherein the pmd may be none.
> The commit states: "The core code already has a check for pXd_none()",
> so I assume that assumption was not true in all cases?
>
> Should that one problematic caller then check for pmd_none() instead?
From what I could gather of Will's commit message, my interpretation is
that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
These individually check for pxd_present():
if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
return 0;
The problem is that vmap_try_huge_pud will also iterate on pte entries.
So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
may encounter a none pmd and trigger a WARN.
>
> If you were able to trigger this WARN, it's always a good idea to
> include the splat in the commit.
I wasn't able to, it is just an observation from code inspection.
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:22 ` Dev Jain
@ 2025-05-15 8:36 ` David Hildenbrand
2025-05-15 8:40 ` Dev Jain
2025-05-15 8:47 ` Dev Jain
0 siblings, 2 replies; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 8:36 UTC (permalink / raw)
To: Dev Jain, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 10:22, Dev Jain wrote:
>
>
> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>> On 15.05.25 08:34, Dev Jain wrote:
>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>> only
>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>> pmd_free_pte_page(), wherein the pmd may be none.
>> The commit states: "The core code already has a check for pXd_none()",
>> so I assume that assumption was not true in all cases?
>>
>> Should that one problematic caller then check for pmd_none() instead?
>
> From what I could gather of Will's commit message, my interpretation is
> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
> These individually check for pxd_present():
>
> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
> return 0;
>
> The problem is that vmap_try_huge_pud will also iterate on pte entries.
> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
> may encounter a none pmd and trigger a WARN.
Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
I assume we should either have an explicit pmd_none() check in
pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
pmd_free_pte_page().
With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
which sounds wrong -- unless I am missing something important.
>
>>
>> If you were able to trigger this WARN, it's always a good idea to
>> include the splat in the commit.
>
> I wasn't able to, it is just an observation from code inspection.
That better be included in the patch description :)
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:36 ` David Hildenbrand
@ 2025-05-15 8:40 ` Dev Jain
2025-05-15 8:49 ` David Hildenbrand
2025-05-15 8:47 ` Dev Jain
1 sibling, 1 reply; 16+ messages in thread
From: Dev Jain @ 2025-05-15 8:40 UTC (permalink / raw)
To: David Hildenbrand, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/25 2:06 pm, David Hildenbrand wrote:
> On 15.05.25 10:22, Dev Jain wrote:
>>
>>
>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>> On 15.05.25 08:34, Dev Jain wrote:
>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>> only
>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>> The commit states: "The core code already has a check for pXd_none()",
>>> so I assume that assumption was not true in all cases?
>>>
>>> Should that one problematic caller then check for pmd_none() instead?
>>
>> From what I could gather of Will's commit message, my interpretation is
>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>> These individually check for pxd_present():
>>
>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>> return 0;
>>
>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>> may encounter a none pmd and trigger a WARN.
>
> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>
> I assume we should either have an explicit pmd_none() check in
> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
> pmd_free_pte_page().
>
> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
> which sounds wrong -- unless I am missing something important.
>
>>
>>>
>>> If you were able to trigger this WARN, it's always a good idea to
>>> include the splat in the commit.
>>
>> I wasn't able to, it is just an observation from code inspection.
>
> That better be included in the patch description :)
I did, actually. My bad for not putting in spaces, I notice now that the
description looks horrible to the eye :)
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:36 ` David Hildenbrand
2025-05-15 8:40 ` Dev Jain
@ 2025-05-15 8:47 ` Dev Jain
2025-05-15 8:53 ` David Hildenbrand
1 sibling, 1 reply; 16+ messages in thread
From: Dev Jain @ 2025-05-15 8:47 UTC (permalink / raw)
To: David Hildenbrand, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/25 2:06 pm, David Hildenbrand wrote:
> On 15.05.25 10:22, Dev Jain wrote:
>>
>>
>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>> On 15.05.25 08:34, Dev Jain wrote:
>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>> only
>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>> The commit states: "The core code already has a check for pXd_none()",
>>> so I assume that assumption was not true in all cases?
>>>
>>> Should that one problematic caller then check for pmd_none() instead?
>>
>> From what I could gather of Will's commit message, my interpretation is
>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>> These individually check for pxd_present():
>>
>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>> return 0;
>>
>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>> may encounter a none pmd and trigger a WARN.
>
> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>
> I assume we should either have an explicit pmd_none() check in
> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
> pmd_free_pte_page().
>
> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
> which sounds wrong -- unless I am missing something important.
Ah thanks, you seem to be right. We will be extracting table from a none
pmd. Perhaps we should still bail out for !pxd_present() but without the
warning, which the fix commit used to do.
>
>>
>>>
>>> If you were able to trigger this WARN, it's always a good idea to
>>> include the splat in the commit.
>>
>> I wasn't able to, it is just an observation from code inspection.
>
> That better be included in the patch description :)
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:40 ` Dev Jain
@ 2025-05-15 8:49 ` David Hildenbrand
0 siblings, 0 replies; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 8:49 UTC (permalink / raw)
To: Dev Jain, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 10:40, Dev Jain wrote:
>
>
> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>> On 15.05.25 10:22, Dev Jain wrote:
>>>
>>>
>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>> only
>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>> The commit states: "The core code already has a check for pXd_none()",
>>>> so I assume that assumption was not true in all cases?
>>>>
>>>> Should that one problematic caller then check for pmd_none() instead?
>>>
>>> From what I could gather of Will's commit message, my interpretation is
>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>> These individually check for pxd_present():
>>>
>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>> return 0;
>>>
>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>> may encounter a none pmd and trigger a WARN.
>>
>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>
>> I assume we should either have an explicit pmd_none() check in
>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>> pmd_free_pte_page().
>>
>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>> which sounds wrong -- unless I am missing something important.
>>
>>>
>>>>
>>>> If you were able to trigger this WARN, it's always a good idea to
>>>> include the splat in the commit.
>>>
>>> I wasn't able to, it is just an observation from code inspection.
>>
>> That better be included in the patch description :)
>
> I did, actually. My bad for not putting in spaces, I notice now that the
> description looks horrible to the eye :)
Ahh, there it is. Sorry :) Yeah, some empty lines won't hurt!
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:47 ` Dev Jain
@ 2025-05-15 8:53 ` David Hildenbrand
2025-05-15 9:27 ` Dev Jain
2025-05-15 10:07 ` Ryan Roberts
0 siblings, 2 replies; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 8:53 UTC (permalink / raw)
To: Dev Jain, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 10:47, Dev Jain wrote:
>
>
> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>> On 15.05.25 10:22, Dev Jain wrote:
>>>
>>>
>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>> only
>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>> The commit states: "The core code already has a check for pXd_none()",
>>>> so I assume that assumption was not true in all cases?
>>>>
>>>> Should that one problematic caller then check for pmd_none() instead?
>>>
>>> From what I could gather of Will's commit message, my interpretation is
>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>> These individually check for pxd_present():
>>>
>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>> return 0;
>>>
>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>> may encounter a none pmd and trigger a WARN.
>>
>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>
>> I assume we should either have an explicit pmd_none() check in
>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>> pmd_free_pte_page().
>>
>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>> which sounds wrong -- unless I am missing something important.
>
> Ah thanks, you seem to be right. We will be extracting table from a none
> pmd. Perhaps we should still bail out for !pxd_present() but without the
> warning, which the fix commit used to do.
Right. We just make sure that all callers of pmd_free_pte_page() already check for it.
I'd just do something like:
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 8fcf59ba39db7..e98dd7af147d5 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
pmd = READ_ONCE(*pmdp);
- if (!pmd_table(pmd)) {
- VM_WARN_ON(1);
- return 1;
- }
+ VM_WARN_ON(!pmd_present(pmd));
+ VM_WARN_ON(!pmd_table(pmd));
table = pte_offset_kernel(pmdp, addr);
pmd_clear(pmdp);
@@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
next = addr;
end = addr + PUD_SIZE;
do {
- pmd_free_pte_page(pmdp, next);
+ if (pmd_present(*pmdp))
+ pmd_free_pte_page(pmdp, next);
} while (pmdp++, next += PMD_SIZE, next != end);
pud_clear(pudp);
--
Cheers,
David / dhildenb
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:53 ` David Hildenbrand
@ 2025-05-15 9:27 ` Dev Jain
2025-05-15 9:32 ` David Hildenbrand
2025-05-15 10:07 ` Ryan Roberts
1 sibling, 1 reply; 16+ messages in thread
From: Dev Jain @ 2025-05-15 9:27 UTC (permalink / raw)
To: David Hildenbrand, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/25 2:23 pm, David Hildenbrand wrote:
> On 15.05.25 10:47, Dev Jain wrote:
>>
>>
>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>
>>>>
>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the
>>>>>> caller
>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>> only
>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd
>>>>>> through
>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>> so I assume that assumption was not true in all cases?
>>>>>
>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>
>>>> From what I could gather of Will's commit message, my
>>>> interpretation is
>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>> These individually check for pxd_present():
>>>>
>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>> return 0;
>>>>
>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>> may encounter a none pmd and trigger a WARN.
>>>
>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>
>>> I assume we should either have an explicit pmd_none() check in
>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>> pmd_free_pte_page().
>>>
>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>> which sounds wrong -- unless I am missing something important.
>>
>> Ah thanks, you seem to be right. We will be extracting table from a none
>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>> warning, which the fix commit used to do.
>
> Right. We just make sure that all callers of pmd_free_pte_page() already
> check for it.
>
> I'd just do something like:
>
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index 8fcf59ba39db7..e98dd7af147d5 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long
> addr)
>
> pmd = READ_ONCE(*pmdp);
>
> - if (!pmd_table(pmd)) {
> - VM_WARN_ON(1);
> - return 1;
> - }
> + VM_WARN_ON(!pmd_present(pmd));
> + VM_WARN_ON(!pmd_table(pmd));
And also return 1?
Also we should BUG_ON(!pmd_present(pmd)) to avoid the null dereference?
>
> table = pte_offset_kernel(pmdp, addr);
> pmd_clear(pmdp);
> @@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long
> addr)
> next = addr;
> end = addr + PUD_SIZE;
> do {
> - pmd_free_pte_page(pmdp, next);
> + if (pmd_present(*pmdp))
> + pmd_free_pte_page(pmdp, next);
Ah yes, the "caller" of pmd_free_pte_page() is not only
vmap_try_huge_pmd but this also...my mind has been foggy lately...
need to solve a math problem or two to sharpen it :)
> } while (pmdp++, next += PMD_SIZE, next != end);
>
> pud_clear(pudp);
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 9:27 ` Dev Jain
@ 2025-05-15 9:32 ` David Hildenbrand
2025-05-15 12:56 ` Will Deacon
0 siblings, 1 reply; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 9:32 UTC (permalink / raw)
To: Dev Jain, catalin.marinas, will
Cc: ryan.roberts, anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 11:27, Dev Jain wrote:
>
>
> On 15/05/25 2:23 pm, David Hildenbrand wrote:
>> On 15.05.25 10:47, Dev Jain wrote:
>>>
>>>
>>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>>
>>>>>
>>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the
>>>>>>> caller
>>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>>> only
>>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd
>>>>>>> through
>>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>>> so I assume that assumption was not true in all cases?
>>>>>>
>>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>>
>>>>> From what I could gather of Will's commit message, my
>>>>> interpretation is
>>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>>> These individually check for pxd_present():
>>>>>
>>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>>> return 0;
>>>>>
>>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>>> may encounter a none pmd and trigger a WARN.
>>>>
>>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>>
>>>> I assume we should either have an explicit pmd_none() check in
>>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>>> pmd_free_pte_page().
>>>>
>>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>>> which sounds wrong -- unless I am missing something important.
>>>
>>> Ah thanks, you seem to be right. We will be extracting table from a none
>>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>>> warning, which the fix commit used to do.
>>
>> Right. We just make sure that all callers of pmd_free_pte_page() already
>> check for it.
>>
>> I'd just do something like:
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 8fcf59ba39db7..e98dd7af147d5 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long
>> addr)
>>
>> pmd = READ_ONCE(*pmdp);
>>
>> - if (!pmd_table(pmd)) {
>> - VM_WARN_ON(1);
>> - return 1;
>> - }
>> + VM_WARN_ON(!pmd_present(pmd));
>> + VM_WARN_ON(!pmd_table(pmd));
>
> And also return 1?
I'll leave that to Catalin + Will.
I'm not a friend for adding runtime-overhead for soemthing that should
not happen and be caught early during testing -> VM_WARN_ON_ONCE().
> Also we should BUG_ON(!pmd_present(pmd)) to avoid the null dereference?
Definitely no BUG_ON :)
>
>>
>> table = pte_offset_kernel(pmdp, addr);
>> pmd_clear(pmdp);
>> @@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long
>> addr)
>> next = addr;
>> end = addr + PUD_SIZE;
>> do {
>> - pmd_free_pte_page(pmdp, next);
>> + if (pmd_present(*pmdp))
>> + pmd_free_pte_page(pmdp, next);
>
> Ah yes, the "caller" of pmd_free_pte_page() is not only
> vmap_try_huge_pmd but this also...my mind has been foggy lately...
> need to solve a math problem or two to sharpen it :)
Haha.
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 8:53 ` David Hildenbrand
2025-05-15 9:27 ` Dev Jain
@ 2025-05-15 10:07 ` Ryan Roberts
2025-05-15 13:01 ` David Hildenbrand
1 sibling, 1 reply; 16+ messages in thread
From: Ryan Roberts @ 2025-05-15 10:07 UTC (permalink / raw)
To: David Hildenbrand, Dev Jain, catalin.marinas, will
Cc: anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/2025 09:53, David Hildenbrand wrote:
> On 15.05.25 10:47, Dev Jain wrote:
>>
>>
>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>
>>>>
>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>> only
>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>> so I assume that assumption was not true in all cases?
>>>>>
>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>
>>>> From what I could gather of Will's commit message, my interpretation is
>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>> These individually check for pxd_present():
>>>>
>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>> return 0;
>>>>
>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>> may encounter a none pmd and trigger a WARN.
>>>
>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>
>>> I assume we should either have an explicit pmd_none() check in
>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>> pmd_free_pte_page().
>>>
>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>> which sounds wrong -- unless I am missing something important.
>>
>> Ah thanks, you seem to be right. We will be extracting table from a none
>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>> warning, which the fix commit used to do.
>
> Right. We just make sure that all callers of pmd_free_pte_page() already check
> for it.
>
> I'd just do something like:
I just reviewed the patch and had the same feedback as David. I agree with the
patch below, with some small mods...
>
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index 8fcf59ba39db7..e98dd7af147d5 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
>
> pmd = READ_ONCE(*pmdp);
>
> - if (!pmd_table(pmd)) {
> - VM_WARN_ON(1);
> - return 1;
> - }
> + VM_WARN_ON(!pmd_present(pmd));
> + VM_WARN_ON(!pmd_table(pmd));
You don't need both of these warnings; pmd_table() is only true if the pmd is
present (well actually only if it's _valid_ which is more strict than present),
so the second one is sufficient on its own.
>
> table = pte_offset_kernel(pmdp, addr);
> pmd_clear(pmdp);
> @@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
Given you are removing the runtime check and early return in
pmd_free_pte_page(), I think you should modify this function to use the same
style too.
> next = addr;
> end = addr + PUD_SIZE;
> do {
> - pmd_free_pte_page(pmdp, next);
> + if (pmd_present(*pmdp))
question: I wonder if it is better to use !pmd_none() as the condition here? It
should either be none or a table at this point, so this allows the warning in
pmd_free_pte_page() to catch more error conditions. No strong opinion though.
Thanks,
Ryan
> + pmd_free_pte_page(pmdp, next);
> } while (pmdp++, next += PMD_SIZE, next != end);
>
> pud_clear(pudp);
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 9:32 ` David Hildenbrand
@ 2025-05-15 12:56 ` Will Deacon
2025-05-15 13:04 ` David Hildenbrand
0 siblings, 1 reply; 16+ messages in thread
From: Will Deacon @ 2025-05-15 12:56 UTC (permalink / raw)
To: David Hildenbrand
Cc: Dev Jain, catalin.marinas, ryan.roberts, anshuman.khandual,
mark.rutland, yang, linux-kernel, linux-arm-kernel, stable
On Thu, May 15, 2025 at 11:32:22AM +0200, David Hildenbrand wrote:
> On 15.05.25 11:27, Dev Jain wrote:
> >
> >
> > On 15/05/25 2:23 pm, David Hildenbrand wrote:
> > > On 15.05.25 10:47, Dev Jain wrote:
> > > >
> > > >
> > > > On 15/05/25 2:06 pm, David Hildenbrand wrote:
> > > > > On 15.05.25 10:22, Dev Jain wrote:
> > > > > >
> > > > > >
> > > > > > On 15/05/25 1:43 pm, David Hildenbrand wrote:
> > > > > > > On 15.05.25 08:34, Dev Jain wrote:
> > > > > > > > Commit 9c006972c3fe removes the pxd_present() checks because the
> > > > > > > > caller
> > > > > > > > checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
> > > > > > > > only
> > > > > > > > checks pud_present(); pud_free_pmd_page() recurses on each pmd
> > > > > > > > through
> > > > > > > > pmd_free_pte_page(), wherein the pmd may be none.
> > > > > > > The commit states: "The core code already has a check for pXd_none()",
> > > > > > > so I assume that assumption was not true in all cases?
> > > > > > >
> > > > > > > Should that one problematic caller then check for pmd_none() instead?
> > > > > >
> > > > > > From what I could gather of Will's commit message, my
> > > > > > interpretation is
> > > > > > that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
> > > > > > These individually check for pxd_present():
> > > > > >
> > > > > > if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
> > > > > > return 0;
> > > > > >
> > > > > > The problem is that vmap_try_huge_pud will also iterate on pte entries.
> > > > > > So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
> > > > > > may encounter a none pmd and trigger a WARN.
> > > > >
> > > > > Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
> > > > >
> > > > > I assume we should either have an explicit pmd_none() check in
> > > > > pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
> > > > > pmd_free_pte_page().
> > > > >
> > > > > With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
> > > > > which sounds wrong -- unless I am missing something important.
> > > >
> > > > Ah thanks, you seem to be right. We will be extracting table from a none
> > > > pmd. Perhaps we should still bail out for !pxd_present() but without the
> > > > warning, which the fix commit used to do.
> > >
> > > Right. We just make sure that all callers of pmd_free_pte_page() already
> > > check for it.
> > >
> > > I'd just do something like:
> > >
> > > diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> > > index 8fcf59ba39db7..e98dd7af147d5 100644
> > > --- a/arch/arm64/mm/mmu.c
> > > +++ b/arch/arm64/mm/mmu.c
> > > @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long
> > > addr)
> > >
> > > pmd = READ_ONCE(*pmdp);
> > >
> > > - if (!pmd_table(pmd)) {
> > > - VM_WARN_ON(1);
> > > - return 1;
> > > - }
> > > + VM_WARN_ON(!pmd_present(pmd));
> > > + VM_WARN_ON(!pmd_table(pmd));
> >
> > And also return 1?
>
> I'll leave that to Catalin + Will.
>
> I'm not a friend for adding runtime-overhead for soemthing that should not
> happen and be caught early during testing -> VM_WARN_ON_ONCE().
I definitely think we should return early if the pmd isn't a table.
Otherwise, we could end up descending into God-knows-what!
Will
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 10:07 ` Ryan Roberts
@ 2025-05-15 13:01 ` David Hildenbrand
2025-05-15 13:14 ` Ryan Roberts
0 siblings, 1 reply; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 13:01 UTC (permalink / raw)
To: Ryan Roberts, Dev Jain, catalin.marinas, will
Cc: anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15.05.25 12:07, Ryan Roberts wrote:
> On 15/05/2025 09:53, David Hildenbrand wrote:
>> On 15.05.25 10:47, Dev Jain wrote:
>>>
>>>
>>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>>
>>>>>
>>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>>> only
>>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>>> so I assume that assumption was not true in all cases?
>>>>>>
>>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>>
>>>>> From what I could gather of Will's commit message, my interpretation is
>>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>>> These individually check for pxd_present():
>>>>>
>>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>>> return 0;
>>>>>
>>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>>> may encounter a none pmd and trigger a WARN.
>>>>
>>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>>
>>>> I assume we should either have an explicit pmd_none() check in
>>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>>> pmd_free_pte_page().
>>>>
>>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>>> which sounds wrong -- unless I am missing something important.
>>>
>>> Ah thanks, you seem to be right. We will be extracting table from a none
>>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>>> warning, which the fix commit used to do.
>>
>> Right. We just make sure that all callers of pmd_free_pte_page() already check
>> for it.
>>
>> I'd just do something like:
>
> I just reviewed the patch and had the same feedback as David. I agree with the
> patch below, with some small mods...
>
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 8fcf59ba39db7..e98dd7af147d5 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
>>
>> pmd = READ_ONCE(*pmdp);
>>
>> - if (!pmd_table(pmd)) {
>> - VM_WARN_ON(1);
>> - return 1;
>> - }
>> + VM_WARN_ON(!pmd_present(pmd));
>> + VM_WARN_ON(!pmd_table(pmd));
>
> You don't need both of these warnings; pmd_table() is only true if the pmd is
> present (well actually only if it's _valid_ which is more strict than present),
> so the second one is sufficient on its own.
Ah, right.
>
>>
>> table = pte_offset_kernel(pmdp, addr);
>> pmd_clear(pmdp);
>> @@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
>
> Given you are removing the runtime check and early return in
> pmd_free_pte_page(), I think you should modify this function to use the same
> style too.
BTW, the "return 1" is weird. But looking at x86, we seem to be making a
private copy of the page table first, to defer freeing the page tables
after the TLB flush.
I wonder if there isn't a better way (e.g., clear PUDP + flush tlb, then
walk over the effectively-disconnected page table). But I'm sure there
is a good reason for that.
>
>> next = addr;
>> end = addr + PUD_SIZE;
>> do {
>> - pmd_free_pte_page(pmdp, next);
>> + if (pmd_present(*pmdp))
>
> question: I wonder if it is better to use !pmd_none() as the condition here? It
> should either be none or a table at this point, so this allows the warning in
> pmd_free_pte_page() to catch more error conditions. No strong opinion though.
Same here. The existing callers check pmd_present().
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 12:56 ` Will Deacon
@ 2025-05-15 13:04 ` David Hildenbrand
2025-05-15 13:21 ` Will Deacon
0 siblings, 1 reply; 16+ messages in thread
From: David Hildenbrand @ 2025-05-15 13:04 UTC (permalink / raw)
To: Will Deacon
Cc: Dev Jain, catalin.marinas, ryan.roberts, anshuman.khandual,
mark.rutland, yang, linux-kernel, linux-arm-kernel, stable
On 15.05.25 14:56, Will Deacon wrote:
> On Thu, May 15, 2025 at 11:32:22AM +0200, David Hildenbrand wrote:
>> On 15.05.25 11:27, Dev Jain wrote:
>>>
>>>
>>> On 15/05/25 2:23 pm, David Hildenbrand wrote:
>>>> On 15.05.25 10:47, Dev Jain wrote:
>>>>>
>>>>>
>>>>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>>>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the
>>>>>>>>> caller
>>>>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>>>>> only
>>>>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd
>>>>>>>>> through
>>>>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>>>>> so I assume that assumption was not true in all cases?
>>>>>>>>
>>>>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>>>>
>>>>>>> From what I could gather of Will's commit message, my
>>>>>>> interpretation is
>>>>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>>>>> These individually check for pxd_present():
>>>>>>>
>>>>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>>>>> return 0;
>>>>>>>
>>>>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>>>>> may encounter a none pmd and trigger a WARN.
>>>>>>
>>>>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>>>>
>>>>>> I assume we should either have an explicit pmd_none() check in
>>>>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>>>>> pmd_free_pte_page().
>>>>>>
>>>>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>>>>> which sounds wrong -- unless I am missing something important.
>>>>>
>>>>> Ah thanks, you seem to be right. We will be extracting table from a none
>>>>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>>>>> warning, which the fix commit used to do.
>>>>
>>>> Right. We just make sure that all callers of pmd_free_pte_page() already
>>>> check for it.
>>>>
>>>> I'd just do something like:
>>>>
>>>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>>>> index 8fcf59ba39db7..e98dd7af147d5 100644
>>>> --- a/arch/arm64/mm/mmu.c
>>>> +++ b/arch/arm64/mm/mmu.c
>>>> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long
>>>> addr)
>>>>
>>>> pmd = READ_ONCE(*pmdp);
>>>>
>>>> - if (!pmd_table(pmd)) {
>>>> - VM_WARN_ON(1);
>>>> - return 1;
>>>> - }
>>>> + VM_WARN_ON(!pmd_present(pmd));
>>>> + VM_WARN_ON(!pmd_table(pmd));
>>>
>>> And also return 1?
>>
>> I'll leave that to Catalin + Will.
>>
>> I'm not a friend for adding runtime-overhead for soemthing that should not
>> happen and be caught early during testing -> VM_WARN_ON_ONCE().
>
> I definitely think we should return early if the pmd isn't a table.
> Otherwise, we could end up descending into God-knows-what!
The question is: how did something that is not a table end up here, and
why is it valid to check exactly that at runtime. Not strong opinion, it
just feels a bit arbitrary to test for exactly that at runtime if it is
completely unexpected.
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 13:01 ` David Hildenbrand
@ 2025-05-15 13:14 ` Ryan Roberts
0 siblings, 0 replies; 16+ messages in thread
From: Ryan Roberts @ 2025-05-15 13:14 UTC (permalink / raw)
To: David Hildenbrand, Dev Jain, catalin.marinas, will
Cc: anshuman.khandual, mark.rutland, yang, linux-kernel,
linux-arm-kernel, stable
On 15/05/2025 14:01, David Hildenbrand wrote:
> On 15.05.25 12:07, Ryan Roberts wrote:
>> On 15/05/2025 09:53, David Hildenbrand wrote:
>>> On 15.05.25 10:47, Dev Jain wrote:
>>>>
>>>>
>>>> On 15/05/25 2:06 pm, David Hildenbrand wrote:
>>>>> On 15.05.25 10:22, Dev Jain wrote:
>>>>>>
>>>>>>
>>>>>> On 15/05/25 1:43 pm, David Hildenbrand wrote:
>>>>>>> On 15.05.25 08:34, Dev Jain wrote:
>>>>>>>> Commit 9c006972c3fe removes the pxd_present() checks because the caller
>>>>>>>> checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
>>>>>>>> only
>>>>>>>> checks pud_present(); pud_free_pmd_page() recurses on each pmd through
>>>>>>>> pmd_free_pte_page(), wherein the pmd may be none.
>>>>>>> The commit states: "The core code already has a check for pXd_none()",
>>>>>>> so I assume that assumption was not true in all cases?
>>>>>>>
>>>>>>> Should that one problematic caller then check for pmd_none() instead?
>>>>>>
>>>>>> From what I could gather of Will's commit message, my interpretation is
>>>>>> that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
>>>>>> These individually check for pxd_present():
>>>>>>
>>>>>> if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
>>>>>> return 0;
>>>>>>
>>>>>> The problem is that vmap_try_huge_pud will also iterate on pte entries.
>>>>>> So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
>>>>>> may encounter a none pmd and trigger a WARN.
>>>>>
>>>>> Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
>>>>>
>>>>> I assume we should either have an explicit pmd_none() check in
>>>>> pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
>>>>> pmd_free_pte_page().
>>>>>
>>>>> With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
>>>>> which sounds wrong -- unless I am missing something important.
>>>>
>>>> Ah thanks, you seem to be right. We will be extracting table from a none
>>>> pmd. Perhaps we should still bail out for !pxd_present() but without the
>>>> warning, which the fix commit used to do.
>>>
>>> Right. We just make sure that all callers of pmd_free_pte_page() already check
>>> for it.
>>>
>>> I'd just do something like:
>>
>> I just reviewed the patch and had the same feedback as David. I agree with the
>> patch below, with some small mods...
>>
>>>
>>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>>> index 8fcf59ba39db7..e98dd7af147d5 100644
>>> --- a/arch/arm64/mm/mmu.c
>>> +++ b/arch/arm64/mm/mmu.c
>>> @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
>>> pmd = READ_ONCE(*pmdp);
>>> - if (!pmd_table(pmd)) {
>>> - VM_WARN_ON(1);
>>> - return 1;
>>> - }
>>> + VM_WARN_ON(!pmd_present(pmd));
>>> + VM_WARN_ON(!pmd_table(pmd));
>>
>> You don't need both of these warnings; pmd_table() is only true if the pmd is
>> present (well actually only if it's _valid_ which is more strict than present),
>> so the second one is sufficient on its own.
>
> Ah, right.
>
>>
>>> table = pte_offset_kernel(pmdp, addr);
>>> pmd_clear(pmdp);
>>> @@ -1305,7 +1303,8 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
>>
>> Given you are removing the runtime check and early return in
>> pmd_free_pte_page(), I think you should modify this function to use the same
>> style too.
>
> BTW, the "return 1" is weird. But looking at x86, we seem to be making a private
> copy of the page table first, to defer freeing the page tables after the TLB flush.
>
> I wonder if there isn't a better way (e.g., clear PUDP + flush tlb, then walk
> over the effectively-disconnected page table). But I'm sure there is a good
> reason for that.
As I understand it, the actual TLB entries should have been invalidated when the
previous mappings we vfree'd. So the single page __flush_tlb_kernel_pgtable()
calls here are to zap any table entries that may be in the walk cache. We could
do an all-levels TLBI for the entire range, but for a system that doesn't
support the tlbi-range operations, we would end up issuing a tlbi per page
across the whole range which I think would be much slower than the one tlbi per
pgtable we have here.
Things could be rearranged a bit so that we issue all the tlbis with only a
single set of barriers (currently each __flush_tlb_kernel_pgtable() issues it's
own barriers), but I'm not sure how important that micro-optimization is given I
guess we never even call pud_free_pmd_page() in practice given we have had no
reports of the warning tripping.
>
>>
>>> next = addr;
>>> end = addr + PUD_SIZE;
>>> do {
>>> - pmd_free_pte_page(pmdp, next);
>>> + if (pmd_present(*pmdp))
>>
>> question: I wonder if it is better to use !pmd_none() as the condition here? It
>> should either be none or a table at this point, so this allows the warning in
>> pmd_free_pte_page() to catch more error conditions. No strong opinion though.
>
> Same here. The existing callers check pmd_present().
Yeah fair let's be consistent and use pmd_present().
Thanks,
Ryan
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables
2025-05-15 13:04 ` David Hildenbrand
@ 2025-05-15 13:21 ` Will Deacon
0 siblings, 0 replies; 16+ messages in thread
From: Will Deacon @ 2025-05-15 13:21 UTC (permalink / raw)
To: David Hildenbrand
Cc: Dev Jain, catalin.marinas, ryan.roberts, anshuman.khandual,
mark.rutland, yang, linux-kernel, linux-arm-kernel, stable
On Thu, May 15, 2025 at 03:04:50PM +0200, David Hildenbrand wrote:
> On 15.05.25 14:56, Will Deacon wrote:
> > On Thu, May 15, 2025 at 11:32:22AM +0200, David Hildenbrand wrote:
> > > On 15.05.25 11:27, Dev Jain wrote:
> > > >
> > > >
> > > > On 15/05/25 2:23 pm, David Hildenbrand wrote:
> > > > > On 15.05.25 10:47, Dev Jain wrote:
> > > > > >
> > > > > >
> > > > > > On 15/05/25 2:06 pm, David Hildenbrand wrote:
> > > > > > > On 15.05.25 10:22, Dev Jain wrote:
> > > > > > > >
> > > > > > > >
> > > > > > > > On 15/05/25 1:43 pm, David Hildenbrand wrote:
> > > > > > > > > On 15.05.25 08:34, Dev Jain wrote:
> > > > > > > > > > Commit 9c006972c3fe removes the pxd_present() checks because the
> > > > > > > > > > caller
> > > > > > > > > > checks pxd_present(). But, in case of vmap_try_huge_pud(), the caller
> > > > > > > > > > only
> > > > > > > > > > checks pud_present(); pud_free_pmd_page() recurses on each pmd
> > > > > > > > > > through
> > > > > > > > > > pmd_free_pte_page(), wherein the pmd may be none.
> > > > > > > > > The commit states: "The core code already has a check for pXd_none()",
> > > > > > > > > so I assume that assumption was not true in all cases?
> > > > > > > > >
> > > > > > > > > Should that one problematic caller then check for pmd_none() instead?
> > > > > > > >
> > > > > > > > From what I could gather of Will's commit message, my
> > > > > > > > interpretation is
> > > > > > > > that the concerned callers are vmap_try_huge_pud and vmap_try_huge_pmd.
> > > > > > > > These individually check for pxd_present():
> > > > > > > >
> > > > > > > > if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
> > > > > > > > return 0;
> > > > > > > >
> > > > > > > > The problem is that vmap_try_huge_pud will also iterate on pte entries.
> > > > > > > > So if the pud is present, then pud_free_pmd_page -> pmd_free_pte_page
> > > > > > > > may encounter a none pmd and trigger a WARN.
> > > > > > >
> > > > > > > Yeah, pud_free_pmd_page()->pmd_free_pte_page() looks shaky.
> > > > > > >
> > > > > > > I assume we should either have an explicit pmd_none() check in
> > > > > > > pud_free_pmd_page() before calling pmd_free_pte_page(), or one in
> > > > > > > pmd_free_pte_page().
> > > > > > >
> > > > > > > With your patch, we'd be calling pte_free_kernel() on a NULL pointer,
> > > > > > > which sounds wrong -- unless I am missing something important.
> > > > > >
> > > > > > Ah thanks, you seem to be right. We will be extracting table from a none
> > > > > > pmd. Perhaps we should still bail out for !pxd_present() but without the
> > > > > > warning, which the fix commit used to do.
> > > > >
> > > > > Right. We just make sure that all callers of pmd_free_pte_page() already
> > > > > check for it.
> > > > >
> > > > > I'd just do something like:
> > > > >
> > > > > diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> > > > > index 8fcf59ba39db7..e98dd7af147d5 100644
> > > > > --- a/arch/arm64/mm/mmu.c
> > > > > +++ b/arch/arm64/mm/mmu.c
> > > > > @@ -1274,10 +1274,8 @@ int pmd_free_pte_page(pmd_t *pmdp, unsigned long
> > > > > addr)
> > > > >
> > > > > pmd = READ_ONCE(*pmdp);
> > > > >
> > > > > - if (!pmd_table(pmd)) {
> > > > > - VM_WARN_ON(1);
> > > > > - return 1;
> > > > > - }
> > > > > + VM_WARN_ON(!pmd_present(pmd));
> > > > > + VM_WARN_ON(!pmd_table(pmd));
> > > >
> > > > And also return 1?
> > >
> > > I'll leave that to Catalin + Will.
> > >
> > > I'm not a friend for adding runtime-overhead for soemthing that should not
> > > happen and be caught early during testing -> VM_WARN_ON_ONCE().
> >
> > I definitely think we should return early if the pmd isn't a table.
> > Otherwise, we could end up descending into God-knows-what!
>
> The question is: how did something that is not a table end up here, and why
> is it valid to check exactly that at runtime. Not strong opinion, it just
> feels a bit arbitrary to test for exactly that at runtime if it is
> completely unexpected.
I see it a little bit like type-checking: we could see an invalid entry,
a leaf entry or a table entry and we should only ever dereference the
latter. If the VM_WARN_ON() is justified, then I find it jarring to go
ahead with the dereference regardless of the type.
That said, maybe the VM_WARN_ON() should either be deleted or moved out
to the callers in mm/vmalloc.c?
Will
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2025-05-15 13:30 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-15 6:34 [PATCH] arm64: Check pxd_leaf() instead of !pxd_table() while tearing down page tables Dev Jain
2025-05-15 8:13 ` David Hildenbrand
2025-05-15 8:22 ` Dev Jain
2025-05-15 8:36 ` David Hildenbrand
2025-05-15 8:40 ` Dev Jain
2025-05-15 8:49 ` David Hildenbrand
2025-05-15 8:47 ` Dev Jain
2025-05-15 8:53 ` David Hildenbrand
2025-05-15 9:27 ` Dev Jain
2025-05-15 9:32 ` David Hildenbrand
2025-05-15 12:56 ` Will Deacon
2025-05-15 13:04 ` David Hildenbrand
2025-05-15 13:21 ` Will Deacon
2025-05-15 10:07 ` Ryan Roberts
2025-05-15 13:01 ` David Hildenbrand
2025-05-15 13:14 ` Ryan Roberts
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).