All of lore.kernel.org
 help / color / mirror / Atom feed
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.Howlett@oracle.com>,
	"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 v16 06/12] mm/hmm: migrate collection in HMM pagewalk - pmd level
Date: Thu, 22 Oct 2026 06:06:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-181054@kernel.org> (raw)
In-Reply-To: <20261021060506.2377545-7-mpenttil@redhat.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm/hmm: stale hmm_pfns left across PMD split re-walk in hmm_vma_walk_pmd()
- [Medium] mm/hmm: hmm_vma_handle_migrate_prepare_pmd() clobbers valid PFNs on skip
--

commit 19d71de5ba9a8df49f896d9047f6e5016503f7dc
Author: Mika Penttilä <mpenttil@redhat.com>

mm/hmm: migrate collection in HMM pagewalk - pmd level

This commit implements hmm_vma_handle_migrate_prepare_pmd() and integrates it
into hmm_vma_walk_pmd() to prepare PMD-level THP and device-private entries
for migration or split them for PTE re-walking.

> diff --git a/mm/hmm.c b/mm/hmm.c
> index 0ce947d0e5727..5891efee3e182 100644
> --- a/mm/hmm.c
> +++ b/mm/hmm.c

[ ... ]

> @@ -548,14 +536,145 @@ static int migrate_vma_split_folio(struct folio *folio,
>  	return 0;
>  }
>  
> +/**
> + * migrate_vma_split_folio() - drop the pte lock and split a THP folio
> + * @folio: the folio to split
> + * @fault_page: struct page associated with the fault if any
> + * @hmm_vma_walk: walk in progress
> + * @ptep: pte_t * for unmap and unlock ptl
> + *
> + * Returns 0 on success
> + */
> +static int migrate_vma_split_folio(struct folio *folio,
> +				   struct page *fault_page,
> +				   struct hmm_vma_walk *hmm_vma_walk,
> +				   pte_t *ptep)
> +{
> +	struct folio *fault_folio = fault_page ? page_folio(fault_page) : NULL;
> +
> +	if (folio != fault_folio)
> +		folio_get(folio);
> +
> +	pte_unmap_unlock(ptep, hmm_vma_walk->ptl);
> +	hmm_vma_walk->ptelocked = false;
> +
> +	if (folio != fault_folio)
> +		folio_lock(folio);
> +
> +	return __migrate_vma_split_folio(folio, fault_page);
> +}
> +
>  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)
>  {

[ ... ]

> +	folio_get(folio);
> +
> +	if (folio != fault_folio && unlikely(!folio_trylock(folio))) {
> +		folio_put(folio);
> +		hmm_pfns_fill(start, end, hmm_vma_walk, HMM_PFN_ERROR);
> +		return 0;
> +	}
> +
> +	if (thp_migration_supported() &&
> +	    (migrate->flags & MIGRATE_VMA_SELECT_COMPOUND) &&
> +	    (IS_ALIGNED(start, HPAGE_PMD_SIZE) &&
> +	     IS_ALIGNED(end, HPAGE_PMD_SIZE))) {
> +		struct page_vma_mapped_walk pvmw = {
> +			.ptl = hmm_vma_walk->ptl,
> +			.address = start,
> +			.pmd = pmdp,
> +			.vma = walk->vma,
> +		};
> +
> +		hmm_pfn[0] |= HMM_PFN_MIGRATE | HMM_PFN_COMPOUND;
> +
> +		r = set_pmd_migration_entry(&pvmw, folio_page(folio, 0));
> +		if (r) {
> +			hmm_pfn[0] &= ~(HMM_PFN_MIGRATE | HMM_PFN_COMPOUND);
> +			goto split;	/* fall back to splitting the pmd */
> +		}
> +		for (i = 1, start += PAGE_SIZE; start < end; start += PAGE_SIZE, i++)
> +			hmm_pfn[i] &= HMM_PFN_INOUT_FLAGS;
> +
> +	} else {
> +		goto split;		/* fall back to splitting the pmd */
> +	}
> +
> +out:
> +	return r;
> +
> +split:
> +	spin_unlock(hmm_vma_walk->ptl);
> +	hmm_vma_walk->pmdlocked = false;
> +
> +	/*
> +	 * 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 (folio == fault_folio)
> +		folio_put(folio);
> +
> +	r = __migrate_vma_split_folio(folio, migrate->fault_page);
> +	if (r)
> +		return -EFAULT;
> +
> +	hmm_vma_walk->last = start;
> +	return -EBUSY;
>  }

[Severity: High]
Can returning -EBUSY from the split path in
hmm_vma_handle_migrate_prepare_pmd() leave stale pre-split PMD entries in
range->hmm_pfns[] across the re-walk?

When migration is enabled (in the later commit "mm: enable device page
migration from HMM pagewalk"), hmm_vma_walk_pmd() first calls
hmm_vma_handle_pmd() or hmm_vma_handle_absent_pmd() before calling
hmm_vma_handle_migrate_prepare_pmd():

mm/hmm.c:hmm_vma_walk_pmd()
  hmm_vma_handle_pmd() / hmm_vma_handle_absent_pmd()
    for (i = 0; addr < end; addr += PAGE_SIZE, i++, pfn++) {
        hmm_pfns[i] &= HMM_PFN_INOUT_FLAGS;
        hmm_pfns[i] |= pfn | cpu_flags;
    }
  hmm_vma_handle_migrate_prepare_pmd()

This populates hmm_pfns_start[0..npages-1] with the pre-split PMD PFNs,
HMM_PFN_VALID, HMM_PFN_WRITE, and hmm_pfn_flags_order(PMD_SHIFT - PAGE_SHIFT).

When hmm_vma_handle_migrate_prepare_pmd() then takes the split path (for
example, when MIGRATE_VMA_SELECT_COMPOUND is not set, [start, end) is smaller
than HPAGE_PMD_SIZE, or set_pmd_migration_entry() fails), it drops
hmm_vma_walk->ptl, splits the folio via __migrate_vma_split_folio() (which
also unlocks the folio when folio != fault_folio), sets
hmm_vma_walk->last = start, and returns -EBUSY without clearing
hmm_pfns_start[0..npages-1].

This breaks the hmm_range_fault_locked() expectation that range->hmm_pfns[]
entries at or after hmm_vma_walk.last have not been overwritten with output
flags when -EBUSY is returned.

On the subsequent PTE re-walk of [start, end), if any split page is
concurrently turned into a migration entry while the page table and folio
locks were dropped (with HMM_PFN_REQ_FAULT set), hmm_vma_handle_pte() returns
0 without updating *hmm_pfn:

mm/hmm.c:hmm_vma_handle_pte()
  if (softleaf_is_migration(entry)) {
      if (!hmm_select_migrate(range)) {
          ...
          return -EBUSY;
      }
      return 0;
  }

and hmm_vma_handle_migrate_prepare() sees !pte_present(pte) and
!softleaf_is_device_private(entry) and also returns 0 without updating
*hmm_pfn (similarly, if the PMD became a PMD migration entry,
hmm_vma_walk_pmd() returns 0 without clearing hmm_pfns_start). The later
commit "mm/hmm: implement rollback for device page migration in HMM pagewalk"
only rolls back entries that have both HMM_PFN_VALID and HMM_PFN_MIGRATE set,
so these pre-split entries (which lack HMM_PFN_MIGRATE) remain in
range->hmm_pfns[], allowing callers that map non-migrated HMM PFNs in place
(such as nouveau_dmem_migrate_fault()) to map a stale 2MB host range into the
device MMU.

Furthermore, if a caller sets range->pfn_flags_mask, overwriting
hmm_pfns_start[0..npages-1] with HMM_PFN_VALID (which shares its bit position
with HMM_PFN_REQ_FAULT) and HMM_PFN_WRITE (which shares its bit position with
HMM_PFN_REQ_WRITE) before the split clobbers the per-page request flags for
the PTE re-walk.

> @@ -958,6 +1077,17 @@ 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; any other error means the split failed.
> +		 */
> +		if (r == -EBUSY)
> +			return -EBUSY;
> +		if (r) {
> +			/* Split not successful, skip */
> +			return hmm_pfns_fill(start, end, hmm_vma_walk, HMM_PFN_ERROR);
> +		}
>  		return r;
>  	}

