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

Yureka Lilian yureka at cyberchaos.dev
Sun Jul 5 03:02:36 PDT 2026


Overriding the idle mechanism might be useful for debugging, performance
testing and on platforms where the WFI instruction misbehaves, such as
Apple Silicon SoCs.

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
should be applied. This solution is more flexible and leaves it up to
bootloader to add the appropriate idle= parameter as a workaround.
---
 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 */
+static int __init setup_idle(char *s)
+{
+	if (!s)
+		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);
+
 /*
  *	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 {
+	WFI,
+	YIELD,
+	NOP,
+};
+
+#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;
+
 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