powerpc/64s/radix/kfence: map __kfence_pool at page granularity

When KFENCE is enabled, total system memory is mapped at page level
granularity. But in radix MMU mode, ~3GB additional memory is needed
to map 100GB of system memory at page level granularity when compared
to using 2MB direct mapping.This is not desired considering KFENCE is
designed to be enabled in production kernels [1].

Mapping only the memory allocated for KFENCE pool at page granularity is
sufficient to enable KFENCE support. So, allocate __kfence_pool during
bootup and map it at page granularity instead of mapping all system
memory at page granularity.

Without patch:
  # cat /proc/meminfo
  MemTotal:       101201920 kB

With patch:
  # cat /proc/meminfo
  MemTotal:       104483904 kB

Note that enabling KFENCE at runtime is disabled for radix MMU for now,
as it depends on the ability to split page table mappings and such APIs
are not currently implemented for radix MMU.

All kfence_test.c testcases passed with this patch.

[1] https://lore.kernel.org/all/20201103175841.3495947-2-elver@google.com/

Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20240701130021.578240-1-hbathini@linux.ibm.com
This commit is contained in:
Hari Bathini 2024-07-01 18:30:21 +05:30 committed by Michael Ellerman
parent af199e6ca2
commit 353d7a84c2
3 changed files with 93 additions and 5 deletions

View File

@ -15,10 +15,19 @@
#define ARCH_FUNC_PREFIX "."
#endif
#ifdef CONFIG_KFENCE
extern bool kfence_disabled;
static inline void disable_kfence(void)
{
kfence_disabled = true;
}
static inline bool arch_kfence_init_pool(void)
{
return true;
return !kfence_disabled;
}
#endif
#ifdef CONFIG_PPC64
static inline bool kfence_protect_page(unsigned long addr, bool protect)

View File

@ -17,6 +17,7 @@
#include <linux/hugetlb.h>
#include <linux/string_helpers.h>
#include <linux/memory.h>
#include <linux/kfence.h>
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
@ -31,6 +32,7 @@
#include <asm/uaccess.h>
#include <asm/ultravisor.h>
#include <asm/set_memory.h>
#include <asm/kfence.h>
#include <trace/events/thp.h>
@ -293,7 +295,8 @@ static unsigned long next_boundary(unsigned long addr, unsigned long end)
static int __meminit create_physical_mapping(unsigned long start,
unsigned long end,
int nid, pgprot_t _prot)
int nid, pgprot_t _prot,
unsigned long mapping_sz_limit)
{
unsigned long vaddr, addr, mapping_size = 0;
bool prev_exec, exec = false;
@ -301,7 +304,10 @@ static int __meminit create_physical_mapping(unsigned long start,
int psize;
unsigned long max_mapping_size = memory_block_size;
if (debug_pagealloc_enabled_or_kfence())
if (mapping_sz_limit < max_mapping_size)
max_mapping_size = mapping_sz_limit;
if (debug_pagealloc_enabled())
max_mapping_size = PAGE_SIZE;
start = ALIGN(start, PAGE_SIZE);
@ -356,8 +362,74 @@ static int __meminit create_physical_mapping(unsigned long start,
return 0;
}
#ifdef CONFIG_KFENCE
static bool __ro_after_init kfence_early_init = !!CONFIG_KFENCE_SAMPLE_INTERVAL;
static int __init parse_kfence_early_init(char *arg)
{
int val;
if (get_option(&arg, &val))
kfence_early_init = !!val;
return 0;
}
early_param("kfence.sample_interval", parse_kfence_early_init);
static inline phys_addr_t alloc_kfence_pool(void)
{
phys_addr_t kfence_pool;
/*
* TODO: Support to enable KFENCE after bootup depends on the ability to
* split page table mappings. As such support is not currently
* implemented for radix pagetables, support enabling KFENCE
* only at system startup for now.
*
* After support for splitting mappings is available on radix,
* alloc_kfence_pool() & map_kfence_pool() can be dropped and
* mapping for __kfence_pool memory can be
* split during arch_kfence_init_pool().
*/
if (!kfence_early_init)
goto no_kfence;
kfence_pool = memblock_phys_alloc(KFENCE_POOL_SIZE, PAGE_SIZE);
if (!kfence_pool)
goto no_kfence;
memblock_mark_nomap(kfence_pool, KFENCE_POOL_SIZE);
return kfence_pool;
no_kfence:
disable_kfence();
return 0;
}
static inline void map_kfence_pool(phys_addr_t kfence_pool)
{
if (!kfence_pool)
return;
if (create_physical_mapping(kfence_pool, kfence_pool + KFENCE_POOL_SIZE,
-1, PAGE_KERNEL, PAGE_SIZE))
goto err;
memblock_clear_nomap(kfence_pool, KFENCE_POOL_SIZE);
__kfence_pool = __va(kfence_pool);
return;
err:
memblock_phys_free(kfence_pool, KFENCE_POOL_SIZE);
disable_kfence();
}
#else
static inline phys_addr_t alloc_kfence_pool(void) { return 0; }
static inline void map_kfence_pool(phys_addr_t kfence_pool) { }
#endif
static void __init radix_init_pgtable(void)
{
phys_addr_t kfence_pool;
unsigned long rts_field;
phys_addr_t start, end;
u64 i;
@ -365,6 +437,8 @@ static void __init radix_init_pgtable(void)
/* We don't support slb for radix */
slb_set_size(0);
kfence_pool = alloc_kfence_pool();
/*
* Create the linear mapping
*/
@ -381,9 +455,11 @@ static void __init radix_init_pgtable(void)
}
WARN_ON(create_physical_mapping(start, end,
-1, PAGE_KERNEL));
-1, PAGE_KERNEL, ~0UL));
}
map_kfence_pool(kfence_pool);
if (!cpu_has_feature(CPU_FTR_HVMODE) &&
cpu_has_feature(CPU_FTR_P9_RADIX_PREFETCH_BUG)) {
/*
@ -875,7 +951,7 @@ int __meminit radix__create_section_mapping(unsigned long start,
}
return create_physical_mapping(__pa(start), __pa(end),
nid, prot);
nid, prot, ~0UL);
}
int __meminit radix__remove_section_mapping(unsigned long start, unsigned long end)

View File

@ -31,6 +31,9 @@ EXPORT_SYMBOL_GPL(kernstart_virt_addr);
bool disable_kuep = !IS_ENABLED(CONFIG_PPC_KUEP);
bool disable_kuap = !IS_ENABLED(CONFIG_PPC_KUAP);
#ifdef CONFIG_KFENCE
bool __ro_after_init kfence_disabled;
#endif
static int __init parse_nosmep(char *p)
{