summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance Yang <lance.yang@linux.dev>2026-10-01 00:04:32 +0800
committerDavid Hildenbrand (Arm) <david@kernel.org>2026-10-01 21:52:30 +0200
commitc31bb839fb829958101250ee5acfa4f94fdb7ecd (patch)
treef7bd69a191163d426ccf235236093a4e1e9a20d3
parentfa4df92e3d18e2428d7313350350de21824e183d (diff)
mm/memory_hotplug: fix missing rollback in __add_pages()
__add_pages() returns on a sparse_add_section() failure without removing the sections already added in the same request. For memremap_pages(), the failed range is not counted in pgmap->nr_range, so memunmap_pages() skips it. The sections already added in that range retain their vmemmap mappings and subsection bits. Retrying a section-aligned range can then fail with -EEXIST. Save the initial PFN and remove [start_pfn, pfn) on failure. For a vmemmap population failure, section_activate() already cleans up the current section, so the rollback excludes it. If the first section fails, __remove_pages() receives an empty range and does nothing. Link: https://lore.kernel.org/all/BAD58999-1EDD-4A37-ABA0-DB1BD8AB3453@linux.dev/ Suggested-by: Muchun Song <muchun.song@linux.dev> Signed-off-by: Lance Yang <lance.yang@linux.dev> Fixes: ba72b4c8cf60 ("mm/sparsemem: support sub-section hotplug") Acked-by: Muchun Song <muchun.song@linux.dev> Link: https://patch.msgid.link/20260930160432.5564-1-lance.yang@linux.dev Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
-rw-r--r--mm/memory_hotplug.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 100941d1b828..3f2bb2828ab1 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -384,6 +384,7 @@ EXPORT_SYMBOL_GPL(pfn_to_online_page);
int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
struct mhp_params *params)
{
+ const unsigned long start_pfn = pfn;
const unsigned long end_pfn = pfn + nr_pages;
unsigned long cur_nr_pages;
int err;
@@ -417,8 +418,11 @@ int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
SECTION_ALIGN_UP(pfn + 1) - pfn);
err = sparse_add_section(nid, pfn, cur_nr_pages, altmap,
params->pgmap);
- if (err)
+ if (err) {
+ __remove_pages(start_pfn, pfn - start_pfn, altmap,
+ params->pgmap);
break;
+ }
cond_resched();
}
vmemmap_populate_print_last();