[RFC PATCH 5/6] arm64: execmem: enable EXECMEM_ROX_CACHE on supported CPUs
Ryan Roberts
ryan.roberts at arm.com
Fri Jun 19 05:09:23 PDT 2026
On 11/06/2026 14:01, Adrian Barnaś wrote:
> Enable EXECMEM_ROX_CACHE support for ARM64 systems that implement
> the bbml2_no_abort CPU feature.
>
> Using the ROX cache brings a performance boost by reducing linear region
> fragmentation caused by strict memory permissions (e.g., W^X enforcement).
> Grouping executable code (which is read-only in the linear region alias)
> into PMD-sized block mappings reduces TLB pressure and page table size.
Do you have any data on fragmentation reduction and/or performance improvement
in practice due to this change?
>
> This is only enabled on systems with bbml2_no_abort, as splitting
> these large blocks to make pages writable during module loading would
> otherwise risk triggering TLB Conflict Aborts.
> > Signed-off-by: Adrian Barnaś <abarnas at google.com>
> ---
> arch/arm64/Kconfig | 1 +
> arch/arm64/mm/init.c | 22 +++++++++++++++++++++-
> 2 files changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 38dba5f7e4d2..79c347ab841e 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -285,6 +285,7 @@ config ARM64
> select USER_STACKTRACE_SUPPORT
> select VDSO_GETRANDOM
> select VMAP_STACK
> + select ARCH_HAS_EXECMEM_ROX
nit: This list is sorted in alphabetical order; please maintain that ordering.
> help
> ARM 64-bit (AArch64) Linux support.
>
> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
> index 71aa745e0bef..8269d7747b84 100644
> --- a/arch/arm64/mm/init.c
> +++ b/arch/arm64/mm/init.c
> @@ -420,6 +420,12 @@ void execmem_fill_trapping_insns(void *ptr, size_t size)
>
> flush_icache_range((unsigned long)ptr, (unsigned long)ptr + size);
> }
> +
> +#define MODULE_TEXT_FLAG EXECMEM_ROX_CACHE
> +#define MODULE_TEXT_PGPROT PAGE_KERNEL_ROX
> +#else
> +#define MODULE_TEXT_FLAG (0)
> +#define MODULE_TEXT_PGPROT PAGE_KERNEL
> #endif
>
> static u64 module_direct_base __ro_after_init = 0;
> @@ -511,6 +517,8 @@ struct execmem_info __init *execmem_arch_setup(void)
> {
> unsigned long fallback_start = 0, fallback_end = 0;
> unsigned long start = 0, end = 0;
> + enum execmem_range_flags module_text_flags = 0;
> + pgprot_t module_text_pgprot = PAGE_KERNEL;
>
> module_init_limits();
>
> @@ -531,12 +539,24 @@ struct execmem_info __init *execmem_arch_setup(void)
> end = module_plt_base + SZ_2G;
> }
>
> + /*
> + * The ROX Cache requires bbml2_no_abort because it uses large block
> + * mappings. On systems without this guarantee, splitting these blocks
> + * to make pages writable for module loading can trigger TLB Conflict
> + * Aborts.
> + */
> + if (system_supports_bbml2_noabort()) {
> + module_text_flags = MODULE_TEXT_FLAG;
> + module_text_pgprot = MODULE_TEXT_PGPROT;
> + }
Perhaps this is a bit clearer? Then you don't need the MODULE_TEXT_* macros:
if (IS_ENABLED(CONFIG_ARCH_HAS_EXECMEM_ROX) &&
system_supports_bbml2_noabort()) {
module_text_flags = EXECMEM_ROX_CACHE;
module_text_pgprot = PAGE_KERNEL_ROX;
}
Thanks,
Ryan
> +
> execmem_info = (struct execmem_info){
> .ranges = {
> [EXECMEM_MODULE_TEXT] = {
> .start = start,
> .end = end,
> - .pgprot = PAGE_KERNEL,
> + .flags = module_text_flags,
> + .pgprot = module_text_pgprot,
> .alignment = 1,
> .fallback_start = fallback_start,
> .fallback_end = fallback_end,
More information about the linux-arm-kernel
mailing list