Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

powerpc/64s/radix: Fix huge vmap false positive

pmd_huge() is defined to false when HUGETLB_PAGE is not configured, but
the vmap code still installs huge PMDs. This leads to false bad PMD
errors when vunmapping because it is not seen as a huge PTE, and the bad
PMD check catches it. The end result may not be much more serious than
some bad pmd warning messages, because the pmd_none_or_clear_bad() does
what we wanted and clears the huge PTE anyway.

Fix this by checking pmd_is_leaf(), which checks for a PTE regardless of
config options. The whole huge/large/leaf stuff is a tangled mess but
that's kernel-wide and not something we can improve much in arch/powerpc
code.

pmd_page(), pud_page(), etc., called by vmalloc_to_page() on huge vmaps
can similarly trigger a false VM_BUG_ON when CONFIG_HUGETLB_PAGE=n, so
those checks are adjusted. The checks were added by commit d6eacedd1f0e
("powerpc/book3s: Use config independent helpers for page table walk"),
while implementing a similar fix for other page table walking functions.

Fixes: d909f9109c30 ("powerpc/64s/radix: Enable HAVE_ARCH_HUGE_VMAP")
Cc: stable@vger.kernel.org # v5.3+
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20211216103342.609192-1-npiggin@gmail.com

authored by

Nicholas Piggin and committed by
Michael Ellerman
467ba14e a605b39e

+13 -5
+2 -2
arch/powerpc/mm/book3s64/radix_pgtable.c
··· 1076 1076 1077 1077 int pud_clear_huge(pud_t *pud) 1078 1078 { 1079 - if (pud_huge(*pud)) { 1079 + if (pud_is_leaf(*pud)) { 1080 1080 pud_clear(pud); 1081 1081 return 1; 1082 1082 } ··· 1123 1123 1124 1124 int pmd_clear_huge(pmd_t *pmd) 1125 1125 { 1126 - if (pmd_huge(*pmd)) { 1126 + if (pmd_is_leaf(*pmd)) { 1127 1127 pmd_clear(pmd); 1128 1128 return 1; 1129 1129 }
+11 -3
arch/powerpc/mm/pgtable_64.c
··· 102 102 struct page *p4d_page(p4d_t p4d) 103 103 { 104 104 if (p4d_is_leaf(p4d)) { 105 - VM_WARN_ON(!p4d_huge(p4d)); 105 + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) 106 + VM_WARN_ON(!p4d_huge(p4d)); 106 107 return pte_page(p4d_pte(p4d)); 107 108 } 108 109 return virt_to_page(p4d_pgtable(p4d)); ··· 113 112 struct page *pud_page(pud_t pud) 114 113 { 115 114 if (pud_is_leaf(pud)) { 116 - VM_WARN_ON(!pud_huge(pud)); 115 + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) 116 + VM_WARN_ON(!pud_huge(pud)); 117 117 return pte_page(pud_pte(pud)); 118 118 } 119 119 return virt_to_page(pud_pgtable(pud)); ··· 127 125 struct page *pmd_page(pmd_t pmd) 128 126 { 129 127 if (pmd_is_leaf(pmd)) { 130 - VM_WARN_ON(!(pmd_large(pmd) || pmd_huge(pmd))); 128 + /* 129 + * vmalloc_to_page may be called on any vmap address (not only 130 + * vmalloc), and it uses pmd_page() etc., when huge vmap is 131 + * enabled so these checks can't be used. 132 + */ 133 + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) 134 + VM_WARN_ON(!(pmd_large(pmd) || pmd_huge(pmd))); 131 135 return pte_page(pmd_pte(pmd)); 132 136 } 133 137 return virt_to_page(pmd_page_vaddr(pmd));