Re: [PATCH 42/46] mm/huge_memory: split huge PUDs when adjusting VMA boundaries
From: Kiryl Shutsemau
Date: Thu Oct 29 2026 - 10:35:41 EST
On Thu, Oct 29, 2026 at 03:24:29PM +0100, David Hildenbrand (Arm) wrote:
> On 10/29/26 12:45, Kiryl Shutsemau wrote:
> > On Tue, Oct 27, 2026 at 12:18:02AM +0100, David Hildenbrand (Arm) wrote:
> >> PFN mappings such as VFIO PCI BAR mappings can use PUD leaves while
> >> allowing VMA splits at page granularity. However, vma_adjust_trans_huge()
> >> only handles PMDs.
> >>
> >> After faulting a writable PUD-sized VFIO BAR mapping, changing its first
> >> page to read-only with mprotect() makes the PMD walk treat the PUD leaf
> >> as a PMD table. Applying MADV_DONTFORK to the first PMD-sized chunk leaves
> >> the PUD spanning two VMAs: the PMD-aligned boundary skips splitting.
> >>
> >> Walk the page tables once at each adjusted boundary and split a huge
> >> PUD or PMD leaf as needed. Handle PUD leaves even when the boundary is
> >> PMD-aligned.
> >>
> >> Fixes: f9e54c3a2f5b7 ("vfio/pci: implement huge_fault support")
> >> Cc: stable@xxxxxxxxxxxxxxx
> >> Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
> >> ---
> >> mm/huge_memory.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++--------
> >> 1 file changed, 60 insertions(+), 10 deletions(-)
> >>
> >> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> >> index 08e58cda7d37d..d090642ac8c2b 100644
> >> --- a/mm/huge_memory.c
> >> +++ b/mm/huge_memory.c
> >> @@ -3503,16 +3503,66 @@ void split_huge_pmd_address(struct vm_area_struct *vma, unsigned long address,
> >> __split_huge_pmd(vma, pmd, address, freeze);
> >> }
> >>
> >> -static inline void split_huge_pmd_if_needed(struct vm_area_struct *vma, unsigned long address)
> >> +static bool pud_split_required(const struct vm_area_struct *vma,
> >> + unsigned long address)
> >> {
> >> + if (!IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
> >> + return false;
> >> + if (IS_ALIGNED(address, HPAGE_PUD_SIZE))
> >> + return false;
> >> /*
> >> - * If the new address isn't hpage aligned and it could previously
> >> - * contain an hugepage: check if we need to split an huge pmd.
> >> + * Only selected VMAs support mapping PUDs for now. Lift the
> >> + * restriction once this changes.
> >> */
> >> - if (!IS_ALIGNED(address, HPAGE_PMD_SIZE) &&
> >> - range_in_vma(vma, ALIGN_DOWN(address, HPAGE_PMD_SIZE),
> >> - ALIGN(address, HPAGE_PMD_SIZE)))
> >> - split_huge_pmd_address(vma, address, false);
> >> + if (!vma_is_dax(vma) && !vma_test_any(vma, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT))
> >> + return false;
> >> + return range_in_vma(vma, ALIGN_DOWN(address, HPAGE_PUD_SIZE),
> >> + ALIGN(address, HPAGE_PUD_SIZE));
> >> +}
> >> +
> >> +static bool pmd_split_required(const struct vm_area_struct *vma,
> >> + unsigned long address)
> >> +{
> >> + if (IS_ALIGNED(address, HPAGE_PMD_SIZE))
> >> + return false;
> >> +
> >> + return range_in_vma(vma, ALIGN_DOWN(address, HPAGE_PMD_SIZE),
> >> + ALIGN(address, HPAGE_PMD_SIZE));
> >> +}
> >> +
> >> +static void split_huge_leaf(struct vm_area_struct *vma,
> >> + unsigned long address)
> >> +{
> >> + const bool split_pud = pud_split_required(vma, address);
> >> + const bool split_pmd = pmd_split_required(vma, address);
> >> + pgd_t *pgd;
> >> + p4d_t *p4d;
> >> + pud_t *pud;
> >> + pmd_t *pmd;
> >> +
> >> + /*
> >> + * mmap, VMA and rmap write locks protect the walk; the split helpers
> >> + * take the page table locks.
> >> + */
> >> + if (!split_pud && !split_pmd)
> >> + return;
> >> +
> >> + pgd = pgd_offset(vma->vm_mm, address);
> >> + if (!pgd_present(*pgd))
> >> + return;
> >> +
> >> + p4d = p4d_offset(pgd, address);
> >> + if (!p4d_present(*p4d))
> >> + return;
> >> +
> >> + pud = pud_offset(p4d, address);
> >> + if (split_pud && pud_leaf(*pud))
> >> + __split_huge_pud(vma, pud, address);
> >> + if (!split_pmd || !pud_present(*pud))
> >> + return;
> >
> > This looks wrong.
> >
> > We should bail out here if PUD is was a leaf and wasn't split.
>
> __split_huge_pud() must split a leave. Either by zapping it (what we do right
> now) or by populating page tables (as in Usama's PoC).
No, meant the situation when pud_leaf() is true, but split_pud is false,
so __split_huge_pud() is never hit. pud_present() is true so we proceed
to pmd_offset()
--
Kiryl Shutsemau / Kirill A. Shutemov