When phys_pud_init() or phys_pmd_init() splits an existing huge page
(pud_leaf(*pud) or pmd_leaf(*pmd)) into lower-level table entries, it
extracts the page protection attributes but does not clear the existing
PUD/PMD entry (*pud or *pmd).
Subsequently, set_pud_safe() / set_pmd_safe() is invoked to link the new
PMD/PTE table. Because *pud / *pmd is still populated with the huge page
entry, set_pud_safe() / set_pmd_safe() detects a present entry that differs
from the new table pointer, triggering a WARN_ON_ONCE call trace:
WARNING: CPU: 0 PID: 0 at arch/x86/mm/init_64.c:89 phys_pud_init+0x2d6/0x390
Call pud_clear(pud) and pmd_clear(pmd) right after extracting protection
attributes when splitting 1 GB / 2 MB huge pages so that set_pud_safe() and
set_pmd_safe() can populate lower-level tables cleanly without issuing a
warning.
Signed-off-by: Phineas Su <pohaosu@google.com>
---
v2:
- Also add pmd_clear(pmd) in phys_pmd_init() to handle 2 MB huge page splitting.
- Update commit message to cover both PUD and PMD splitting.
arch/x86/mm/init_64.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index d57f29ca23a5..55845c40c1ca 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -573,6 +573,7 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long paddr, unsigned long paddr_end,
continue;
}
new_prot = pte_pgprot(pte_clrhuge(*(pte_t *)pmd));
+ pmd_clear(pmd);
}
if (page_size_mask & (1<<PG_LEVEL_2M)) {
@@ -659,6 +660,7 @@ phys_pud_init(pud_t *pud_page, unsigned long paddr, unsigned long paddr_end,
continue;
}
prot = pte_pgprot(pte_clrhuge(*(pte_t *)pud));
+ pud_clear(pud);
}
if (page_size_mask & (1<<PG_LEVEL_1G)) {
--
2.55.0.229.g6434b31f56-goog