[PATCH 3/4] mm/huge_memory: don't warn when a page table blocks a huge pfn insert

From: Eliav Farber

Date: Wed Nov 11 2026 - 09:07:35 EST


insert_pmd() and insert_pud() treat a non-empty entry at their own level
as a leaf and compare its PFN against the one being inserted. A page
table installed by a concurrent order-0 fault also makes the entry
non-empty, and pmd_pfn()/pud_pfn() then report the PFN of that table,
which never matches. insert_pmd() warns unless the entry is the huge
zero PMD, and insert_pud() warns on any mismatch.

A VM_PFNMAP mapping with both ->fault and ->huge_fault can produce the
mixed orders: an order-0 fault installs a page table over an address
while a concurrent huge fault on the same address reaches insert_pmd().
Changing an mm's THP policy while another thread faults the same PMD,
for example with prctl(PR_SET_THP_DISABLE), is enough to do it.

Compare the PFN only for a leaf entry. A page table is not an error, so
take the existing out_unlock path, which returns VM_FAULT_NOPAGE and
lets the access refault onto the smaller entry.

Fixes: f9e54c3a2f5b ("vfio/pci: implement huge_fault support")
Signed-off-by: Eliav Farber <farbere@xxxxxxxxxx>
---
mm/huge_memory.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index eb913acb03b7..052c50dadc48 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1643,6 +1643,14 @@ static vm_fault_t insert_pmd(struct vm_area_struct *vma, unsigned long addr,
const unsigned long pfn = fop.is_folio ? folio_pfn(fop.folio) :
fop.pfn;

+ /*
+ * A concurrent order-0 fault may have installed a PTE table
+ * here, which pmd_pfn() would report as the PFN of the table
+ * itself. Only a leaf entry can be compared.
+ */
+ if (!pmd_leaf(*pmd))
+ goto out_unlock;
+
if (write) {
if (pmd_pfn(*pmd) != pfn) {
WARN_ON_ONCE(!is_huge_zero_pmd(*pmd));
@@ -1770,6 +1778,14 @@ static vm_fault_t insert_pud(struct vm_area_struct *vma, unsigned long addr,
const unsigned long pfn = fop.is_folio ? folio_pfn(fop.folio) :
fop.pfn;

+ /*
+ * A concurrent order-0 fault may have installed a PMD table
+ * here, which pud_pfn() would report as the PFN of the table
+ * itself. Only a leaf entry can be compared.
+ */
+ if (!pud_leaf(*pud))
+ goto out_unlock;
+
if (write) {
if (WARN_ON_ONCE(pud_pfn(*pud) != pfn))
goto out_unlock;
--
2.47.3