mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-10 19:35:21 +02:00
BACKPORT: timekeeping: Use READ/WRITE_ONCE() for tick_do_timer_cpu
tick_do_timer_cpu is used lockless to check which CPU needs to take care
of the per tick timekeeping duty. This is done to avoid a thundering
herd problem on jiffies_lock.
The read and writes are not annotated so KCSAN complains about data races:
BUG: KCSAN: data-race in tick_nohz_idle_stop_tick / tick_nohz_next_event
write to 0xffffffff8a2bda30 of 4 bytes by task 0 on cpu 26:
tick_nohz_idle_stop_tick+0x3b1/0x4a0
do_idle+0x1e3/0x250
read to 0xffffffff8a2bda30 of 4 bytes by task 0 on cpu 16:
tick_nohz_next_event+0xe7/0x1e0
tick_nohz_get_sleep_length+0xa7/0xe0
menu_select+0x82/0xb90
cpuidle_select+0x44/0x60
do_idle+0x1c2/0x250
value changed: 0x0000001a -> 0xffffffff
Annotate them with READ/WRITE_ONCE() to document the intentional data race.
Bug: 361037203
Reported-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Tested-by: Sean Anderson <sean.anderson@seco.com>
Link: https://lore.kernel.org/r/87cyqy7rt3.ffs@tglx
Change-Id: I622c0e57d0622cfd52308b26935f8579e195b428
(cherry picked from commit f87cbcb345
)
[quic_mojha: Resolved merge conflict in kernel/time/tick-sched.c, kernel/time/tick-common.c]
Signed-off-by: Mukesh Ojha <quic_mojha@quicinc.com>
This commit is contained in:
parent
4b38cee3d2
commit
a94ba5ab28
|
@ -85,7 +85,7 @@ int tick_is_oneshot_available(void)
|
|||
*/
|
||||
static void tick_periodic(int cpu)
|
||||
{
|
||||
if (tick_do_timer_cpu == cpu) {
|
||||
if (READ_ONCE(tick_do_timer_cpu) == cpu) {
|
||||
raw_spin_lock(&jiffies_lock);
|
||||
write_seqcount_begin(&jiffies_seq);
|
||||
|
||||
|
@ -219,8 +219,8 @@ static void tick_setup_device(struct tick_device *td,
|
|||
* If no cpu took the do_timer update, assign it to
|
||||
* this cpu:
|
||||
*/
|
||||
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
|
||||
tick_do_timer_cpu = cpu;
|
||||
if (READ_ONCE(tick_do_timer_cpu) == TICK_DO_TIMER_BOOT) {
|
||||
WRITE_ONCE(tick_do_timer_cpu, cpu);
|
||||
tick_next_period = ktime_get();
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
/*
|
||||
|
@ -236,7 +236,7 @@ static void tick_setup_device(struct tick_device *td,
|
|||
!tick_nohz_full_cpu(cpu)) {
|
||||
tick_take_do_timer_from_boot();
|
||||
tick_do_timer_boot_cpu = -1;
|
||||
WARN_ON(tick_do_timer_cpu != cpu);
|
||||
WARN_ON(READ_ONCE(tick_do_timer_cpu) != cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ static ktime_t tick_init_jiffy_update(void)
|
|||
|
||||
static void tick_sched_do_timer(struct tick_sched *ts, ktime_t now)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
int tick_cpu, cpu = smp_processor_id();
|
||||
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
/*
|
||||
|
@ -198,16 +198,18 @@ static void tick_sched_do_timer(struct tick_sched *ts, ktime_t now)
|
|||
* If nohz_full is enabled, this should not happen because the
|
||||
* tick_do_timer_cpu never relinquishes.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE)) {
|
||||
tick_cpu = READ_ONCE(tick_do_timer_cpu);
|
||||
if (unlikely(tick_cpu == TICK_DO_TIMER_NONE)) {
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
WARN_ON_ONCE(tick_nohz_full_running);
|
||||
#endif
|
||||
tick_do_timer_cpu = cpu;
|
||||
WRITE_ONCE(tick_do_timer_cpu, cpu);
|
||||
tick_cpu = cpu;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check, if the jiffies need an update */
|
||||
if (tick_do_timer_cpu == cpu) {
|
||||
if (tick_cpu == cpu) {
|
||||
tick_do_update_jiffies64(now);
|
||||
trace_android_vh_jiffies_update(NULL);
|
||||
}
|
||||
|
@ -553,7 +555,7 @@ bool tick_nohz_cpu_hotpluggable(unsigned int cpu)
|
|||
* timers, workqueues, timekeeping, ...) on behalf of full dynticks
|
||||
* CPUs. It must remain online when nohz full is enabled.
|
||||
*/
|
||||
if (tick_nohz_full_running && tick_do_timer_cpu == cpu)
|
||||
if (tick_nohz_full_running && READ_ONCE(tick_do_timer_cpu) == cpu)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -806,6 +808,7 @@ static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu)
|
|||
u64 basemono, next_tick, delta, expires;
|
||||
unsigned long basejiff;
|
||||
unsigned int seq;
|
||||
int tick_cpu;
|
||||
|
||||
/* Read jiffies and the time when jiffies were updated last */
|
||||
do {
|
||||
|
@ -868,8 +871,9 @@ static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu)
|
|||
* Otherwise we can sleep as long as we want.
|
||||
*/
|
||||
delta = timekeeping_max_deferment();
|
||||
if (cpu != tick_do_timer_cpu &&
|
||||
(tick_do_timer_cpu != TICK_DO_TIMER_NONE || !ts->do_timer_last))
|
||||
tick_cpu = READ_ONCE(tick_do_timer_cpu);
|
||||
if (tick_cpu != cpu &&
|
||||
(tick_cpu != TICK_DO_TIMER_NONE || !ts->do_timer_last))
|
||||
delta = KTIME_MAX;
|
||||
|
||||
/* Calculate the next expiry time */
|
||||
|
@ -890,6 +894,7 @@ static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
|
|||
u64 basemono = ts->timer_expires_base;
|
||||
u64 expires = ts->timer_expires;
|
||||
ktime_t tick = expires;
|
||||
int tick_cpu;
|
||||
|
||||
/* Make sure we won't be trying to stop it twice in a row. */
|
||||
ts->timer_expires_base = 0;
|
||||
|
@ -902,10 +907,11 @@ static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
|
|||
* do_timer() never invoked. Keep track of the fact that it
|
||||
* was the one which had the do_timer() duty last.
|
||||
*/
|
||||
if (cpu == tick_do_timer_cpu) {
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
tick_cpu = READ_ONCE(tick_do_timer_cpu);
|
||||
if (tick_cpu == cpu) {
|
||||
WRITE_ONCE(tick_do_timer_cpu, TICK_DO_TIMER_NONE);
|
||||
ts->do_timer_last = 1;
|
||||
} else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
|
||||
} else if (tick_cpu != TICK_DO_TIMER_NONE) {
|
||||
ts->do_timer_last = 0;
|
||||
}
|
||||
|
||||
|
@ -1068,8 +1074,10 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
|
|||
* invoked.
|
||||
*/
|
||||
if (unlikely(!cpu_online(cpu))) {
|
||||
if (cpu == tick_do_timer_cpu)
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
int tick_cpu = READ_ONCE(tick_do_timer_cpu);
|
||||
|
||||
if (tick_cpu == cpu)
|
||||
WRITE_ONCE(tick_do_timer_cpu, TICK_DO_TIMER_NONE);
|
||||
/*
|
||||
* Make sure the CPU doesn't get fooled by obsolete tick
|
||||
* deadline if it comes back online later.
|
||||
|
@ -1088,15 +1096,16 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
|
|||
return false;
|
||||
|
||||
if (tick_nohz_full_enabled()) {
|
||||
int tick_cpu = READ_ONCE(tick_do_timer_cpu);
|
||||
/*
|
||||
* Keep the tick alive to guarantee timekeeping progression
|
||||
* if there are full dynticks CPUs around
|
||||
*/
|
||||
if (tick_do_timer_cpu == cpu)
|
||||
if (tick_cpu == cpu)
|
||||
return false;
|
||||
|
||||
/* Should not happen for nohz-full */
|
||||
if (WARN_ON_ONCE(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
if (WARN_ON_ONCE(tick_cpu == TICK_DO_TIMER_NONE))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user