From 2858b4027f491e6fdb8ee2d8923798b619bf2791 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Fri, 10 Jul 2026 03:55:21 -0700 Subject: [PATCH] mm/mempolicy: skip non-present PMDs when queueing folios Patch series "mm: handle device-private PMDs in walk callbacks", v3. Since commit 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations") a PMD may hold a device-private swap entry whenever an HMM-based GPU driver migrates an anonymous THP folio to device memory via migrate_vma_pages(). pmd_trans_huge_lock() succeeds for such PMDs (pmd_is_huge() returns true for any non-present, non-none huge PMD), so several MM walk callbacks that used to assume present THP or migration entry are now reachable with a device-private PMD. The results range from a VM_BUG_ON() firing on debug kernels, to an oops on a bogus vmemmap dereference, to silently isolating an unrelated live folio from LRU in the aliasing case. This patch (of 3): queue_folios_pmd() is called under pmd_trans_huge_lock(), whose pmd_is_huge() check returns true for any non-present, non-none PMD softleaf. Passing such a PMD to pmd_folio() treats the softleaf encoding as a hardware PFN and can return a bogus folio pointer. Mirror queue_folios_pte_range(): handle non-present entries before looking up a folio. Keep migration entries counted as failures, but skip other non-present PMDs such as device-private entries. Potential trigger: an HMM-based GPU driver migrates an anonymous THP folio to device memory via migrate_vma_pages(), leaving a device-private PMD. Userspace then calls mbind(), migrate_pages() or set_mempolicy_home_node() on that range. Link: https://lore.kernel.org/20260710105557.1987433-1-usama.arif@linux.dev Link: https://lore.kernel.org/20260710105557.1987433-2-usama.arif@linux.dev Fixes: 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations") Signed-off-by: Usama Arif Reported-by: sashiko-bot Link: https://sashiko.dev/#/patchset/20260703173903.3789516-1-usama.arif%40linux.dev?part=6 Reviewed-by: Joshua Hahn Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Reviewed-by: Balbir Singh Reviewed-by: Gregory Price Cc: Alistair Popple Cc: Baolin Wang Cc: Barry Song Cc: Byungchul Park Cc: Dev Jain Cc: "Huang, Ying" Cc: Jann Horn Cc: Johannes Weiner Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Nico Pache Cc: Rakie Kim Cc: Ryan Roberts Cc: Shakeel Butt Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/mempolicy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mm/mempolicy.c b/mm/mempolicy.c index a92bd29cf032..501e0b80d7da 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -654,12 +654,14 @@ static void queue_folios_pmd(pmd_t *pmd, struct mm_walk *walk) { struct folio *folio; struct queue_pages *qp = walk->private; + pmd_t pmdval = pmdp_get(pmd); - if (unlikely(pmd_is_migration_entry(*pmd))) { - qp->nr_failed++; + if (unlikely(!pmd_present(pmdval))) { + if (pmd_is_migration_entry(pmdval)) + qp->nr_failed++; return; } - folio = pmd_folio(*pmd); + folio = pmd_folio(pmdval); if (is_huge_zero_folio(folio)) { walk->action = ACTION_CONTINUE; return;