dri-devel Archive on 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: 28+ 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

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox