[PATCH for 7.4-rc2+ 1/7] powerpc/book3s64/mm: don't count invalidated PMDs in page table check

From: Zi Yan

Date: Thu Oct 15 2026 - 14:15:18 EST


pmdp_invalidate() replaces a huge PMD with an invalidated one
(_PAGE_PRESENT cleared and _PAGE_INVALID set) and calls
page_table_check_pmd_clear() on the old entry. This decreases the page
table check map count. When the invalidated entry is later replaced with
set_pmd_at(), as MADV_COLD, page_mkclean_one() and change_huge_pmd() do,
page_table_check_pmds_set() in set_pmd_at() clears it a second time and
decreases the map count because pmd_user_accessible_page() still counts it
as a live user mapping. The same happens when it is cleared with
pmdp_huge_get_and_clear(). Then the map count underflows and hits BUG_ON()
in page_table_check_clear().

Found by code inspection and confirmed in QEMU (pseries, POWER9, 64K
pages, CONFIG_PAGE_TABLE_CHECK_ENFORCED=y): MADV_COLD or mprotect() on
an anonymous THP hits the BUG_ON() with both the radix and the hash MMU.

Fix it by only counting entries that are hardware valid, so an
invalidated entry is skipped, as the patch "riscv: mm: exclude invalid
THP PMDs from page table check" did for riscv.

Fixes: 641d47d4c963 ("powerpc/mm: support page table check")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Zi Yan <ziy@xxxxxxxxxx>
To: Madhavan Srinivasan <maddy@xxxxxxxxxxxxx>
To: Andrew Donnellan <andrew+kernel@xxxxxxxxxxxxxxx>
To: Rohan McLure <rmclure@xxxxxxxxxxxxx>
To: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
To: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Michael Ellerman <mpe@xxxxxxxxxxxxxx>
Cc: Nicholas Piggin <npiggin@xxxxxxxxx>
Cc: "Christophe Leroy (CS GROUP)" <chleroy@xxxxxxxxxx>
Cc: "Ritesh Harjani (IBM)" <ritesh.list@xxxxxxxxx>
Cc: Shrikanth Hegde <sshegde@xxxxxxxxxxxxx>
Cc: linuxppc-dev@xxxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
---
arch/powerpc/include/asm/book3s/64/pgtable.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h
index dff8790a047db..392474972777c 100644
--- a/arch/powerpc/include/asm/book3s/64/pgtable.h
+++ b/arch/powerpc/include/asm/book3s/64/pgtable.h
@@ -1091,7 +1091,8 @@ static inline bool pmd_access_permitted(pmd_t pmd, bool write)
#define pmd_user_accessible_page pmd_user_accessible_page
static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd)
{
- return pmd_leaf(pmd) && pte_user_accessible_page(mm, addr, pmd_pte(pmd));
+ return pmd_leaf(pmd) && pte_hw_valid(pmd_pte(pmd)) &&
+ pte_user_accessible_page(mm, addr, pmd_pte(pmd));
}

#ifdef CONFIG_TRANSPARENT_HUGEPAGE

--
2.53.0