From: sashiko-bot@kernel.org
To: mpenttil@redhat.com, linux-mm@kvack.org
Cc: dri-devel@lists.freedesktop.org, intel-xe@lists.freedesktop.org,
"David Hildenbrand" <david@kernel.org>,
"Jason Gunthorpe" <jgg@nvidia.com>,
"Leon Romanovsky" <leonro@nvidia.com>,
"Alistair Popple" <apopple@nvidia.com>,
"Balbir Singh" <balbirs@nvidia.com>, "Zi Yan" <ziy@nvidia.com>,
"Matthew Brost" <matthew.brost@intel.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Lorenzo Stoakes" <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
"Vlastimil Babka" <vbabka@suse.cz>,
"Mike Rapoport" <rppt@kernel.org>,
"Suren Baghdasaryan" <surenb@google.com>,
"Michal Hocko" <mhocko@suse.com>, "Lyude Paul" <lyude@redhat.com>,
"Danilo Krummrich" <dakr@kernel.org>
Subject: Re: [PATCH v17 06/12] mm/hmm: migrate collection in HMM pagewalk - pmd level
Date: Wed, 04 Nov 2026 06:18:00 +0000 [thread overview]
Message-ID: <sashiko-outbox-196744@kernel.org> (raw)
In-Reply-To: <20261103061629.972669-7-mpenttil@redhat.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] mm/hmm: missing folio_test_anon() check in hmm_vma_handle_migrate_prepare_pmd()
- [High] mm/hmm: missing device-coherent checks in hmm_vma_handle_migrate_prepare_pmd()
- [High] mm/hmm: ABBA folio lock deadlock in migrate_vma_split_folio()
- [Medium] mm/hmm: PMD split in hmm_vma_handle_migrate_prepare_pmd() aborts notifier walk
--
commit 2ed090a278a29feb7fac7d83140b748506dedc9e
Author: Mika Penttilä <mpenttil@redhat.com>
mm/hmm: migrate collection in HMM pagewalk - pmd level
This commit implements hmm_vma_handle_migrate_prepare_pmd() to collect
PMD-mapped THPs or split them into PTEs during HMM pagewalk migration, and
factors out __migrate_vma_split_folio() for splitting folios.
> diff --git a/mm/hmm.c b/mm/hmm.c
> index ec9280ef26822..cbde004fd778a 100644
> --- a/mm/hmm.c
> +++ b/mm/hmm.c
> @@ -511,9 +537,7 @@ static int migrate_vma_split_folio(struct folio *folio,
> struct hmm_vma_walk *hmm_vma_walk,
> pte_t *ptep)
> {
> - int ret;
> struct folio *fault_folio = fault_page ? page_folio(fault_page) : NULL;
> - struct folio *new_fault_folio = NULL;
>
> /*
> * Take the reference while still holding the PTE lock, so the folio
> @@ -530,44 +554,136 @@ static int migrate_vma_split_folio(struct folio *folio,
> if (folio != fault_folio)
> folio_lock(folio);
>
> - ret = split_folio(folio);
> - if (ret) {
> - if (folio != fault_folio) {
> + return __migrate_vma_split_folio(folio, fault_page);
> +}
[Severity: High]
Can calling folio_lock(folio) in migrate_vma_split_folio() cause an ABBA
deadlock when another folio is already held locked in the walk?
During a migrating hmm_range_fault() walk, the calling thread already holds
folio locks on fault_folio (when called from do_swap_page() ->
migrate_to_ram()) as well as on any folios collected at earlier addresses
in the range via hmm_vma_handle_migrate_prepare_pmd() or
hmm_vma_handle_migrate_prepare().
While normal collection in hmm_vma_handle_migrate_prepare() and
hmm_vma_handle_migrate_prepare_pmd() uses folio_trylock() to avoid deadlocking
against concurrent migrations or faults, migrate_vma_split_folio() drops the
PTE lock and sleeps unconditionally in folio_lock(folio):
Thread 1 Thread 2
holds folio A locked
(fault_folio or earlier entry) holds folio B locked
hmm_vma_handle_migrate_prepare()
migrate_vma_split_folio(folio B)
folio_lock(folio B) migrate_vma_split_folio(folio A)
folio_lock(folio A)
(or migration_entry_wait(folio A))
Should migrate_vma_split_folio() also use folio_trylock(folio) and skip
migrating the folio if it cannot be locked?
> +
> +static int hmm_vma_handle_migrate_prepare_pmd(const struct mm_walk *walk,
> + pmd_t *pmdp,
> + unsigned long start,
> + unsigned long end,
> + unsigned long *hmm_pfn)
> +{
> + struct hmm_vma_walk *hmm_vma_walk = walk->private;
> + struct hmm_range *range = hmm_vma_walk->range;
> + struct migrate_vma *migrate = range->migrate;
> + struct folio *fault_folio = NULL;
> + enum migrate_vma_info minfo;
> + struct folio *folio;
> + unsigned long i;
> + int r = 0;
> +
> + // Do we want to migrate at all?
> + minfo = hmm_select_migrate(range);
> + if (!minfo)
> + return r;
> +
> + WARN_ON_ONCE(!migrate);
> + HMM_ASSERT_PMD_LOCKED(hmm_vma_walk, true);
> +
> + fault_folio = migrate->fault_page ?
> + page_folio(migrate->fault_page) : NULL;
> +
> + if (pmd_none(*pmdp))
> + return hmm_pfns_fill(start, end, hmm_vma_walk, 0);
> +
> + /* Like at the pte level, NUMA hinting (protnone) pmds are migrated */
> + if (!(hmm_pfn[0] & HMM_PFN_VALID) &&
> + !(pmd_trans_huge(*pmdp) && pmd_protnone(*pmdp)))
> + goto out;
> +
> + if (pmd_trans_huge(*pmdp)) {
> + if (!(minfo & MIGRATE_VMA_SELECT_SYSTEM))
> + goto out;
[Severity: High]
How are PMD-mapped MEMORY_DEVICE_COHERENT folios handled here?
Compound device-coherent folios are mapped as present huge PMDs where
pmd_trans_huge(*pmdp) is true (via migrate_vma_insert_huge_pmd_page() and
remove_migration_pmd()). At the PTE level, hmm_vma_handle_migrate_prepare()
distinguishes system memory from device-coherent pages:
mm/hmm.c:hmm_vma_handle_migrate_prepare() {
...
page = vm_normal_page(walk->vma, addr, pte);
if (page && !is_zone_device_page(page) &&
!(minfo & MIGRATE_VMA_SELECT_SYSTEM)) {
goto out;
} else if (page && is_device_coherent_page(page)) {
pgmap = page_pgmap(page);
if (!(minfo &
MIGRATE_VMA_SELECT_DEVICE_COHERENT) ||
pgmap->owner != migrate->pgmap_owner)
goto out;
}
...
}
In hmm_vma_handle_migrate_prepare_pmd(), requiring MIGRATE_VMA_SELECT_SYSTEM
for all pmd_trans_huge(*pmdp) entries without checking
folio_is_zone_device(folio), folio_is_device_coherent(folio),
MIGRATE_VMA_SELECT_DEVICE_COHERENT, or folio->pgmap->owner means that
migrating device-coherent memory to system RAM with
MIGRATE_VMA_SELECT_DEVICE_COHERENT skips present device-coherent huge PMDs,
while migrating with MIGRATE_VMA_SELECT_SYSTEM collects or splits
device-coherent huge PMDs regardless of pgmap->owner.
Does the pmd_trans_huge(*pmdp) branch need the same zone_device and
device_coherent checks as hmm_vma_handle_migrate_prepare()?
> +
> + folio = pmd_folio(*pmdp);
> + if (is_huge_zero_folio(folio))
> + return hmm_pfns_fill(start, end, hmm_vma_walk, 0);
> +
> + } else if (!pmd_present(*pmdp)) {
[Severity: High]
Should hmm_vma_handle_migrate_prepare_pmd() check folio_test_anon(folio)
before collecting or splitting a PMD-mapped folio?
At the PTE level, hmm_vma_handle_migrate_prepare() rejects non-anonymous
folios:
mm/hmm.c:hmm_vma_handle_migrate_prepare() {
...
/* Only anonymous memory can be migrated to device memory */
if (!page || !folio_test_anon(page_folio(page)))
goto out;
...
}
Because hmm_vma_capture_migrate_range() and migrate_vma_setup() allow file and
shmem VMAs, a PMD in such a VMA can map a non-anonymous THP. Without a
folio_test_anon(folio) check in hmm_vma_handle_migrate_prepare_pmd():
1. If compound migration is selected, the page-cache folio is locked and a PMD
migration entry is installed with HMM_PFN_MIGRATE | HMM_PFN_COMPOUND.
Holding a page-cache folio locked while hmm_do_fault() -> handle_mm_fault()
faults subsequent pages in the VMA inverts the mapping->invalidate_lock ->
folio_lock ordering. If the caller later splits the unmapped compound
folio in __migrate_device_pages() -> migrate_vma_split_unmapped_folio(),
split_huge_pmd_address() zaps the non-anonymous PMD without dropping the
extra folio reference, and folio_split_unmapped() triggers
VM_WARN_ON_ONCE_FOLIO(!folio_test_anon(folio), folio) before running
__folio_freeze_split_anon() on a page-cache folio:
mm/huge_memory.c:folio_split_unmapped() {
VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio);
VM_WARN_ON_ONCE_FOLIO(!folio_test_anon(folio), folio);
return __folio_freeze_split_anon(folio, new_order, &folio->page,
false, NULL, SPLIT_TYPE_UNIFORM);
}
2. If the split fallback is taken, __migrate_vma_split_folio() ->
split_folio() -> __folio_freeze_split_file() acquires
i_mmap_lock_read(mapping) while holding locks on previously collected
folios and unmaps the file folio completely via unmap_folio(). If the
split fails after unmapping, hmm_vma_handle_migrate_prepare_pmd() returns 0
with stale HMM_PFN_VALID entries for a zapped PMD; if it succeeds, the
re-walk finds pte_none entries and loses the valid PFNs when
HMM_PFN_REQ_FAULT is not set.
[ ... ]
> +split:
> + spin_unlock(hmm_vma_walk->ptl);
> + hmm_vma_walk->pmdlocked = false;
>
> /*
> - * Ensure the lock is held on the correct
> - * folio after the split
> + * folio_get() above took an extra reference. For the fault folio the
> + * caller still holds a reference and the lock, which is the
> + * precondition of __migrate_vma_split_folio(), so drop the extra one.
> */
> - if (!new_fault_folio) {
> - folio_unlock(folio);
> + if (folio == fault_folio)
> folio_put(folio);
> - } else if (folio != new_fault_folio) {
> - if (new_fault_folio != fault_folio) {
> - folio_get(new_fault_folio);
> - folio_lock(new_fault_folio);
> - }
> - folio_unlock(folio);
> - folio_put(folio);
> - }
>
> - return 0;
> -}
> + /* Split failed: not migrated, the valid pfns stay as reported */
> + if (__migrate_vma_split_folio(folio, migrate->fault_page))
> + return 0;
>
> -static int hmm_vma_handle_migrate_prepare_pmd(const struct mm_walk *walk,
> - pmd_t *pmdp,
> - unsigned long start,
> - unsigned long end,
> - unsigned long *hmm_pfn)
> -{
> - // TODO: implement migration entry insertion
> - return 0;
> + /* Re-walked as PTEs from start: drop the pmd-level output */
> + for (i = 0; i < (end - start) >> PAGE_SHIFT; i++)
> + hmm_pfn[i] &= HMM_PFN_INOUT_FLAGS;
> +
> + hmm_vma_walk->last = start;
> + return -EBUSY;
> }
>
> /*
> @@ -1016,6 +1132,11 @@ static int hmm_vma_walk_pmd(pmd_t *pmdp,
> hmm_vma_walk->pmdlocked = false;
> }
>
> + /*
> + * hmm_vma_handle_migrate_prepare_pmd() splits the huge pmd in
> + * place when needed and returns -EBUSY to re-walk the range as
> + * PTEs.
> + */
> return r;
> }
[Severity: Medium]
When range->notifier is set during a migrating hmm_range_fault() call, does
returning -EBUSY out of walk_page_range() after splitting a PMD cause
hmm_range_fault_locked() to abort the walk instead of re-walking the split
PMD as PTEs?
In hmm_vma_handle_migrate_prepare_pmd(), __migrate_vma_split_folio() calls
split_folio() or split_huge_page() -> __folio_split() ->
__folio_freeze_split_anon() -> unmap_folio() -> try_to_migrate_one(), which
invokes mmu_notifier_invalidate_range_start() with MMU_NOTIFY_CLEAR and
range.owner == NULL. Because the event is MMU_NOTIFY_CLEAR rather than
MMU_NOTIFY_MIGRATE with range->dev_private_owner, the registered
mmu_interval_notifier updates range->notifier->invalidate_seq.
When hmm_vma_handle_migrate_prepare_pmd() and hmm_vma_walk_pmd() return
-EBUSY back to hmm_range_fault_locked(), the next iteration of the loop checks
mmu_interval_check_retry():
mm/hmm.c:hmm_range_fault_locked() {
...
do {
/* If range is no longer valid force retry. */
if (range->notifier && mmu_interval_check_retry(range->notifier,
range->notifier_seq)) {
ret = -EBUSY;
break;
}
ret = walk_page_range(mm, hmm_vma_walk.last, range->end,
&hmm_walk_ops, &hmm_vma_walk);
...
}
Because range->notifier was invalidated by the split,
mmu_interval_check_retry() returns true and hmm_range_fault_locked() breaks
out with -EBUSY, forcing a full rollback and retry of the range. By contrast,
the PTE-level split in hmm_vma_handle_migrate_prepare() returns -EAGAIN and
restarts locally at the again label inside hmm_vma_walk_pmd() without
returning to hmm_range_fault_locked().
Could hmm_vma_walk_pmd() similarly loop locally to the again label when
hmm_vma_handle_migrate_prepare_pmd() splits the PMD?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261103061629.972669-1-mpenttil@redhat.com?part=6
next prev parent reply other threads:[~2026-11-04 6:18 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 6:16 [PATCH v17 00/12] migrate on fault for device pages mpenttil
2026-11-03 6:16 ` [PATCH v17 01/12] mm/Kconfig: changes for " mpenttil
2026-11-03 6:16 ` [PATCH v17 02/12] mm: add helper to convert HMM pfn to migrate pfn mpenttil
2026-11-03 6:16 ` [PATCH v17 03/12] mm/hmm: preparations for HMM to participate in migration mpenttil
2026-11-04 6:17 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 04/12] mm/hmm: do the plumbing " mpenttil
2026-11-04 6:17 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 05/12] mm/hmm: migrate collection in HMM pagewalk - pte level mpenttil
2026-11-04 6:17 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 06/12] mm/hmm: migrate collection in HMM pagewalk - pmd level mpenttil
2026-11-04 6:18 ` sashiko-bot [this message]
2026-11-03 6:16 ` [PATCH v17 07/12] mm/hmm: add lazy MMU mode support for migration in HMM pagewalk mpenttil
2026-11-03 6:16 ` [PATCH v17 08/12] mm/hmm: implement rollback for device page " mpenttil
2026-11-04 6:18 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 09/12] mm: enable device page migration from " mpenttil
2026-11-04 6:18 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 10/12] lib/test_hmm: add a new testcase for the migrate on fault mpenttil
2026-11-04 6:18 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 11/12] Documentation/mm/hmm: document migration through hmm_range_fault() mpenttil
2026-11-04 6:18 ` sashiko-bot
2026-11-03 6:16 ` [PATCH v17 12/12] drm/nouveau/svm: add support for migrate on fault mpenttil
2026-11-04 6:18 ` sashiko-bot
2026-11-03 8:46 ` ✗ CI.checkpatch: warning for Migrate on fault for device pages (rev9) Patchwork
2026-11-03 8:48 ` ✓ CI.KUnit: success " Patchwork
2026-11-03 9:04 ` ✗ CI.checksparse: warning " Patchwork
2026-11-03 9:24 ` ✓ Xe.CI.BAT: success " Patchwork
2026-11-03 15:37 ` ✗ Xe.CI.FULL: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-196744@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=apopple@nvidia.com \
--cc=balbirs@nvidia.com \
--cc=dakr@kernel.org \
--cc=david@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=jgg@nvidia.com \
--cc=leonro@nvidia.com \
--cc=liam@infradead.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=lyude@redhat.com \
--cc=matthew.brost@intel.com \
--cc=mhocko@suse.com \
--cc=mpenttil@redhat.com \
--cc=rppt@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=surenb@google.com \
--cc=vbabka@suse.cz \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.