diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 551093b74596..cf93527ac9eb 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -314,6 +314,7 @@ static inline void pgdat_resize_init(struct pglist_data *pgdat) {} extern void try_offline_node(int nid); extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); extern int remove_memory(int nid, u64 start, u64 size); +extern int remove_memory_subsection(int nid, u64 start, u64 size); extern void __remove_memory(int nid, u64 start, u64 size); extern int offline_and_remove_memory(int nid, u64 start, u64 size); @@ -340,6 +341,7 @@ extern void clear_zone_contiguous(struct zone *zone); extern void __ref free_area_init_core_hotplug(int nid); 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 aa453a433143..0c1629cce5a6 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1131,6 +1131,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%lx size 0x%lx 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); + + 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%lx size 0x%lx\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 @@ -1788,6 +1828,40 @@ int remove_memory(int nid, u64 start, u64 size) } EXPORT_SYMBOL_GPL(remove_memory); +int remove_memory_subsection(int nid, u64 start, u64 size) +{ + if (size == memory_block_size_bytes()) + return remove_memory(nid, start, size); + + if (!IS_ALIGNED(start, SUBSECTION_SIZE) || + !IS_ALIGNED(size, SUBSECTION_SIZE)) { + pr_err("%s: start 0x%lx size 0x%lx 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: [%lx, %lx) PFNs are not isolated\n", + __func__, start, start + size); + mem_hotplug_done(); + return -EBUSY; + } + + arch_remove_memory(nid, 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); + /* * Try to offline and remove a memory block. Might take a long time to * finish in case memory is still in use. Primarily useful for memory devices