Commit 127fefa0 authored by Kefeng Wang's avatar Kefeng Wang Committed by Kaixiong Yu
Browse files

arm64: support page mapping percpu first chunk allocator

mainline inclusion
from mainline-v5.16-rc1
commit 09cea619
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IB2BDP
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=09cea6195073ee1d0f076d907d9249045757245d

--------------------------------

Percpu embedded first chunk allocator is the firstly option, but it
could fails on ARM64, eg,

  percpu: max_distance=0x5fcfdc640000 too large for vmalloc space 0x781fefff0000
  percpu: max_distance=0x600000540000 too large for vmalloc space 0x7dffb7ff0000
  percpu: max_distance=0x5fff9adb0000 too large for vmalloc space 0x5dffb7ff0000

then we could get

  WARNING: CPU: 15 PID: 461 at vmalloc.c:3087 pcpu_get_vm_areas+0x488/0x838

and the system could not boot successfully.

Let's implement page mapping percpu first chunk allocator as a fallback
to the embedding allocator to increase the robustness of the system.

Link: https://lkml.kernel.org/r/20210910053354.26721-3-wangkefeng.wang@huawei.com


Signed-off-by: default avatarKefeng Wang <wangkefeng.wang@huawei.com>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Marco Elver <elver@google.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Conflicts:
        arch/arm64/mm/numa.c
[OLK-5.10 don't merge linux master inclusion commit
ae3c107c("numa: Move numa
implementation to common code"), so drivers/base/arch_numa.c don't
exist. Move pcpu_populate_pte() and modification of setup_per_cpu_areas()
to arch/arm64/mm/numa.c. Besides, Commit 09cea619("arm64: support page
mapping percpu first chunk allocator") from mainline leads to ABI breakage.
Fix it by moving the "#include <asm/pgalloc.h>" statement after the "#ifdef
CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK".]
Signed-off-by: default avatarKaixiong Yu <yukaixiong@huawei.com>
parent 0037ab4b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -1196,6 +1196,10 @@ config NEED_PER_CPU_EMBED_FIRST_CHUNK
	def_bool y
	depends on NUMA

config NEED_PER_CPU_PAGE_FIRST_CHUNK
       def_bool y
       depends on NUMA

source "kernel/Kconfig.hz"

config ARCH_SUPPORTS_DEBUG_PAGEALLOC
+73 −11
Original line number Diff line number Diff line
@@ -342,12 +342,61 @@ static void __init pcpu_fc_free(void *ptr, size_t size)
	memblock_free_early(__pa(ptr), size);
}

#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
#include <asm/pgalloc.h>

static void __init pcpu_populate_pte(unsigned long addr)
{
	pgd_t *pgd = pgd_offset_k(addr);
	p4d_t *p4d;
	pud_t *pud;
	pmd_t *pmd;

	p4d = p4d_offset(pgd, addr);
	if (p4d_none(*p4d)) {
		pud_t *new;

		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
		if (!new)
			goto err_alloc;
		p4d_populate(&init_mm, p4d, new);
	}

	pud = pud_offset(p4d, addr);
	if (pud_none(*pud)) {
		pmd_t *new;

		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
		if (!new)
			goto err_alloc;
		pud_populate(&init_mm, pud, new);
	}

	pmd = pmd_offset(pud, addr);
	if (!pmd_present(*pmd)) {
		pte_t *new;

		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
		if (!new)
			goto err_alloc;
		pmd_populate_kernel(&init_mm, pmd, new);
	}

	return;

err_alloc:
	panic("%s: Failed to allocate %lu bytes align=%lx from=%lx\n",
		__func__, PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
}
#endif

void __init setup_per_cpu_areas(void)
{
	unsigned long delta;
	unsigned int cpu;
	int rc;
	int rc = -EINVAL;

	if (pcpu_chosen_fc != PCPU_FC_PAGE) {
		/*
		 * Always reserve area for module percpu variables.  That's
		 * what the legacy allocator did.
@@ -356,9 +405,22 @@ void __init setup_per_cpu_areas(void)
						PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
						pcpu_cpu_distance,
						pcpu_fc_alloc, pcpu_fc_free);
#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
	if (rc < 0)
		panic("Failed to initialize percpu areas.");
		pr_warn("PERCPU: %s allocator failed (%d), falling back to page size\n",
			pcpu_fc_names[pcpu_chosen_fc], rc);
#endif
	}

#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
	if (rc < 0)
		rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE,
						pcpu_fc_alloc,
						pcpu_fc_free,
						pcpu_populate_pte);
#endif
	if (rc < 0)
		panic("Failed to initialize percpu areas (err=%d).", rc);
	delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
	for_each_possible_cpu(cpu)
		__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];