Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arch/idle: Change arch_cpu_idle() behavior: always exit with IRQs disabled

Current arch_cpu_idle() is called with IRQs disabled, but will return
with IRQs enabled.

However, the very first thing the generic code does after calling
arch_cpu_idle() is raw_local_irq_disable(). This means that
architectures that can idle with IRQs disabled end up doing a
pointless 'enable-disable' dance.

Therefore, push this IRQ disabling into the idle function, meaning
that those architectures can avoid the pointless IRQ state flipping.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Tested-by: Tony Lindgren <tony@atomide.com>
Tested-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
Acked-by: Mark Rutland <mark.rutland@arm.com> [arm64]
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Guo Ren <guoren@kernel.org>
Acked-by: Frederic Weisbecker <frederic@kernel.org>
Link: https://lore.kernel.org/r/20230112195540.618076436@infradead.org

authored by

Peter Zijlstra and committed by
Ingo Molnar
89b30987 9b461a6f

+29 -37
-1
arch/alpha/kernel/process.c
··· 57 57 void arch_cpu_idle(void) 58 58 { 59 59 wtint(0); 60 - raw_local_irq_enable(); 61 60 } 62 61 63 62 void arch_cpu_idle_dead(void)
+3
arch/arc/kernel/process.c
··· 114 114 "sleep %0 \n" 115 115 : 116 116 :"I"(arg)); /* can't be "r" has to be embedded const */ 117 + 118 + raw_local_irq_disable(); 117 119 } 118 120 119 121 #else /* ARC700 */ ··· 124 122 { 125 123 /* sleep, but enable both set E1/E2 (levels of interrupts) before committing */ 126 124 __asm__ __volatile__("sleep 0x3 \n"); 125 + raw_local_irq_disable(); 127 126 } 128 127 129 128 #endif
-1
arch/arm/kernel/process.c
··· 78 78 arm_pm_idle(); 79 79 else 80 80 cpu_do_idle(); 81 - raw_local_irq_enable(); 82 81 } 83 82 84 83 void arch_cpu_idle_prepare(void)
+2 -1
arch/arm/mach-gemini/board-dt.c
··· 42 42 */ 43 43 44 44 /* FIXME: Enabling interrupts here is racy! */ 45 - local_irq_enable(); 45 + raw_local_irq_enable(); 46 46 cpu_do_idle(); 47 + raw_local_irq_disable(); 47 48 } 48 49 49 50 static void __init gemini_init_machine(void)
-1
arch/arm64/kernel/idle.c
··· 42 42 * tricks 43 43 */ 44 44 cpu_do_idle(); 45 - raw_local_irq_enable(); 46 45 }
-1
arch/csky/kernel/process.c
··· 100 100 #ifdef CONFIG_CPU_PM_STOP 101 101 asm volatile("stop\n"); 102 102 #endif 103 - raw_local_irq_enable(); 104 103 } 105 104 #endif
+1 -1
arch/csky/kernel/smp.c
··· 309 309 while (!secondary_stack) 310 310 arch_cpu_idle(); 311 311 312 - local_irq_disable(); 312 + raw_local_irq_disable(); 313 313 314 314 asm volatile( 315 315 "mov sp, %0\n"
-1
arch/hexagon/kernel/process.c
··· 44 44 { 45 45 __vmwait(); 46 46 /* interrupts wake us up, but irqs are still disabled */ 47 - raw_local_irq_enable(); 48 47 } 49 48 50 49 /*
+1
arch/ia64/kernel/process.c
··· 242 242 (*mark_idle)(1); 243 243 244 244 raw_safe_halt(); 245 + raw_local_irq_disable(); 245 246 246 247 if (mark_idle) 247 248 (*mark_idle)(0);
+1
arch/loongarch/kernel/idle.c
··· 13 13 { 14 14 raw_local_irq_enable(); 15 15 __arch_cpu_idle(); /* idle instruction needs irq enabled */ 16 + raw_local_irq_disable(); 16 17 }
-1
arch/microblaze/kernel/process.c
··· 140 140 141 141 void arch_cpu_idle(void) 142 142 { 143 - raw_local_irq_enable(); 144 143 }
+3 -5
arch/mips/kernel/idle.c
··· 33 33 { 34 34 unsigned long cfg = read_c0_conf(); 35 35 write_c0_conf(cfg | R30XX_CONF_HALT); 36 - raw_local_irq_enable(); 37 36 } 38 37 39 38 void __cpuidle r4k_wait(void) 40 39 { 41 40 raw_local_irq_enable(); 42 41 __r4k_wait(); 42 + raw_local_irq_disable(); 43 43 } 44 44 45 45 /* ··· 57 57 " .set arch=r4000 \n" 58 58 " wait \n" 59 59 " .set pop \n"); 60 - raw_local_irq_enable(); 61 60 } 62 61 63 62 /* ··· 76 77 " wait \n" 77 78 " mtc0 $1, $12 # stalls until W stage \n" 78 79 " .set pop \n"); 79 - raw_local_irq_enable(); 80 80 } 81 81 82 82 /* ··· 101 103 " nop \n" 102 104 " .set pop \n" 103 105 : : "r" (au1k_wait), "r" (c0status)); 106 + 107 + raw_local_irq_disable(); 104 108 } 105 109 106 110 static int __initdata nowait; ··· 245 245 { 246 246 if (cpu_wait) 247 247 cpu_wait(); 248 - else 249 - raw_local_irq_enable(); 250 248 } 251 249 252 250 #ifdef CONFIG_CPU_IDLE
-1
arch/nios2/kernel/process.c
··· 33 33 34 34 void arch_cpu_idle(void) 35 35 { 36 - raw_local_irq_enable(); 37 36 } 38 37 39 38 /*
+1
arch/openrisc/kernel/process.c
··· 102 102 raw_local_irq_enable(); 103 103 if (mfspr(SPR_UPR) & SPR_UPR_PMP) 104 104 mtspr(SPR_PMR, mfspr(SPR_PMR) | SPR_PMR_DME); 105 + raw_local_irq_disable(); 105 106 } 106 107 107 108 void (*pm_power_off)(void) = NULL;
-2
arch/parisc/kernel/process.c
··· 183 183 184 184 void __cpuidle arch_cpu_idle(void) 185 185 { 186 - raw_local_irq_enable(); 187 - 188 186 /* nop on real hardware, qemu will idle sleep. */ 189 187 asm volatile("or %%r10,%%r10,%%r10\n":::); 190 188 }
+2 -3
arch/powerpc/kernel/idle.c
··· 51 51 * Some power_save functions return with 52 52 * interrupts enabled, some don't. 53 53 */ 54 - if (irqs_disabled()) 55 - raw_local_irq_enable(); 54 + if (!irqs_disabled()) 55 + raw_local_irq_disable(); 56 56 } else { 57 - raw_local_irq_enable(); 58 57 /* 59 58 * Go into low thread priority and possibly 60 59 * low power mode.
-1
arch/riscv/kernel/process.c
··· 39 39 void arch_cpu_idle(void) 40 40 { 41 41 cpu_do_idle(); 42 - raw_local_irq_enable(); 43 42 } 44 43 45 44 void __show_regs(struct pt_regs *regs)
-1
arch/s390/kernel/idle.c
··· 66 66 idle->idle_count++; 67 67 account_idle_time(cputime_to_nsecs(idle_time)); 68 68 raw_write_seqcount_end(&idle->seqcount); 69 - raw_local_irq_enable(); 70 69 } 71 70 72 71 static ssize_t show_idle_count(struct device *dev,
+1
arch/sh/kernel/idle.c
··· 25 25 raw_local_irq_enable(); 26 26 /* Isn't this racy ? */ 27 27 cpu_sleep(); 28 + raw_local_irq_disable(); 28 29 clear_bl_bit(); 29 30 } 30 31
+4
arch/sparc/kernel/leon_pmc.c
··· 57 57 "lda [%0] %1, %%g0\n" 58 58 : 59 59 : "r"(address), "i"(ASI_LEON_BYPASS)); 60 + 61 + raw_local_irq_disable(); 60 62 } 61 63 62 64 /* ··· 72 70 73 71 /* For systems without power-down, this will be no-op */ 74 72 __asm__ __volatile__ ("wr %g0, %asr19\n\t"); 73 + 74 + raw_local_irq_disable(); 75 75 } 76 76 77 77 /* Install LEON Power Down function */
-1
arch/sparc/kernel/process_32.c
··· 71 71 { 72 72 if (sparc_idle) 73 73 (*sparc_idle)(); 74 - raw_local_irq_enable(); 75 74 } 76 75 77 76 /* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
+2 -1
arch/sparc/kernel/process_64.c
··· 59 59 { 60 60 if (tlb_type != hypervisor) { 61 61 touch_nmi_watchdog(); 62 - raw_local_irq_enable(); 63 62 } else { 64 63 unsigned long pstate; 65 64 ··· 89 90 "wrpr %0, %%g0, %%pstate" 90 91 : "=&r" (pstate) 91 92 : "i" (PSTATE_IE)); 93 + 94 + raw_local_irq_disable(); 92 95 } 93 96 } 94 97
-1
arch/um/kernel/process.c
··· 218 218 { 219 219 cpu_tasks[current_thread_info()->cpu].pid = os_getpid(); 220 220 um_idle_sleep(); 221 - raw_local_irq_enable(); 222 221 } 223 222 224 223 int __cant_sleep(void) {
+3
arch/x86/coco/tdx/tdx.c
··· 274 274 */ 275 275 if (__halt(irq_disabled, do_sti)) 276 276 WARN_ONCE(1, "HLT instruction emulation failed\n"); 277 + 278 + /* XXX I can't make sense of what @do_sti actually does */ 279 + raw_local_irq_disable(); 277 280 } 278 281 279 282 static int read_msr(struct pt_regs *regs, struct ve_info *ve)
+4 -11
arch/x86/kernel/process.c
··· 701 701 void __cpuidle default_idle(void) 702 702 { 703 703 raw_safe_halt(); 704 + raw_local_irq_disable(); 704 705 } 705 706 #if defined(CONFIG_APM_MODULE) || defined(CONFIG_HALTPOLL_CPUIDLE_MODULE) 706 707 EXPORT_SYMBOL(default_idle); ··· 807 806 808 807 default_idle(); 809 808 810 - /* 811 - * The switch back from broadcast mode needs to be called with 812 - * interrupts disabled. 813 - */ 814 - raw_local_irq_disable(); 815 809 tick_broadcast_exit(); 816 - raw_local_irq_enable(); 817 810 } 818 811 819 812 /* ··· 865 870 } 866 871 867 872 __monitor((void *)&current_thread_info()->flags, 0, 0); 868 - if (!need_resched()) 873 + if (!need_resched()) { 869 874 __sti_mwait(0, 0); 870 - else 871 - raw_local_irq_enable(); 872 - } else { 873 - raw_local_irq_enable(); 875 + raw_local_irq_disable(); 876 + } 874 877 } 875 878 __current_clr_polling(); 876 879 }
+1
arch/xtensa/kernel/process.c
··· 183 183 void arch_cpu_idle(void) 184 184 { 185 185 platform_idle(); 186 + raw_local_irq_disable(); 186 187 } 187 188 188 189 /*
-2
kernel/sched/idle.c
··· 79 79 void __weak arch_cpu_idle(void) 80 80 { 81 81 cpu_idle_force_poll = 1; 82 - raw_local_irq_enable(); 83 82 } 84 83 85 84 /** ··· 95 96 96 97 ct_cpuidle_enter(); 97 98 arch_cpu_idle(); 98 - raw_local_irq_disable(); 99 99 ct_cpuidle_exit(); 100 100 101 101 start_critical_timings();