diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index b27ddce5d324..17d53001f812 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -282,6 +282,7 @@ extern void try_offline_node(int nid); extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages, struct zone *zone, struct memory_group *group); extern int remove_memory(u64 start, u64 size); +extern int remove_memory_subsection(u64 start, u64 size); extern void __remove_memory(u64 start, u64 size); extern int offline_and_remove_memory(u64 start, u64 size); @@ -306,6 +307,7 @@ static inline void __remove_memory(u64 start, u64 size) {} extern void __ref free_area_init_core_hotplug(struct pglist_data *pgdat); extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); +extern int add_memory_subsection(int nid, u64 start, u64 size); extern int add_memory_resource(int nid, struct resource *resource, mhp_t mhp_flags); extern int add_memory_driver_managed(int nid, u64 start, u64 size, diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 619445096ef4..fc14833ae953 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1607,6 +1607,46 @@ int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags) } EXPORT_SYMBOL_GPL(add_memory); +int add_memory_subsection(int nid, u64 start, u64 size) +{ + struct mhp_params params = { .pgprot = PAGE_KERNEL }; + struct resource *res; + int ret; + + if (size == memory_block_size_bytes()) + return add_memory(nid, start, size, MHP_NONE); + + if (!IS_ALIGNED(start, SUBSECTION_SIZE) || + !IS_ALIGNED(size, SUBSECTION_SIZE)) { + pr_err("%s: start 0x%llx size 0x%llx not aligned to subsection size\n", + __func__, start, size); + return -EINVAL; + } + + res = register_memory_resource(start, size, "System RAM"); + if (IS_ERR(res)) + return PTR_ERR(res); + + mem_hotplug_begin(); + + nid = memory_add_physaddr_to_nid(start); + + if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) + memblock_add_node(start, size, nid, MEMBLOCK_NONE); + + ret = arch_add_memory(nid, start, size, ¶ms); + if (ret) { + if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) + memblock_remove(start, size); + pr_err("%s failed to add subsection start 0x%llx size 0x%llx\n", + __func__, start, size); + } + mem_hotplug_done(); + + return ret; +} +EXPORT_SYMBOL_GPL(add_memory_subsection); + /* * Add special, driver-managed memory to the system as system RAM. Such * memory is not exposed via the raw firmware-provided memmap as system @@ -2320,6 +2360,40 @@ int remove_memory(u64 start, u64 size) } EXPORT_SYMBOL_GPL(remove_memory); +int remove_memory_subsection(u64 start, u64 size) +{ + if (size == memory_block_size_bytes()) + return remove_memory(start, size); + + if (!IS_ALIGNED(start, SUBSECTION_SIZE) || + !IS_ALIGNED(size, SUBSECTION_SIZE)) { + pr_err("%s: start 0x%llx size 0x%llx not aligned to subsection size\n", + __func__, start, size); + return -EINVAL; + } + + mem_hotplug_begin(); + + if (test_pages_isolated(start, start + size, MEMORY_OFFLINE)) { + pr_err("%s: [%llx, %llx) PFNs are not isolated\n", + __func__, start, start + size); + mem_hotplug_done(); + return -EBUSY; + } + + arch_remove_memory(start, size, NULL); + + if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) + memblock_remove(start, size); + + release_mem_region_adjustable(start, size); + + mem_hotplug_done(); + + return 0; +} +EXPORT_SYMBOL_GPL(remove_memory_subsection); + static int try_offline_memory_block(struct memory_block *mem, void *arg) { uint8_t online_type = MMOP_ONLINE_KERNEL;