[PATCH RFC v6 1/5] mm/migrate: skip data copy for already-copied folios
From: Shivank Garg
Date: Tue Jun 30 2026 - 03:32:38 EST
Add FOLIO_CONTENT_COPIED marker to the dst->migrate_info migration
state. When set, it tells __migrate_folio() to skip folio_mc_copy()
and perform metadata-only migration. This bit is not set yet, the
batch-copy path enables it later in a subsequent patch.
Update __migrate_folio_extract() to preserve FOLIO_CONTENT_COPIED
while it consumes old states of src folio.
Move the dst->migrate_info state enum to migrate.h header file so
offload drivers can see FOLIO_CONTENT_COPIED.
On 32Bit, we do not have spare bit to store FOLIO_CONTENT_COPIED
info, migration copy offload is disabled for them.
Signed-off-by: Shivank Garg <shivankg@xxxxxxx>
---
include/linux/migrate.h | 25 +++++++++++++++++++++++++
mm/migrate.c | 29 +++++++++++++----------------
2 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index d5af2b7f577b..876035df2fdc 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -72,6 +72,31 @@ int folio_migrate_mapping(struct address_space *mapping,
struct folio *newfolio, struct folio *folio, int extra_count);
int set_movable_ops(const struct movable_operations *ops, enum pagetype type);
+/*
+ * To record some information during migration, we use the migrate_info
+ * field of struct folio of the newly allocated destination folio,
+ * together with the anon_vma pointer. The state is encoded in the
+ * unused low bits of the pointer.
+ * This is safe because nobody is using it except us.
+ *
+ * FOLIO_WAS_MAPPED and FOLIO_WAS_MLOCKED record the src folios's state
+ * at unmap time and are consumed during the move/undo phase.
+ * FOLIO_CONTENT_COPIED tells __migrate_folio() that the folio contents
+ * have already been copied, so the per-folio copy can be skipped. A
+ * driver must set this bit on each dst folio it copied.
+ */
+enum {
+ FOLIO_WAS_MAPPED = BIT(0),
+ FOLIO_WAS_MLOCKED = BIT(1),
+ FOLIO_OLD_STATES = FOLIO_WAS_MAPPED | FOLIO_WAS_MLOCKED,
+#ifdef CONFIG_64BIT
+ FOLIO_CONTENT_COPIED = BIT(2),
+#else
+ /* On 32bit we do not have a spare bit, migration-copy offload is disabled. */
+ FOLIO_CONTENT_COPIED = 0,
+#endif
+};
+
#else
static inline void putback_movable_pages(struct list_head *l) {}
diff --git a/mm/migrate.c b/mm/migrate.c
index 7a83032a14b5..b3f632575c82 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -859,14 +859,21 @@ static int __migrate_folio(struct address_space *mapping, struct folio *dst,
enum migrate_mode mode)
{
int rc, expected_count = folio_expected_ref_count(src) + 1;
+ const bool already_copied = dst->migrate_info & FOLIO_CONTENT_COPIED;
+
+ /* Consume the content copied marker */
+ if (already_copied)
+ dst->migrate_info &= ~FOLIO_CONTENT_COPIED;
/* Check whether src does not have extra refs before we do more work */
if (folio_ref_count(src) != expected_count)
return -EAGAIN;
- rc = folio_mc_copy(dst, src);
- if (unlikely(rc))
- return rc;
+ if (!already_copied) {
+ rc = folio_mc_copy(dst, src);
+ if (unlikely(rc))
+ return rc;
+ }
rc = __folio_migrate_mapping(mapping, dst, src, expected_count);
if (rc)
@@ -1129,17 +1136,6 @@ static int move_to_new_folio(struct folio *dst, struct folio *src,
return rc;
}
-/*
- * To record some information during migration, we use the migrate_info
- * field of struct folio of the newly allocated destination folio.
- * This is safe because nobody is using it except us.
- */
-enum {
- FOLIO_WAS_MAPPED = BIT(0),
- FOLIO_WAS_MLOCKED = BIT(1),
- FOLIO_OLD_STATES = FOLIO_WAS_MAPPED | FOLIO_WAS_MLOCKED,
-};
-
static void __migrate_folio_record(struct folio *dst,
int old_folio_state, struct anon_vma *anon_vma)
{
@@ -1151,9 +1147,10 @@ static void __migrate_folio_extract(struct folio *dst,
{
unsigned long info = dst->migrate_info;
- *anon_vmap = (struct anon_vma *)(info & ~FOLIO_OLD_STATES);
+ *anon_vmap = (struct anon_vma *)(info & ~(FOLIO_OLD_STATES |
+ FOLIO_CONTENT_COPIED));
*old_folio_state = info & FOLIO_OLD_STATES;
- dst->migrate_info = 0;
+ dst->migrate_info &= FOLIO_CONTENT_COPIED;
}
/* Restore the source folio to the original state upon failure */
--
2.43.0