[PATCH 4/4] mm/mempolicy: skip zone device folios when queueing folios

From: Gregory Price

Date: Tue Jul 28 2026 - 15:56:29 EST


queue_folios_pte_range() already pairs vm_normal_folio() with an
explicit folio_is_zone_device() check before adding folios to the
migration pagelist.

vm_normal_folio() alone does not reject zone device memory (a present
device-coherent page in a normal VMA is returned as "normal").

Mirror the explicit check in the two other walkers.

queue_folios_pmd() uses pmd_folio() directly and can encounter a present
zone device PMD - e.g. a device-coherent THP.

This is not filtered by existing checks:
!pmd_present() - only rejects non-present device-private and
migration entries

vma_migratable() - excludes DAX and VM_PFNMAP.

The early return also means such a folio is no longer counted in
qp->nr_failed under MPOL_MF_STRICT. This is the same pattern used
by queue_folios_pte_range() (skipping zone device without failing).

queue_folios_hugetlb() cannot see zone device memory - hugetlb folios
are never ZONE_DEVICE - so the check there is purely defensive and keeps
all three walkers consistent.

Signed-off-by: Gregory Price (Meta) <gourry@xxxxxxxxxx>
---
mm/mempolicy.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 5720f7f54d94..bd79e61a40d1 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -668,6 +668,8 @@ static void queue_folios_pmd(pmd_t *pmd, struct mm_walk *walk)
}
if (!queue_folio_required(folio, qp))
return;
+ if (folio_is_zone_device(folio))
+ return;
if (!(qp->flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ||
!vma_migratable(walk->vma) ||
!migrate_folio_add(folio, qp->pagelist, qp->flags))
@@ -797,6 +799,8 @@ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask,
folio = pfn_folio(pte_pfn(ptep));
if (!queue_folio_required(folio, qp))
goto unlock;
+ if (folio_is_zone_device(folio))
+ goto unlock;
if (!(flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) ||
!vma_migratable(walk->vma)) {
qp->nr_failed++;
--
2.55.0