[PATCH] arch: arm64: add early_param idle=<wfi|yield|nop>

Anshuman Khandual anshuman.khandual at arm.com
Sun Jul 5 22:23:54 PDT 2026



On 05/07/26 3:32 PM, Yureka Lilian wrote:
> Overriding the idle mechanism might be useful for debugging, performance
> testing and on platforms where the WFI instruction misbehaves, such as
> Apple Silicon SoCs.

The commit message should have little more description given the proposed
code churn here e.g 'idle=' is not new but an existing kernel command line
parameter already being used on x86 platform and describe different values
it takes on arm64 platform.
> 
> Signed-off-by: Yureka Lilian <yureka at cyberchaos.dev>
> ---
> Thanks to Will Daecon for suggesting this path, since, when treating the WFI
> misbehavior as an erratum, we had difficulties telling when the alternatives

This "why standard erratum method could not be used" should be mentioned along
with earlier discussion link in the commit message. Please add 'Suggested-by'.
> should be applied. This solution is more flexible and leaves it up to
> bootloader to add the appropriate idle= parameter as a workaround.

Please do mention this in commit message as well.

> --->  Documentation/admin-guide/kernel-parameters.txt | 23 ++++++++++++++++++
>  arch/arm64/kernel/idle.c                        | 31 +++++++++++++++++++++++--
>  arch/arm64/kernel/idle.h                        | 11 +++++++++
>  arch/arm64/lib/delay.c                          |  7 +++++-
>  4 files changed, 69 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index b2d7d3540ded..d7f5471edf8f 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2239,6 +2239,29 @@ Kernel parameters
>  
>  			idle=nomwait: Disable mwait for CPU C-states
>  
> +			[ARM64,EARLY]
> +			Format: idle=wfi, idle=yield, idle=nop
> +
> +			idle=wfi: Use the WFI (Wait For Interrupt) hint
> +			instruction in the idle loop. This is the default and
> +			allows the CPU to enter a low-power state until an
> +			interrupt arrives.
> +
> +			idle=yield: Use the YIELD hint instruction instead of
> +			WFI. CPUs supporting simultaneous multi-threading (SMT),
> +			can continue executing another thread when the current
> +			thread reaches the idle loop. This will make the CPUs
> +			eat more power, but may be useful to get slightly better
> +			performance in some applications, since the CPUs will
> +			not enter a low-power state.
> +
> +			idle=nop: Do not execute any idle instruction in the
> +			idle loop. This is useful on platforms where WFI
> +			misbehaves, leading to system instability or loss of CPU
> +			state. This will make the CPUs eat more power, but may
> +			give slightly better performance in some applications,
> +			since the CPUs will not enter a low-power state.
> +
>  	idxd.sva=	[HW]
>  			Format: <bool>
>  			Allow force disabling of Shared Virtual Memory (SVA)
> diff --git a/arch/arm64/kernel/idle.c b/arch/arm64/kernel/idle.c
> index 05cfb347ec26..018bcc812d45 100644
> --- a/arch/arm64/kernel/idle.c
> +++ b/arch/arm64/kernel/idle.c
> @@ -11,6 +11,28 @@
>  #include <asm/cpufeature.h>
>  #include <asm/sysreg.h>
>  
> +#include "idle.h"
> +
> +enum idle_mode idle = WFI;
> +> +/* User can over-ride above with "idle=<wfi|yield|nop>" in cmdline */

This comment is redundant as the usage in mentioned in Documentation.
> +static int __init setup_idle(char *s)
> +{
> +	if (!s)

Small nit - renaming 's' as 'arg' or 'str' might be better I guess.
> +		return -1;
> +	else if (!strcmp(s, "wfi"))
> +		idle = WFI;
> +	else if (!strcmp(s, "yield"))
> +		idle = YIELD;
> +	else if (!strcmp(s, "nop"))
> +		idle = NOP;
> +	else
> +		return -1;
> +
> +	return 0;
> +}
> +early_param("idle", setup_idle);

Small nit - s/setup_idle/setup_arm64_idle/

> +
>  /*
>   *	cpu_do_idle()
>   *
> @@ -26,8 +48,13 @@ void __cpuidle cpu_do_idle(void)
>  
>  	arm_cpuidle_save_irq_context(&context);
>  
> -	dsb(sy);
> -	wfi();
> +	if (likely(idle == WFI)) {
> +		dsb(sy);
> +		wfi();
> +	} else if (idle == YIELD) {
> +		dsb(sy);
> +		asm volatile("yield" ::: "memory");
> +	}
>  
>  	arm_cpuidle_restore_irq_context(&context);
>  }
> diff --git a/arch/arm64/kernel/idle.h b/arch/arm64/kernel/idle.h
> new file mode 100644
> index 000000000000..350b758ea215
> --- /dev/null
> +++ b/arch/arm64/kernel/idle.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef __ARM64_KERNEL_IDLE_H
> +#define __ARM64_KERNEL_IDLE_H
> +
> +enum idle_mode {

Small nit - s/idle_mode/arm64_idle_mode
> +	WFI,
> +	YIELD,
> +	NOP,

Small nit - ARM64_IDLE_[WFI|YIELD|NOP] to be more descriptive
> +};
> +
> +#endif
> diff --git a/arch/arm64/lib/delay.c b/arch/arm64/lib/delay.c
> index e278e060e78a..d6fd09466abc 100644
> --- a/arch/arm64/lib/delay.c
> +++ b/arch/arm64/lib/delay.c
> @@ -15,9 +15,13 @@
>  
>  #include <clocksource/arm_arch_timer.h>
>  
> +#include "../kernel/idle.h"
> +
>  #define USECS_TO_CYCLES(time_usecs)			\
>  	xloops_to_cycles((time_usecs) * 0x10C7UL)
>  
> +extern enum idle_mode idle;

Could the extern be moved inside arch/arm64/kernel/idle.h instead
> +
>  static inline unsigned long xloops_to_cycles(unsigned long xloops)
>  {
>  	return (xloops * loops_per_jiffy * HZ) >> 32;
> @@ -49,7 +53,8 @@ void __delay(unsigned long cycles)
>  		 * Start with WFIT. If an interrupt makes us resume
>  		 * early, use a WFET loop to complete the delay.
>  		 */
> -		wfit(end);
> +		if (likely(idle == WFI))
> +			wfit(end);
>  		while ((__delay_cycles() - start) < cycles)
>  			wfet(end);
>  	} else 	if (arch_timer_evtstrm_available()) {
> 
> ---
> base-commit: 96cb07bd64bf4d3c8c9159636314c6fbdd9b9881
> change-id: 20260705-arm64-idle-param-c27fc0e7ea05
> 
> Best regards,
> --  
> Yureka Lilian <yureka at cyberchaos.dev>
> 
> 




More information about the linux-arm-kernel mailing list