mm/migrate: Add migrate_device_pfns

Add migrate_device_pfns which prepares an array of pre-populated device
pages for migration. This is needed for eviction of known set of
non-contiguous devices pages to cpu pages which is a common case for SVM
in DRM drivers using TTM.

v2:
 - s/migrate_device_vma_range/migrate_device_prepopulated_range
 - Drop extra mmu invalidation (Vetter)
v3:
 - s/migrate_device_prepopulated_range/migrate_device_pfns (Alistar)
 - Use helper to lock device pages (Alistar)
 - Update commit message with why this is required (Alistar)

Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Alistair Popple <apopple@nvidia.com>
Reviewed-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250306012657.3505757-3-matthew.brost@intel.com
This commit is contained in:
Matthew Brost
2025-03-05 17:26:27 -08:00
parent 1d724a2f1b
commit a14fa8ec9d
2 changed files with 40 additions and 17 deletions

View File

@@ -227,6 +227,7 @@ void migrate_vma_pages(struct migrate_vma *migrate);
void migrate_vma_finalize(struct migrate_vma *migrate);
int migrate_device_range(unsigned long *src_pfns, unsigned long start,
unsigned long npages);
int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages);
void migrate_device_pages(unsigned long *src_pfns, unsigned long *dst_pfns,
unsigned long npages);
void migrate_device_finalize(unsigned long *src_pfns,

View File

@@ -871,6 +871,22 @@ void migrate_vma_finalize(struct migrate_vma *migrate)
}
EXPORT_SYMBOL(migrate_vma_finalize);
static unsigned long migrate_device_pfn_lock(unsigned long pfn)
{
struct folio *folio;
folio = folio_get_nontail_page(pfn_to_page(pfn));
if (!folio)
return 0;
if (!folio_trylock(folio)) {
folio_put(folio);
return 0;
}
return migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
}
/**
* migrate_device_range() - migrate device private pfns to normal memory.
* @src_pfns: array large enough to hold migrating source device private pfns.
@@ -895,23 +911,8 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start,
{
unsigned long i, pfn;
for (pfn = start, i = 0; i < npages; pfn++, i++) {
struct folio *folio;
folio = folio_get_nontail_page(pfn_to_page(pfn));
if (!folio) {
src_pfns[i] = 0;
continue;
}
if (!folio_trylock(folio)) {
src_pfns[i] = 0;
folio_put(folio);
continue;
}
src_pfns[i] = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
}
for (pfn = start, i = 0; i < npages; pfn++, i++)
src_pfns[i] = migrate_device_pfn_lock(pfn);
migrate_device_unmap(src_pfns, npages, NULL);
@@ -919,6 +920,27 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start,
}
EXPORT_SYMBOL(migrate_device_range);
/**
* migrate_device_pfns() - migrate device private pfns to normal memory.
* @src_pfns: pre-popluated array of source device private pfns to migrate.
* @npages: number of pages to migrate.
*
* Similar to migrate_device_range() but supports non-contiguous pre-popluated
* array of device pages to migrate.
*/
int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages)
{
unsigned long i;
for (i = 0; i < npages; i++)
src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]);
migrate_device_unmap(src_pfns, npages, NULL);
return 0;
}
EXPORT_SYMBOL(migrate_device_pfns);
/*
* Migrate a device coherent folio back to normal memory. The caller should have
* a reference on folio which will be copied to the new folio if migration is