]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
s390/mm: Support removal of boot-allocated virtual memory map
authorSumanth Korikkar <sumanthk@linux.ibm.com>
Fri, 10 Oct 2025 08:51:44 +0000 (10:51 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Tue, 14 Oct 2025 12:24:53 +0000 (14:24 +0200)
On s390, memory blocks are not currently removed via
arch_remove_memory(). With upcoming dynamic memory (de)configuration
support, runtime removal of memory blocks is possible. This internally
involves tearing down identity mapping, virtual memory mappings and
freeing the physical memory backing the struct pages metadata.

During early boot, physical memory used to back the struct pages
metadata in vmemmap is allocated through:

setup_arch()
  -> sparse_init()
    -> sparse_init_nid()
      -> __populate_section_memmap()
        -> vmemmap_alloc_block_buf()
          -> sparse_buffer_alloc()
            -> memblock_alloc()

Here, sparse_init_nid() sets up virtual-to-physical mapping for struct
pages backed by memblock_alloc(). This differs from runtime addition of
hotplug memory which uses the buddy allocator later.

To correctly free identity mappings, vmemmap mappings during hot-remove,
boot-time and runtime allocations must be distinguished using the
PageReserved bit:

* Boot-time memory, such as identity-mapped page tables allocated via
  boot_crst_alloc() and reserved via reserve_pgtables() is marked
  PageReserved in memmap_init_reserved_pages().

* Physical memory backing vmemmap (struct pages from memblock_alloc())
  is also marked PageReserved similarly.

During teardown, PageReserved bit is checked to distinguish between
boot-time allocation or buddy allocation.

This is similar to commit 645d5ce2f7d6 ("powerpc/mm/radix: Fix PTE/PMD
fragment count for early page table mappings")

Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/mm/pgalloc.c
arch/s390/mm/vmem.c

index 626fca116cd71d12e670f2a0b3c77a8477e1c5e6..7df23528c01bd4bcb5ce71a3c2b8d2d33627a5d6 100644 (file)
@@ -164,6 +164,8 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
 {
        struct ptdesc *ptdesc = virt_to_ptdesc(table);
 
+       if (pagetable_is_reserved(ptdesc))
+               return free_reserved_ptdesc(ptdesc);
        pagetable_dtor_free(ptdesc);
 }
 
index f48ef361bc8315b27efe9bb3b617f55581fb8148..d96587b84e81f54e22ebba191db280f6a2142996 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/memory_hotplug.h>
+#include <linux/bootmem_info.h>
 #include <linux/cpufeature.h>
 #include <linux/memblock.h>
 #include <linux/pfn.h>
@@ -39,15 +40,21 @@ static void __ref *vmem_alloc_pages(unsigned int order)
 
 static void vmem_free_pages(unsigned long addr, int order, struct vmem_altmap *altmap)
 {
+       unsigned int nr_pages = 1 << order;
+       struct page *page;
+
        if (altmap) {
                vmem_altmap_free(altmap, 1 << order);
                return;
        }
-       /* We don't expect boot memory to be removed ever. */
-       if (!slab_is_available() ||
-           WARN_ON_ONCE(PageReserved(virt_to_page((void *)addr))))
-               return;
-       free_pages(addr, order);
+       page = virt_to_page((void *)addr);
+       if (PageReserved(page)) {
+               /* allocated from memblock */
+               while (nr_pages--)
+                       free_bootmem_page(page++);
+       } else {
+               free_pages(addr, order);
+       }
 }
 
 void *vmem_crst_alloc(unsigned long val)
@@ -79,10 +86,6 @@ pte_t __ref *vmem_pte_alloc(void)
 
 static void vmem_pte_free(unsigned long *table)
 {
-       /* We don't expect boot memory to be removed ever. */
-       if (!slab_is_available() ||
-           WARN_ON_ONCE(PageReserved(virt_to_page(table))))
-               return;
        page_table_free(&init_mm, table);
 }