[Severity: Medium]
Should the folio_trylock() failure path in
hmm_vma_handle_migrate_prepare_pmd() and the split failure path here in
hmm_vma_walk_pmd() preserve the valid PFNs in range->hmm_pfns[] instead of
overwriting them with HMM_PFN_ERROR?

Before hmm_vma_handle_migrate_prepare_pmd() is called, hmm_vma_handle_pmd()
(or hmm_vma_handle_absent_pmd()) has already populated
hmm_pfns_start[0..npages-1] with the valid PFNs (HMM_PFN_VALID without
HMM_PFN_MIGRATE).

However, in two best-effort migration skip cases, those valid PFNs are
clobbered with HMM_PFN_ERROR:

1. In hmm_vma_handle_migrate_prepare_pmd(), when folio_trylock(folio) fails
   while holding hmm_vma_walk->ptl, it calls
   hmm_pfns_fill(start, end, hmm_vma_walk, HMM_PFN_ERROR) even though the PMD
   lock was never dropped and the mapping is still valid.

2. When compound migration is not selected or [start, end) is smaller than
   HPAGE_PMD_SIZE (such as single-page faults in nouveau_dmem_migrate_fault()
   in the later commit "drm/nouveau/svm: add support for migrate on fault"),
   hmm_vma_handle_migrate_prepare_pmd() jumps to split and calls
   __migrate_vma_split_folio(). If split_folio() fails (for example, when the
   THP is pinned or has extra references), it returns -EFAULT and
   hmm_vma_walk_pmd() overwrites the range with HMM_PFN_ERROR.

