Re: [PATCH 5/7] mm/migrate: add copy offload registration infrastructure

From: Garg, Shivank

Date: Wed May 20 2026 - 12:06:31 EST




On 5/11/2026 9:16 PM, David Hildenbrand (Arm) wrote:
> On 4/28/26 17:50, Shivank Garg wrote:
>> Add a registration interface that lets a single offload provider
>> (DMA, multi-threaded CPU copy, etc) take over the batch folio copy
>> performed by migrate_pages_batch().
>>
>> The provider fills in a struct migrator with an offload_copy()
>> callback and calls migrate_offload_register(). Registration patches
>> the migrate_offload_copy() static_call and flips the
>> migrate_offload_enabled static branch. The migrate_offload_unregister()
>> reverts both.
>>
>> Whether a migration reason is batch-copy eligible is decided by the
>> core in migrate_offload_do_batch(). A migrator may decline a particular
>> batch (e.g. when nr_batch is too small to amortize setup) by returning
>> -EOPNOTSUPP, and the move phase falls back to per-folio CPU copy.
>>
>> Only one migrator can be active at a time. A second registration
>> returns -EBUSY, and only the active migrator can unregister itself.
>> The static_call dispatch is protected by SRCU so that the
>> synchronize_srcu() in unregister waits for all in-flight copy before
>> the module reference is dropped.
>>
>> Co-developed-by: Mike Day <michael.day@xxxxxxx>
>> Signed-off-by: Mike Day <michael.day@xxxxxxx>
>> Signed-off-by: Shivank Garg <shivankg@xxxxxxx>
>
> MAINTAINERS file deserves some love (likely put the new files under the
> migration section)

Sure, will add this in the next posting.

[...]

>> +++ b/mm/migrate.c
>> @@ -44,6 +44,8 @@
>> #include <linux/memory-tiers.h>
>> #include <linux/pagewalk.h>
>> #include <linux/jump_label.h>
>> +#include <linux/static_call.h>
>> +#include <linux/migrate_copy_offload.h>
>>
>> #include <asm/tlbflush.h>
>>
>> @@ -54,6 +56,51 @@
>>
>> DEFINE_STATIC_KEY_FALSE(migrate_offload_enabled);
>>
>> +#ifdef CONFIG_MIGRATION_COPY_OFFLOAD
>> +DEFINE_SRCU(migrate_offload_srcu);
>> +DEFINE_STATIC_CALL(migrate_offload_copy, folios_mc_copy);
>> +
>> +static bool migrate_offload_do_batch(int reason)
>> +{
>> + if (!static_branch_unlikely(&migrate_offload_enabled))
>> + return false;
>> +
>> + switch (reason) {
>> + case MR_COMPACTION:
>> + case MR_SYSCALL:
>> + case MR_DEMOTION:
>> + case MR_NUMA_MISPLACED:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> +static int migrate_offload_batch_copy(struct list_head *dst_batch,
>> + struct list_head *src_batch,
>> + unsigned int nr_batch)
>> +{
>> + int idx, rc;
>> +
>> + idx = srcu_read_lock(&migrate_offload_srcu);
>> + rc = static_call(migrate_offload_copy)(dst_batch, src_batch, nr_batch);
>> + srcu_read_unlock(&migrate_offload_srcu, idx);
>> + return rc;
>> +}
>> +#else
>> +static bool migrate_offload_do_batch(int reason)
>> +{
>> + return false;
>> +}
>> +
>> +static int migrate_offload_batch_copy(struct list_head *dst_batch,
>> + struct list_head *src_batch,
>> + unsigned int nr_batch)
>> +{
>> + return -EOPNOTSUPP;
>> +}
>> +#endif
>
> Can't all that go to migrate_copy_offload.(hc) Looks like a clean interface form
> migrate.c, no?
>

Yes, Will do.

>> +
>> static const struct movable_operations *offline_movable_ops;
>> static const struct movable_operations *zsmalloc_movable_ops;
>>
>> @@ -1833,7 +1880,7 @@ static int migrate_pages_batch(struct list_head *from,
>> struct folio *folio, *folio2, *dst = NULL;
>> int rc, rc_saved = 0, nr_pages;
>> unsigned int nr_batch = 0;
>> - bool batch_copied = false;
>> + bool do_batch = false, batch_copied = false;
>> LIST_HEAD(unmap_batch);
>> LIST_HEAD(dst_batch);
>> LIST_HEAD(unmap_single);
>> @@ -1843,6 +1890,8 @@ static int migrate_pages_batch(struct list_head *from,
>> VM_WARN_ON_ONCE(mode != MIGRATE_ASYNC &&
>> !list_empty(from) && !list_is_singular(from));
>>
>> + do_batch = migrate_offload_do_batch(reason);
>> +
>> for (pass = 0; pass < nr_pass && retry; pass++) {
>> retry = 0;
>> thp_retry = 0;
>> @@ -1984,8 +2033,7 @@ static int migrate_pages_batch(struct list_head *from,
>> nr_retry_pages += nr_pages;
>> break;
>> case 0:
>> - if (static_branch_unlikely(&migrate_offload_enabled) &&
>> - folio_supports_batch_copy(folio)) {
>> + if (do_batch && folio_supports_batch_copy(folio)) {
>
> I was about to say, the migrate_offload_enabled usage in the previous patch
> looks off.
>
> Can you move that to this patch here, and use in the previous patch simply a
> default migrate_offload_do_batch() that simply returns "false" ?
>

Yes, will do.
This is more clean.

Thanks,
Shivank