This adds support for migrating file backed pages with the migrate_vma calls. Note that this does not support migrating file backed pages to device private pages, only CPU addressable memory. However add the extra refcount argument to support faulting on device privatge pages in future. Signed-off-by: Alistair Popple <apopple@xxxxxxxxxx> --- Known issues with the RFC: - Some filesystems (eg. xfs, nfs) can insert higher order compound pages in the pagecache. Migration will fail for such pages. --- include/linux/migrate.h | 4 ++++ mm/migrate.c | 19 +++++++++++-------- mm/migrate_device.c | 11 +++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/linux/migrate.h b/include/linux/migrate.h index 29919fa..9023d0f 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -222,6 +222,10 @@ struct migrate_vma { struct page *fault_page; }; +int fallback_migrate_folio(struct address_space *mapping, + struct folio *dst, struct folio *src, enum migrate_mode mode, + int extra_count); + int migrate_vma_setup(struct migrate_vma *args); void migrate_vma_pages(struct migrate_vma *migrate); void migrate_vma_finalize(struct migrate_vma *migrate); diff --git a/mm/migrate.c b/mm/migrate.c index fb19a18..11fca43 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -749,9 +749,10 @@ EXPORT_SYMBOL(folio_migrate_flags); static int __migrate_folio(struct address_space *mapping, struct folio *dst, struct folio *src, void *src_private, - enum migrate_mode mode) + enum migrate_mode mode, int extra_count) { - int rc, expected_count = folio_expected_refs(mapping, src); + int rc; + int expected_count = folio_expected_refs(mapping, src) + extra_count; /* Check whether src does not have extra refs before we do more work */ if (folio_ref_count(src) != expected_count) @@ -788,7 +789,7 @@ int migrate_folio(struct address_space *mapping, struct folio *dst, struct folio *src, enum migrate_mode mode) { BUG_ON(folio_test_writeback(src)); /* Writeback must be complete */ - return __migrate_folio(mapping, dst, src, NULL, mode); + return __migrate_folio(mapping, dst, src, NULL, mode, 0); } EXPORT_SYMBOL(migrate_folio); @@ -942,7 +943,8 @@ EXPORT_SYMBOL_GPL(buffer_migrate_folio_norefs); int filemap_migrate_folio(struct address_space *mapping, struct folio *dst, struct folio *src, enum migrate_mode mode) { - return __migrate_folio(mapping, dst, src, folio_get_private(src), mode); + return __migrate_folio(mapping, dst, src, + folio_get_private(src), mode, 0); } EXPORT_SYMBOL_GPL(filemap_migrate_folio); @@ -990,8 +992,9 @@ static int writeout(struct address_space *mapping, struct folio *folio) /* * Default handling if a filesystem does not provide a migration function. */ -static int fallback_migrate_folio(struct address_space *mapping, - struct folio *dst, struct folio *src, enum migrate_mode mode) +int fallback_migrate_folio(struct address_space *mapping, + struct folio *dst, struct folio *src, enum migrate_mode mode, + int extra_count) { if (folio_test_dirty(src)) { /* Only writeback folios in full synchronous migration */ @@ -1011,7 +1014,7 @@ static int fallback_migrate_folio(struct address_space *mapping, if (!filemap_release_folio(src, GFP_KERNEL)) return mode == MIGRATE_SYNC ? -EAGAIN : -EBUSY; - return migrate_folio(mapping, dst, src, mode); + return __migrate_folio(mapping, dst, src, NULL, mode, extra_count); } /* @@ -1052,7 +1055,7 @@ static int move_to_new_folio(struct folio *dst, struct folio *src, rc = mapping->a_ops->migrate_folio(mapping, dst, src, mode); else - rc = fallback_migrate_folio(mapping, dst, src, mode); + rc = fallback_migrate_folio(mapping, dst, src, mode, 0); } else { const struct movable_operations *mops; diff --git a/mm/migrate_device.c b/mm/migrate_device.c index afc033b..7bcc177 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -763,11 +763,18 @@ static void __migrate_device_pages(unsigned long *src_pfns, if (migrate && migrate->fault_page == page) extra_cnt = 1; - r = folio_migrate_mapping(mapping, newfolio, folio, extra_cnt); + if (mapping) + r = fallback_migrate_folio(mapping, newfolio, folio, + MIGRATE_SYNC, extra_cnt); + else + r = folio_migrate_mapping(mapping, newfolio, folio, + extra_cnt); if (r != MIGRATEPAGE_SUCCESS) src_pfns[i] &= ~MIGRATE_PFN_MIGRATE; - else + else if (!mapping) folio_migrate_flags(newfolio, folio); + else + folio->mapping = NULL; } if (notified) -- git-series 0.9.1