From 1b4aca7d82ae9b40145484fa09ceab38a6a06062 Mon Sep 17 00:00:00 2001 From: Sudarshan Rajagopalan Date: Wed, 6 Jan 2021 18:53:58 -0800 Subject: [PATCH] ANDROID: arm64/mm: implement {populate/depopulate}_range_driver_managed After certain memory blocks are offlined, some usecases require that the page-table mappings are removed while still keeping the memblock device nodes, memory resources and the memmap entries intact. This is to avoid the overhead involved in using 'remove_memory' if the offlined blocks will be added/onlined back into the system at later point. {populate/depopulate}_range_driver_managed provide the abilty to drivers to tear-down and create page-table mappings of memory blocks that its managing, without having to use 'remove_memory' and friends for the purpose. These functions does not interfere with mappings of boot memory or resources that isn't owned by the driver. Bug: 171907330 Change-Id: Ie11201334bd7438bf87f933ccc81814fa99162c4 Signed-off-by: Sudarshan Rajagopalan --- arch/arm64/include/asm/pgtable.h | 7 ++++ arch/arm64/mm/mmu.c | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 5628289b9d5e..507812ebc910 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -530,6 +530,13 @@ extern pgd_t tramp_pg_dir[PTRS_PER_PGD]; extern void set_swapper_pgd(pgd_t *pgdp, pgd_t pgd); +#ifdef CONFIG_MEMORY_HOTPLUG +extern int populate_range_driver_managed(u64 start, u64 size, + const char *resource_name); +extern int depopulate_range_driver_managed(u64 start, u64 size, + const char *resource_name); +#endif + static inline bool in_swapper_pgdir(void *addr) { return ((unsigned long)addr & PAGE_MASK) == diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 567021f15e7d..66f3367dc0a3 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1496,6 +1496,71 @@ void arch_remove_memory(int nid, u64 start, u64 size, __remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size); } +int check_range_driver_managed(u64 start, u64 size, const char *resource_name) +{ + struct mem_section *ms; + unsigned long pfn = __phys_to_pfn(start); + unsigned long end_pfn = __phys_to_pfn(start + size); + struct resource *res; + unsigned long flags; + + res = lookup_resource(&iomem_resource, start); + if (!res) { + pr_err("%s: couldn't find memory resource for start 0x%lx\n", + __func__, start); + return -EINVAL; + } + + flags = res->flags; + + if (!(flags & IORESOURCE_SYSRAM_DRIVER_MANAGED) || + strstr(resource_name, "System RAM (") != resource_name) + return -EINVAL; + + for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) { + ms = __pfn_to_section(pfn); + if (early_section(ms)) + return -EINVAL; + } + + return 0; +} + +int populate_range_driver_managed(u64 start, u64 size, + const char *resource_name) +{ + unsigned long virt = (unsigned long)phys_to_virt(start); + int flags = 0; + + if (check_range_driver_managed(start, size, resource_name)) + return -EINVAL; + + /* + * When rodata_full is enabled, memory is mapped at page size granule, + * as opposed to block mapping. + */ + if (rodata_full || debug_pagealloc_enabled()) + flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; + + __create_pgd_mapping(init_mm.pgd, start, virt, size, + PAGE_KERNEL, NULL, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(populate_range_driver_managed); + +int depopulate_range_driver_managed(u64 start, u64 size, + const char *resource_name) +{ + if (check_range_driver_managed(start, size, resource_name)) + return -EINVAL; + + unmap_hotplug_range(start, start + size, false, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(depopulate_range_driver_managed); + /* * This memory hotplug notifier helps prevent boot memory from being * inadvertently removed as it blocks pfn range offlining process in