* [PATCH] mm/migrate_device: consolidate compound folio handling
@ 2026-09-11 6:13 Hui Su
2026-09-11 18:01 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 3+ messages in thread
From: Hui Su @ 2026-09-11 6:13 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Matthew Brost, Balbir Singh, Zi Yan, Joshua Hahn, Rakie Kim,
Byungchul Park, Gregory Price, Ying Huang, Alistair Popple,
linux-mm, linux-kernel, Hui Su
Commit dc41e961a269 ("mm/migrate_device: avoid out-of-bounds writes for
compound folios") added handling for compound folios that do not fit in
the remaining PFN array.
migrate_device_range() and migrate_device_pfns() duplicate the logic for
locking device PFNs, encoding compound folios, and handling this boundary
condition.
A compound folio cannot be represented partially for migration. Warn when
one does not fit in the remaining PFN array, while retaining the existing
defensive handling: release any lock and reference acquired for the
current folio, clear the remaining entries, and stop collecting.
Move the shared collection and encoding logic into a helper so both
interfaces handle compound folios consistently. Also use memset() for
the compound-folio tail entries instead of open-coding the clearing loop.
Document that an encountered compound folio must fit entirely in the
remaining range or PFN array.
Link: https://lore.kernel.org/r/c99ca53a-73ef-4a0c-8738-eba1cc89bea2@kernel.org
Suggested-by: David Hildenbrand <david@kernel.org>
Signed-off-by: Hui Su <sh_def@163.com>
---
Tested on x86_64 with KASAN enabled:
- Built mm/migrate_device.o and the kernel successfully.
- Ran the HMM migrate_anon_huge_zero selftest; the private-device case
passed.
- No KASAN report or truncated compound-folio WARN was observed.
mm/migrate_device.c | 90 ++++++++++++++++++++++++---------------------
1 file changed, 49 insertions(+), 41 deletions(-)
diff --git a/mm/migrate_device.c b/mm/migrate_device.c
index 009bfa8b212d..c57eadea6565 100644
--- a/mm/migrate_device.c
+++ b/mm/migrate_device.c
@@ -1392,6 +1392,39 @@ static unsigned long migrate_device_pfn_lock(unsigned long pfn)
return migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
}
+/*
+ * Collect a device folio into the page-granular PFN array.
+ *
+ * Return the number of entries consumed, or 0 if the folio does not fit in
+ * the remaining array.
+ */
+static unsigned int migrate_device_collect_folio(unsigned long *src_pfn,
+ unsigned long pfn,
+ unsigned long remaining)
+{
+ struct folio *folio = page_folio(pfn_to_page(pfn));
+ unsigned int nr;
+
+ *src_pfn = migrate_device_pfn_lock(pfn);
+ nr = folio_nr_pages(folio);
+
+ if (WARN_ON_ONCE(nr > remaining)) {
+ if (*src_pfn & MIGRATE_PFN_MIGRATE) {
+ folio_unlock(folio);
+ folio_put(folio);
+ }
+ memset(src_pfn, 0, remaining * sizeof(*src_pfn));
+ return 0;
+ }
+
+ if (nr > 1) {
+ *src_pfn |= MIGRATE_PFN_COMPOUND;
+ memset(src_pfn + 1, 0, (nr - 1) * sizeof(*src_pfn));
+ }
+
+ return nr;
+}
+
/**
* migrate_device_range() - migrate device private pfns to normal memory.
* @src_pfns: array large enough to hold migrating source device private pfns.
@@ -1410,35 +1443,22 @@ static unsigned long migrate_device_pfn_lock(unsigned long pfn)
* migrating pages that aren't free before unmapping them. Drivers may then
* allocate destination pages and start copying data from the device to CPU
* memory before calling migrate_device_pages().
+ *
+ * A compound folio must fit entirely in the remaining range.
*/
int migrate_device_range(unsigned long *src_pfns, unsigned long start,
unsigned long npages)
{
- unsigned long i, j, pfn;
+ unsigned long i, pfn;
for (pfn = start, i = 0; i < npages; pfn++, i++) {
- struct page *page = pfn_to_page(pfn);
- struct folio *folio = page_folio(page);
- unsigned int nr = 1;
+ unsigned int nr;
- src_pfns[i] = migrate_device_pfn_lock(pfn);
- nr = folio_nr_pages(folio);
- if (nr > npages - i) {
- if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
- folio_unlock(folio);
- folio_put(folio);
- }
- memset(&src_pfns[i], 0,
- (npages - i) * sizeof(*src_pfns));
+ nr = migrate_device_collect_folio(&src_pfns[i], pfn, npages - i);
+ if (!nr)
break;
- }
- if (nr > 1) {
- src_pfns[i] |= MIGRATE_PFN_COMPOUND;
- for (j = 1; j < nr; j++)
- src_pfns[i+j] = 0;
- i += j - 1;
- pfn += j - 1;
- }
+ i += nr - 1;
+ pfn += nr - 1;
}
migrate_device_unmap(src_pfns, npages, NULL);
@@ -1454,33 +1474,21 @@ EXPORT_SYMBOL(migrate_device_range);
*
* Similar to migrate_device_range() but supports non-contiguous pre-populated
* array of device pages to migrate.
+ *
+ * A compound folio must fit entirely in the remaining PFN array.
*/
int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages)
{
- unsigned long i, j;
+ unsigned long i;
for (i = 0; i < npages; i++) {
- struct page *page = pfn_to_page(src_pfns[i]);
- struct folio *folio = page_folio(page);
- unsigned int nr = 1;
+ unsigned long pfn = src_pfns[i];
+ unsigned int nr;
- src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]);
- nr = folio_nr_pages(folio);
- if (nr > npages - i) {
- if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
- folio_unlock(folio);
- folio_put(folio);
- }
- memset(&src_pfns[i], 0,
- (npages - i) * sizeof(*src_pfns));
+ nr = migrate_device_collect_folio(&src_pfns[i], pfn, npages - i);
+ if (!nr)
break;
- }
- if (nr > 1) {
- src_pfns[i] |= MIGRATE_PFN_COMPOUND;
- for (j = 1; j < nr; j++)
- src_pfns[i+j] = 0;
- i += j - 1;
- }
+ i += nr - 1;
}
migrate_device_unmap(src_pfns, npages, NULL);
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] mm/migrate_device: consolidate compound folio handling
2026-09-11 6:13 [PATCH] mm/migrate_device: consolidate compound folio handling Hui Su
@ 2026-09-11 18:01 ` David Hildenbrand (Arm)
2026-09-12 3:10 ` Hui Su
0 siblings, 1 reply; 3+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-11 18:01 UTC (permalink / raw)
To: Hui Su, Andrew Morton
Cc: Matthew Brost, Balbir Singh, Zi Yan, Joshua Hahn, Rakie Kim,
Byungchul Park, Gregory Price, Ying Huang, Alistair Popple,
linux-mm, linux-kernel
On 9/11/26 08:13, Hui Su wrote:
> Commit dc41e961a269 ("mm/migrate_device: avoid out-of-bounds writes for
> compound folios") added handling for compound folios that do not fit in
> the remaining PFN array.
>
> migrate_device_range() and migrate_device_pfns() duplicate the logic for
> locking device PFNs, encoding compound folios, and handling this boundary
> condition.
>
> A compound folio cannot be represented partially for migration. Warn when
> one does not fit in the remaining PFN array, while retaining the existing
> defensive handling: release any lock and reference acquired for the
> current folio, clear the remaining entries, and stop collecting.
>
> Move the shared collection and encoding logic into a helper so both
> interfaces handle compound folios consistently. Also use memset() for
> the compound-folio tail entries instead of open-coding the clearing loop.
>
> Document that an encountered compound folio must fit entirely in the
> remaining range or PFN array.
>
> Link: https://lore.kernel.org/r/c99ca53a-73ef-4a0c-8738-eba1cc89bea2@kernel.org
> Suggested-by: David Hildenbrand <david@kernel.org>
> Signed-off-by: Hui Su <sh_def@163.com>
> ---
> Tested on x86_64 with KASAN enabled:
>
> - Built mm/migrate_device.o and the kernel successfully.
> - Ran the HMM migrate_anon_huge_zero selftest; the private-device case
> passed.
> - No KASAN report or truncated compound-folio WARN was observed.
>
> mm/migrate_device.c | 90 ++++++++++++++++++++++++---------------------
> 1 file changed, 49 insertions(+), 41 deletions(-)
>
> diff --git a/mm/migrate_device.c b/mm/migrate_device.c
> index 009bfa8b212d..c57eadea6565 100644
> --- a/mm/migrate_device.c
> +++ b/mm/migrate_device.c
> @@ -1392,6 +1392,39 @@ static unsigned long migrate_device_pfn_lock(unsigned long pfn)
> return migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
> }
>
> +/*
> + * Collect a device folio into the page-granular PFN array.
> + *
> + * Return the number of entries consumed, or 0 if the folio does not fit in
> + * the remaining array.
> + */
> +static unsigned int migrate_device_collect_folio(unsigned long *src_pfn,
> + unsigned long pfn,
> + unsigned long remaining)
Two tab indent please.
> +{
> + struct folio *folio = page_folio(pfn_to_page(pfn));
> + unsigned int nr;
> +
> + *src_pfn = migrate_device_pfn_lock(pfn);
> + nr = folio_nr_pages(folio);
> +
> + if (WARN_ON_ONCE(nr > remaining)) {
Just to be sure: there is no way we can currently validly trigger this, right?
> + if (*src_pfn & MIGRATE_PFN_MIGRATE) {
> + folio_unlock(folio);
> + folio_put(folio);
> + }
> + memset(src_pfn, 0, remaining * sizeof(*src_pfn));
> + return 0;
> + }
> +
> + if (nr > 1) {
> + *src_pfn |= MIGRATE_PFN_COMPOUND;
> + memset(src_pfn + 1, 0, (nr - 1) * sizeof(*src_pfn));
> + }
> +
> + return nr;
> +}
Looks much cleaner.
--
Cheers,
David
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] mm/migrate_device: consolidate compound folio handling
2026-09-11 18:01 ` David Hildenbrand (Arm)
@ 2026-09-12 3:10 ` Hui Su
0 siblings, 0 replies; 3+ messages in thread
From: Hui Su @ 2026-09-12 3:10 UTC (permalink / raw)
To: David Hildenbrand (Arm), Andrew Morton
Cc: Matthew Brost, Balbir Singh, Zi Yan, Joshua Hahn, Rakie Kim,
Byungchul Park, Gregory Price, Ying Huang, Alistair Popple,
linux-mm, linux-kernel
Hi David,
> Two tab indent please.
>
> Just to be sure: there is no way we can currently validly trigger this, right?
Correct, as far as I can tell. I checked the current in-tree callers and do
not see a valid path that can trigger this condition.
The migrate_device_range() callers evict complete device-memory chunks,
and the migrate_device_pfns() caller expects large source folios to be
represented whole. In these cases, the supplied range or PFN array is
large enough to contain each encountered compound folio completely,
which is required because a compound folio cannot be partially migrated.
The condition is still reachable with invalid, truncated input. For
example, on a 4 KiB base-page system, passing only one entry for the head
of a 2 MiB device compound folio would leave one entry remaining while
folio_nr_pages() is 512. That would be caller misuse, and is the invariant
checked by the WARN_ON_ONCE().
I reran the original HMM migrate_anon_huge_zero reproducer on current
mainline. The private-device case passes, the coherent-device case is
skipped because DEVICE_COHERENT is unavailable in my test configuration,
and the test exits with status 0. I also instrumented the truncated
compound-folio condition; it was not hit, and no KASAN report was
observed.
I'll fix the helper parameter indentation and resend the patch.
Thanks,
Hui
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-12 3:11 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-11 6:13 [PATCH] mm/migrate_device: consolidate compound folio handling Hui Su
2026-09-11 18:01 ` David Hildenbrand (Arm)
2026-09-12 3:10 ` Hui Su
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).