Skip to content

Commit 2be2a19

Browse files
KAGA-KOKObp3tk0v
authored andcommitted
sched/idle: Conditionally handle tick broadcast in default_idle_call()
The x86 architecture has an idle routine for AMD CPUs which are affected by erratum 400. On the affected CPUs the local APIC timer stops in the C1E halt state. It therefore requires tick broadcasting. The invocation of tick_broadcast_enter()/exit() from this function violates the RCU constraints because it can end up in lockdep or tracing, which rightfully triggers a warning. tick_broadcast_enter()/exit() must be invoked before ct_cpuidle_enter() and after ct_cpuidle_exit() in default_idle_call(). Add a static branch conditional invocation of tick_broadcast_enter()/exit() into this function to allow X86 to replace the AMD specific idle code. It's guarded by a config switch which will be selected by x86. Otherwise it's a NOOP. Reported-by: Borislav Petkov <bp@alien8.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/20240229142248.266708822@linutronix.de
1 parent 44c7682 commit 2be2a19

File tree

4 files changed

+31
-0
lines changed

4 files changed

+31
-0
lines changed

include/linux/cpu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ void arch_cpu_idle(void);
196196
void arch_cpu_idle_prepare(void);
197197
void arch_cpu_idle_enter(void);
198198
void arch_cpu_idle_exit(void);
199+
void arch_tick_broadcast_enter(void);
200+
void arch_tick_broadcast_exit(void);
199201
void __noreturn arch_cpu_idle_dead(void);
200202

201203
#ifdef CONFIG_ARCH_HAS_CPU_FINALIZE_INIT

include/linux/tick.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/cpumask.h>
1313
#include <linux/sched.h>
1414
#include <linux/rcupdate.h>
15+
#include <linux/static_key.h>
1516

1617
#ifdef CONFIG_GENERIC_CLOCKEVENTS
1718
extern void __init tick_init(void);
@@ -63,6 +64,8 @@ enum tick_broadcast_state {
6364
TICK_BROADCAST_ENTER,
6465
};
6566

67+
extern struct static_key_false arch_needs_tick_broadcast;
68+
6669
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
6770
extern void tick_broadcast_control(enum tick_broadcast_mode mode);
6871
#else

kernel/sched/idle.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,25 @@ void __weak arch_cpu_idle(void)
8181
cpu_idle_force_poll = 1;
8282
}
8383

84+
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST_IDLE
85+
DEFINE_STATIC_KEY_FALSE(arch_needs_tick_broadcast);
86+
87+
static inline void cond_tick_broadcast_enter(void)
88+
{
89+
if (static_branch_unlikely(&arch_needs_tick_broadcast))
90+
tick_broadcast_enter();
91+
}
92+
93+
static inline void cond_tick_broadcast_exit(void)
94+
{
95+
if (static_branch_unlikely(&arch_needs_tick_broadcast))
96+
tick_broadcast_exit();
97+
}
98+
#else
99+
static inline void cond_tick_broadcast_enter(void) { }
100+
static inline void cond_tick_broadcast_exit(void) { }
101+
#endif
102+
84103
/**
85104
* default_idle_call - Default CPU idle routine.
86105
*
@@ -90,6 +109,7 @@ void __cpuidle default_idle_call(void)
90109
{
91110
instrumentation_begin();
92111
if (!current_clr_polling_and_test()) {
112+
cond_tick_broadcast_enter();
93113
trace_cpu_idle(1, smp_processor_id());
94114
stop_critical_timings();
95115

@@ -99,6 +119,7 @@ void __cpuidle default_idle_call(void)
99119

100120
start_critical_timings();
101121
trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id());
122+
cond_tick_broadcast_exit();
102123
}
103124
local_irq_enable();
104125
instrumentation_end();

kernel/time/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ config GENERIC_CLOCKEVENTS_BROADCAST
3939
bool
4040
depends on GENERIC_CLOCKEVENTS
4141

42+
# Handle broadcast in default_idle_call()
43+
config GENERIC_CLOCKEVENTS_BROADCAST_IDLE
44+
bool
45+
depends on GENERIC_CLOCKEVENTS_BROADCAST
46+
4247
# Automatically adjust the min. reprogramming time for
4348
# clock event device
4449
config GENERIC_CLOCKEVENTS_MIN_ADJUST

0 commit comments

Comments
 (0)