Commit d5e88d32 authored by Sumanth Korikkar's avatar Sumanth Korikkar Committed by Heiko Carstens
Browse files

s390/mm: Support removal of boot-allocated virtual memory map



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 645d5ce2 ("powerpc/mm/radix: Fix PTE/PMD
fragment count for early page table mappings")

Reviewed-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarSumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 3a866087
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
}

+12 −9
Original line number Diff line number Diff line
@@ -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,16 +40,22 @@ 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;
	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);
}