Device memory ranges when getting hot added into ZONE_DEVICE, might require
their vmemmap mapping's backing memory to be allocated from their own range
instead of consuming system memory. This prevents large system memory usage
for potentially large device memory ranges. Device driver communicates this
request via vmem_altmap structure. Architecture needs to take this request
into account while creating and tearing down vemmmap mappings.

This enables vmem_altmap support in vmemmap_populate() and vmemmap_free()
which includes vmemmap_populate_basepages() used for ARM64_16K_PAGES and
ARM64_64K_PAGES configs.

Cc: Catalin Marinas <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Steve Capper <[email protected]>
Cc: David Hildenbrand <[email protected]>
Cc: Yu Zhao <[email protected]>
Cc: Hsin-Yi Wang <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: [email protected]
Cc: [email protected]

Signed-off-by: Anshuman Khandual <[email protected]>
---
 arch/arm64/mm/mmu.c | 58 +++++++++++++++++++++++++++++++--------------
 1 file changed, 40 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 9e63828872cf..47c0a6325269 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -727,15 +727,30 @@ int kern_addr_valid(unsigned long addr)
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG
-static void free_hotplug_page_range(struct page *page, size_t size)
+static void free_hotplug_page_range(struct page *page, size_t size,
+                                   struct vmem_altmap *altmap)
 {
-       WARN_ON(PageReserved(page));
-       free_pages((unsigned long)page_address(page), get_order(size));
+       if (altmap) {
+               /*
+                * Though unmap_hotplug_range() will tear down altmap based
+                * vmemmap mappings at all page table levels, these mappings
+                * should only have been created either at PTE or PMD level
+                * with vmemmap_populate_basepages() or vmemmap_populate()
+                * respectively. Unmapping requests at any other level will
+                * be problematic. Drop these warnings when vmemmap mapping
+                * is supported at PUD level.
+                */
+               WARN_ON((size != PAGE_SIZE) && (size != PMD_SIZE));
+               vmem_altmap_free(altmap, size >> PAGE_SHIFT);
+       } else {
+               WARN_ON(PageReserved(page));
+               free_pages((unsigned long)page_address(page), get_order(size));
+       }
 }
 
 static void free_hotplug_pgtable_page(struct page *page)
 {
-       free_hotplug_page_range(page, PAGE_SIZE);
+       free_hotplug_page_range(page, PAGE_SIZE, NULL);
 }
 
 static bool pgtable_range_aligned(unsigned long start, unsigned long end,
@@ -758,7 +773,8 @@ static bool pgtable_range_aligned(unsigned long start, 
unsigned long end,
 }
 
 static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr,
-                                   unsigned long end, bool free_mapped)
+                                   unsigned long end, bool free_mapped,
+                                   struct vmem_altmap *altmap)
 {
        pte_t *ptep, pte;
 
@@ -772,12 +788,14 @@ static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned 
long addr,
                pte_clear(&init_mm, addr, ptep);
                flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
                if (free_mapped)
-                       free_hotplug_page_range(pte_page(pte), PAGE_SIZE);
+                       free_hotplug_page_range(pte_page(pte),
+                                               PAGE_SIZE, altmap);
        } while (addr += PAGE_SIZE, addr < end);
 }
 
 static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr,
-                                   unsigned long end, bool free_mapped)
+                                   unsigned long end, bool free_mapped,
+                                   struct vmem_altmap *altmap)
 {
        unsigned long next;
        pmd_t *pmdp, pmd;
@@ -800,16 +818,17 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned 
long addr,
                        flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
                        if (free_mapped)
                                free_hotplug_page_range(pmd_page(pmd),
-                                                       PMD_SIZE);
+                                                       PMD_SIZE, altmap);
                        continue;
                }
                WARN_ON(!pmd_table(pmd));
-               unmap_hotplug_pte_range(pmdp, addr, next, free_mapped);
+               unmap_hotplug_pte_range(pmdp, addr, next, free_mapped, altmap);
        } while (addr = next, addr < end);
 }
 
 static void unmap_hotplug_pud_range(pgd_t *pgdp, unsigned long addr,
-                                   unsigned long end, bool free_mapped)
+                                   unsigned long end, bool free_mapped,
+                                   struct vmem_altmap *altmap)
 {
        unsigned long next;
        pud_t *pudp, pud;
@@ -832,16 +851,16 @@ static void unmap_hotplug_pud_range(pgd_t *pgdp, unsigned 
long addr,
                        flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
                        if (free_mapped)
                                free_hotplug_page_range(pud_page(pud),
-                                                       PUD_SIZE);
+                                                       PUD_SIZE, altmap);
                        continue;
                }
                WARN_ON(!pud_table(pud));
-               unmap_hotplug_pmd_range(pudp, addr, next, free_mapped);
+               unmap_hotplug_pmd_range(pudp, addr, next, free_mapped, altmap);
        } while (addr = next, addr < end);
 }
 
 static void unmap_hotplug_range(unsigned long addr, unsigned long end,
-                               bool free_mapped)
+                               bool free_mapped, struct vmem_altmap *altmap)
 {
        unsigned long next;
        pgd_t *pgdp, pgd;
@@ -854,7 +873,7 @@ static void unmap_hotplug_range(unsigned long addr, 
unsigned long end,
                        continue;
 
                WARN_ON(!pgd_present(pgd));
-               unmap_hotplug_pud_range(pgdp, addr, next, free_mapped);
+               unmap_hotplug_pud_range(pgdp, addr, next, free_mapped, altmap);
        } while (addr = next, addr < end);
 }
 
@@ -999,7 +1018,7 @@ static void free_empty_tables(unsigned long addr, unsigned 
long end,
 int __meminit vmemmap_populate(unsigned long start, unsigned long end, int 
node,
                struct vmem_altmap *altmap)
 {
-       return vmemmap_populate_basepages(start, end, node, NULL);
+       return vmemmap_populate_basepages(start, end, node, altmap);
 }
 #else  /* !ARM64_SWAPPER_USES_SECTION_MAPS */
 int __meminit vmemmap_populate(unsigned long start, unsigned long end, int 
node,
@@ -1026,7 +1045,10 @@ int __meminit vmemmap_populate(unsigned long start, 
unsigned long end, int node,
                if (pmd_none(READ_ONCE(*pmdp))) {
                        void *p = NULL;
 
-                       p = vmemmap_alloc_block_buf(PMD_SIZE, node);
+                       if (altmap)
+                               p = altmap_alloc_block_buf(PMD_SIZE, altmap);
+                       else
+                               p = vmemmap_alloc_block_buf(PMD_SIZE, node);
                        if (!p)
                                return -ENOMEM;
 
@@ -1044,7 +1066,7 @@ void vmemmap_free(unsigned long start, unsigned long end,
 #ifdef CONFIG_MEMORY_HOTPLUG
        WARN_ON((start < VMEMMAP_START) || (end > VMEMMAP_END));
 
-       unmap_hotplug_range(start, end, true);
+       unmap_hotplug_range(start, end, true, altmap);
        free_empty_tables(start, end, VMEMMAP_START, VMEMMAP_END);
 #endif
 }
@@ -1332,7 +1354,7 @@ static void __remove_pgd_mapping(pgd_t *pgdir, unsigned 
long start, u64 size)
        WARN_ON(pgdir != init_mm.pgd);
        WARN_ON((start < PAGE_OFFSET) || (end > PAGE_END));
 
-       unmap_hotplug_range(start, end, false);
+       unmap_hotplug_range(start, end, false, NULL);
        free_empty_tables(start, end, PAGE_OFFSET, PAGE_END);
 }
 
-- 
2.20.1

Reply via email to