By contrast, the other non-migrating exits in
hmm_vma_handle_migrate_prepare_pmd() (goto out) and folio_trylock() failure
at the PTE level in hmm_vma_handle_migrate_prepare():

mm/hmm.c:hmm_vma_handle_migrate_prepare()
	if (fault_folio == folio || folio_trylock(folio)) {
		...
	} else {
		folio_put(folio);
	}
out:
	return 0;

leave range->hmm_pfns[] with the valid PFNs and without HMM_PFN_MIGRATE.
Because migrate_hmm_range_setup() only collects entries with HMM_PFN_MIGRATE
set, leaving the valid PFNs intact still skips migration while allowing
callers such as nouveau_dmem_migrate_fault() to map the present THP in place
instead of treating it as an error and falling back to a redundant page table
walk and GPU PFNMAP ioctl.

Could both skip paths return 0 without calling hmm_pfns_fill(...,
HMM_PFN_ERROR)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021060506.2377545-1-mpenttil@redhat.com?part=6


  reply	other threads:[~2026-10-22  6:06 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21  6:04 [PATCH v16 00/12] migrate on fault for device pages mpenttil
2026-10-21  6:04 ` [PATCH v16 01/12] mm/Kconfig: changes for " mpenttil
2026-10-21  6:04 ` [PATCH v16 02/12] mm: add helper to convert HMM pfn to migrate pfn mpenttil
2026-10-21  6:04 ` [PATCH v16 03/12] mm/hmm: preparations for HMM to participate in migration mpenttil
2026-10-21  7:17   ` Balbir Singh
2026-10-21  8:11     ` Mika Penttilä
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:04 ` [PATCH v16 04/12] mm/hmm: do the plumbing " mpenttil
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:04 ` [PATCH v16 05/12] mm/hmm: migrate collection in HMM pagewalk - pte level mpenttil
2026-10-21  6:56   ` Balbir Singh
2026-10-21  7:09     ` Mika Penttilä
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:05 ` [PATCH v16 06/12] mm/hmm: migrate collection in HMM pagewalk - pmd level mpenttil
2026-10-22  6:06   ` sashiko-bot [this message]
2026-10-21  6:05 ` [PATCH v16 07/12] mm/hmm: add lazy MMU mode support for migration in HMM pagewalk mpenttil
2026-10-21  6:05 ` [PATCH v16 08/12] mm/hmm: implement rollback for device page " mpenttil
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:05 ` [PATCH v16 09/12] mm: enable device page migration from " mpenttil
2026-10-21  6:19   ` Balbir Singh
2026-10-21  6:30     ` Mika Penttilä
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:05 ` [PATCH v16 10/12] lib/test_hmm: add a new testcase for the migrate on fault mpenttil
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:05 ` [PATCH v16 11/12] Documentation/mm/hmm: document migration through hmm_range_fault() mpenttil
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:05 ` [PATCH v16 12/12] drm/nouveau/svm: add support for migrate on fault mpenttil
2026-10-22  6:06   ` sashiko-bot
2026-10-21  6:50 ` ✗ CI.checkpatch: warning for Migrate on fault for device pages (rev8) Patchwork
2026-10-21  6:53 ` ✓ CI.KUnit: success " Patchwork
2026-10-21  7:09 ` ✗ CI.checksparse: warning " Patchwork
2026-10-21  7:29 ` ✓ Xe.CI.BAT: success " Patchwork
2026-10-21  9:50 ` ✗ 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-181054@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Liam.Howlett@oracle.com \
    --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=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.