ANDROID: mm/memory_hotplug: implement {add/remove}_memory_subsection

Memory hotplugging is allowed only for multiples of section sizes.
Section size could be huge (ex. 1GB for arm64 targets) thus limiting
to add/remove lower chunks of memory. This patch allows drivers to add
memory of subsection sizes which are then added to memblock. This does
not create a separate memblock device nodes for newly added subsections
until the whole of the memblock section is added.

Bug: 170460867
Change-Id: I15749b5320340cba4d526e7ddb26a9cd6029c690
[quic_sudaraja: remove nid argument from remove_memory_subsection]
Signed-off-by: Sudarshan Rajagopalan <quic_sudaraja@quicinc.com>
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 9fd0be3..a23cb84 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1490,6 +1490,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, &params);
+	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
@@ -2253,6 +2293,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%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(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;