[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