[PATCH RFC 03/11] mm/migrate: factor out folio splitting on allocation failure
From: Shivank Garg
Date: Wed Sep 02 2026 - 07:27:12 EST
If destination folio allocation fails, migrate_folios_unmap() may split
a large folio and retry migration with smaller folios. Factor this logic
out into migrate_folio_split_on_alloc_fail() to simplify the allocation
failure path.
No functional change intended.
Signed-off-by: Shivank Garg <shivankg@xxxxxxx>
---
mm/migrate.c | 69 +++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 43 insertions(+), 26 deletions(-)
diff --git a/mm/migrate.c b/mm/migrate.c
index 89207bf515b9..510dfffa7235 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1809,6 +1809,34 @@ static void migrate_folios_undo(struct list_head *src_folios,
}
}
+static int migrate_folio_split_on_alloc_fail(struct folio *folio,
+ struct list_head *split_folios, enum migrate_mode mode,
+ enum migrate_reason reason, struct migrate_pages_stats *stats)
+{
+ bool is_thp = folio_test_pmd_mappable(folio);
+ int rc;
+
+ /* Large folio NUMA faulting doesn't split to retry. */
+ if (!folio_test_large(folio) || reason == MR_NUMA_MISPLACED)
+ return -ENOMEM;
+
+ rc = try_split_folio(folio, split_folios, mode);
+ if (!rc) {
+ stats->nr_thp_split += is_thp;
+ stats->nr_split++;
+ return 0;
+ }
+
+ /*
+ * Try again to split large folio to mitigate the failure of longterm
+ * pinning.
+ */
+ if (reason == MR_LONGTERM_PIN && rc == -EAGAIN)
+ return -EAGAIN;
+
+ return -ENOMEM;
+}
+
/*
* Unmap the folios in @from, queuing successfully unmapped sources on
* @unmap_folios and their destination on @dst_folios. Increment @nr_failed for
@@ -1829,10 +1857,8 @@ static int migrate_folios_unmap(struct list_head *from,
int nr_retry_pages = 0;
int pass = 0;
bool is_thp = false;
- bool is_large = false;
struct folio *folio, *folio2, *dst = NULL;
- int rc, nr_pages;
- bool nosplit = (reason == MR_NUMA_MISPLACED);
+ int rc, split_rc, nr_pages;
for (pass = 0; pass < nr_pass && retry; pass++) {
retry = 0;
@@ -1840,7 +1866,6 @@ static int migrate_folios_unmap(struct list_head *from,
nr_retry_pages = 0;
list_for_each_entry_safe(folio, folio2, from, lru) {
- is_large = folio_test_large(folio);
is_thp = folio_test_pmd_mappable(folio);
nr_pages = folio_nr_pages(folio);
@@ -1937,28 +1962,20 @@ static int migrate_folios_unmap(struct list_head *from,
*/
*nr_failed += 1;
stats->nr_thp_failed += is_thp;
- /* Large folio NUMA faulting doesn't split to retry. */
- if (is_large && !nosplit) {
- int ret = try_split_folio(folio, split_folios, mode);
-
- if (!ret) {
- stats->nr_thp_split += is_thp;
- stats->nr_split++;
- break;
- } else if (reason == MR_LONGTERM_PIN &&
- ret == -EAGAIN) {
- /*
- * Try again to split large folio to
- * mitigate the failure of longterm pinning.
- */
- retry++;
- thp_retry += is_thp;
- nr_retry_pages += nr_pages;
- /* Undo duplicated failure counting. */
- *nr_failed -= 1;
- stats->nr_thp_failed -= is_thp;
- break;
- }
+
+ split_rc = migrate_folio_split_on_alloc_fail(folio,
+ split_folios, mode, reason, stats);
+ if (!split_rc)
+ break;
+ /* Retry this folio in a later pass. */
+ if (split_rc == -EAGAIN) {
+ retry++;
+ thp_retry += is_thp;
+ nr_retry_pages += nr_pages;
+ /* Undo duplicated failure counting. */
+ *nr_failed -= 1;
+ stats->nr_thp_failed -= is_thp;
+ break;
}
stats->nr_failed_pages += nr_pages + nr_retry_pages;
--
2.43.0