summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-21 11:20:38 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-21 11:20:38 -1000
commitaaf3d47e530616ed0cf9110ba8c537ace455dd90 (patch)
treed1ef2d72acb7f64963c7051ad8540dccd5ee11e7 /kernel
parentb4460f89a274240204038d559e8571f370d14327 (diff)
parent348f54c435bf3177baa78201952810b255d44b30 (diff)
Merge tag 'timers-core-2026-10-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer core updates from Thomas Gleixner: "Updates for the core timekeeping and timers subsystem: - Futher cleanups for the POSIX timer exit/exec related code to simplify the code flow and to make it more robust against future changes. - Prevent tearing of timekeeper::ktime_sec which can be accessed without the sequence count protection. The timekeeper is updated by memcpy() with an uneven sequence count to force readers into the retry path. On 64-bit machine the ktime_sec member can be accessed atomically without the sequence count protection. That is only correct when the update side memcpy() guarantees atomicity at the 64-bit granularity. That's not guaranteed due to debug options, compilers and instruction availability. If the memcpy() does not provide the atomicity then the unprotected readers can observe store tearing. To prevent this write the ktime_sec member before the memcpy() so that a non-atomic memcpy() will write the same data. A similar fix has been done for the xtime_sec member, but the ktime_sec equivalent was missed. - Prevent time64_to_tm() day truncation on 32-bit kernels The function receives a 64-bit seconds value but stores the quotient of the computation in a long, which truncates the result for large input values. Cure this by using div_s64_rem() which stores the quotient in s64 and only leaves the seconds-within-day reminder in a long, which is guaranteed to fit. This fix comes with a corresponding kunit test to prevent further mishaps. - Mark data-racy accesses to the hrtimer_sleeper::task member. The recent updates to KCSAN flagged these benign races. Annotate them with new helper functions, convert all existing usage sites and mark the task member private after that so that future users which fail to use the helpers can be detected automatically. - Address similar benign KCSAN flagged issues in the hrtimer, timer and timer migration code. - Fixes for a few selftest issues" * tag 'timers-core-2026-10-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (26 commits) selftests/timers: clocksource-switch: Fix unchecked open()/read() selftests: timers: Measure the CPU timers on the clock they count selftests: timers: Count what tick is worth in the drift estimate time/kunit: Add time64_to_tm() case beyond 32-bit day count time: Prevent time64_to_tm() day truncation on 32-bit timekeeping: Use READ_ONCE/WRITE_ONCE() for ktime_sec to prevent tearing timers/migration: Mark racy updates to tmigr_event::ignore field timers: Mark racy updates to hlist_node::pprev field hrtimer: Apply READ_ONCE() to lockless base->running loads hrtimer: Mark the hrtimer_sleeper structure's ->task field __private hrtimer: Update hrtimer_resolution only if value changes futex: Use accessor for hrtimer_sleeper ->task field in requeue rtmutex: Use accessor for hrtimer_sleeper ->task field net: pktgen: Use accessor for hrtimer_sleeper ->task field timers: Use accessor for hrtimer_sleeper ->task field in sleep_timeout.c futex: Use accessor for hrtimer_sleeper ->task field in waitwake.c io-uring/rw: Use accessor for hrtimer_sleeper ->task field wait: Use accessor for hrtimer_sleeper ->task field aio: Use accessor for hrtimer_sleeper ->task field hrtimer: Mark data-racy accesses to hrtimer_sleeper ->task field ...
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c13
-rw-r--r--kernel/futex/requeue.c2
-rw-r--r--kernel/futex/waitwake.c8
-rw-r--r--kernel/locking/rtmutex.c2
-rw-r--r--kernel/time/hrtimer.c23
-rw-r--r--kernel/time/posix-cpu-timers.c103
-rw-r--r--kernel/time/posix-timers.c26
-rw-r--r--kernel/time/posix-timers.h3
-rw-r--r--kernel/time/sleep_timeout.c4
-rw-r--r--kernel/time/time_test.c16
-rw-r--r--kernel/time/timeconv.c6
-rw-r--r--kernel/time/timekeeping.c4
-rw-r--r--kernel/time/timer.c2
-rw-r--r--kernel/time/timer_migration.c6
14 files changed, 168 insertions, 50 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index 65ec0bd0b4f5..d5e7bc2af804 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -168,12 +168,6 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts
lockdep_tasklist_lock_is_held());
spin_lock(&sighand->siglock);
-#ifdef CONFIG_POSIX_TIMERS
- posix_cpu_timers_exit(tsk);
- if (group_dead)
- posix_cpu_timers_exit_group(tsk);
-#endif
-
if (group_dead) {
tty = sig->tty;
sig->tty = NULL;
@@ -940,13 +934,12 @@ void __noreturn do_exit(long code)
panic("Attempted to kill init! exitcode=0x%08x\n",
tsk->signal->group_exit_code ?: (int)code);
-#ifdef CONFIG_POSIX_TIMERS
- hrtimer_cancel(&tsk->signal->real_timer);
- exit_itimers(tsk);
-#endif
if (tsk->mm)
setmax_mm_hiwater_rss(&tsk->signal->maxrss, tsk->mm);
}
+
+ posixtimer_exit(group_dead);
+
acct_collect(code, group_dead);
if (group_dead)
tty_audit_exit();
diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c
index b3f4a4bccb12..842d852302dd 100644
--- a/kernel/futex/requeue.c
+++ b/kernel/futex/requeue.c
@@ -744,7 +744,7 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
/* Handle spurious wakeups gracefully */
ret = -EWOULDBLOCK;
- if (timeout && !timeout->task)
+ if (timeout && !hrtimer_sleeper_task_get(timeout))
ret = -ETIMEDOUT;
else if (signal_pending(current))
ret = -ERESTARTNOINTR;
diff --git a/kernel/futex/waitwake.c b/kernel/futex/waitwake.c
index d4483d15d30a..cf18309e5770 100644
--- a/kernel/futex/waitwake.c
+++ b/kernel/futex/waitwake.c
@@ -383,7 +383,7 @@ void futex_do_wait(struct futex_q *q, struct hrtimer_sleeper *timeout)
* flagged for rescheduling. Only call schedule if there
* is no timeout, or if it has yet to expire.
*/
- if (!timeout || timeout->task)
+ if (!timeout || hrtimer_sleeper_task_get(timeout))
schedule();
}
__set_current_state(TASK_RUNNING);
@@ -539,7 +539,7 @@ retry:
static void futex_sleep_multiple(struct futex_vector *vs, unsigned int count,
struct hrtimer_sleeper *to)
{
- if (to && !to->task)
+ if (to && !hrtimer_sleeper_task_get(to))
return;
for (; count; count--, vs++) {
@@ -590,7 +590,7 @@ int futex_wait_multiple(struct futex_vector *vs, unsigned int count,
if (ret >= 0)
return ret;
- if (to && !to->task)
+ if (to && !hrtimer_sleeper_task_get(to))
return -ETIMEDOUT;
else if (signal_pending(current))
return -ERESTARTSYS;
@@ -725,7 +725,7 @@ retry:
if (!futex_unqueue(&q))
return 0;
- if (to && !to->task)
+ if (to && !hrtimer_sleeper_task_get(to))
return -ETIMEDOUT;
/*
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 4728631ae719..5a9534c715b8 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1644,7 +1644,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,
break;
}
- if (timeout && !timeout->task) {
+ if (timeout && !hrtimer_sleeper_task_get(timeout)) {
ret = -ETIMEDOUT;
break;
}
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index cbf1693c86b3..17dd38a6cee7 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -780,7 +780,8 @@ static void hrtimer_switch_to_hres(void)
return;
}
base->hres_active = true;
- hrtimer_resolution = HIGH_RES_NSEC;
+ if (hrtimer_resolution != HIGH_RES_NSEC)
+ hrtimer_resolution = HIGH_RES_NSEC;
tick_setup_sched_timer(true);
/* "Retrigger" the interrupt to get things going */
@@ -2003,7 +2004,7 @@ bool hrtimer_active(const struct hrtimer *timer)
base = READ_ONCE(timer->base);
seq = raw_read_seqcount_begin(&base->seq);
- if (timer->is_queued || base->running == timer)
+ if (timer->is_queued || READ_ONCE(base->running) == timer)
return true;
} while (read_seqcount_retry(&base->seq, seq) || base != READ_ONCE(timer->base));
@@ -2040,7 +2041,7 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base, struct hrtimer_cloc
lockdep_assert_held(&cpu_base->lock);
debug_hrtimer_deactivate(timer);
- base->running = timer;
+ WRITE_ONCE(base->running, timer);
/*
* Separate the ->running assignment from the ->is_queued assignment.
@@ -2099,7 +2100,7 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base, struct hrtimer_cloc
raw_write_seqcount_barrier(&base->seq);
WARN_ON_ONCE(base->running != timer);
- base->running = NULL;
+ WRITE_ONCE(base->running, NULL);
}
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
@@ -2323,9 +2324,9 @@ void hrtimer_run_queues(void)
static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
{
struct hrtimer_sleeper *t = container_of(timer, struct hrtimer_sleeper, timer);
- struct task_struct *task = t->task;
+ struct task_struct *task = hrtimer_sleeper_task_get(t);
- t->task = NULL;
+ hrtimer_sleeper_task_set(t, NULL);
if (task)
wake_up_process(task);
@@ -2354,7 +2355,7 @@ void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl, enum hrtimer_mode
/* If already expired, clear the task pointer and set current state to running */
if (!hrtimer_start_expires_user(&sl->timer, mode)) {
- sl->task = NULL;
+ hrtimer_sleeper_task_set(sl, NULL);
__set_current_state(TASK_RUNNING);
}
}
@@ -2388,7 +2389,7 @@ static void __hrtimer_setup_sleeper(struct hrtimer_sleeper *sl, clockid_t clock_
}
__hrtimer_setup(&sl->timer, hrtimer_wakeup, clock_id, mode);
- sl->task = current;
+ hrtimer_sleeper_task_set(sl, current);
}
/**
@@ -2432,17 +2433,17 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE);
hrtimer_sleeper_start_expires(t, mode);
- if (likely(t->task))
+ if (likely(hrtimer_sleeper_task_get(t)))
schedule();
hrtimer_cancel(&t->timer);
mode = HRTIMER_MODE_ABS;
- } while (t->task && !signal_pending(current));
+ } while (hrtimer_sleeper_task_get(t) && !signal_pending(current));
__set_current_state(TASK_RUNNING);
- if (!t->task)
+ if (!hrtimer_sleeper_task_get(t))
return 0;
restart = &current->restart_block;
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 0bf4fcd969c8..cd75d4bb5b64 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -439,6 +439,38 @@ static void trigger_base_recalc_expires(struct k_itimer *timer,
base->nextevt = 0;
}
+static inline bool cpu_timer_enqueue(struct timerqueue_head *head,
+ struct cpu_timer *ctmr)
+{
+ ctmr->head = head;
+ return timerqueue_add(head, &ctmr->node);
+}
+
+static inline bool cpu_timer_queued(struct cpu_timer *ctmr)
+{
+ return !!ctmr->head;
+}
+
+static inline bool cpu_timer_dequeue(struct cpu_timer *ctmr)
+{
+ if (cpu_timer_queued(ctmr)) {
+ timerqueue_del(ctmr->head, &ctmr->node);
+ ctmr->head = NULL;
+ return true;
+ }
+ return false;
+}
+
+static inline u64 cpu_timer_getexpires(struct cpu_timer *ctmr)
+{
+ return ctmr->node.expires;
+}
+
+static inline void cpu_timer_setexpires(struct cpu_timer *ctmr, u64 exp)
+{
+ ctmr->node.expires = exp;
+}
+
/*
* Dequeue the timer and reset the base if it was its earliest expiration.
* It makes sure the next tick recalculates the base next expiration so we
@@ -607,6 +639,7 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
}
if (!ret) {
+ WARN_ON_ONCE(cpu_timer_queued(&timer->it.cpu));
put_pid(timer->it.cpu.pid);
timer->it_status = POSIX_TIMER_DISARMED;
}
@@ -639,18 +672,50 @@ static void cleanup_timers(struct posix_cputimers *pct)
cleanup_timerqueue(&pct->bases[CPUCLOCK_SCHED].tqhead);
}
+static inline void posix_cpu_timers_exit_work(void);
+
/*
- * These are both called with the siglock held, when the current thread
- * is being reaped. When the final (leader) thread in the group is reaped,
- * posix_cpu_timers_exit_group will be called after posix_cpu_timers_exit.
+ * Invoked from posixtimer_exit_task() after PF_EXITING was set in tsk::flags or
+ * from posixtimer_exec_cleanup().
*/
-void posix_cpu_timers_exit(struct task_struct *tsk)
+void posix_cpu_timers_exit_task(void)
{
- cleanup_timers(&tsk->posix_cputimers);
+ posix_cpu_timers_exit_work();
+
+ guard(spinlock_irq)(&current->sighand->siglock);
+ cleanup_timers(&current->posix_cputimers);
}
-void posix_cpu_timers_exit_group(struct task_struct *tsk)
+
+/*
+ * Invoked from posixtimer_exit_group() after PF_EXITING was set in tsk::flags.
+ */
+void posix_cpu_timers_exit_group(void)
{
- cleanup_timers(&tsk->signal->posix_cputimers);
+ posix_cpu_timers_exit_task();
+
+ guard(spinlock_irq)(&current->sighand->siglock);
+ cleanup_timers(&current->signal->posix_cputimers);
+}
+
+/*
+ * This function validates that POSIX CPU timers can be safely enqueued on the
+ * target task.
+ *
+ * Enqueue is allowed when PF_EXITING is not set. If set then it is only allowed
+ * for process shared timers (type = PIDTYPE_TGID) as long as tsk::signal::flags
+ * does not have SIGNAL_GROUP_EXIT set. PIDTYPE_PID targets are not allowed at
+ * all when the task has PF_EXITING set.
+ *
+ * This guarantees that after the POSIX timer cleanup in posixtimer_exit() no
+ * POSIX CPU timers are queued on the task or in case of a group exit on the
+ * process.
+ */
+static inline bool task_can_enqueue_timer(struct task_struct *tsk, enum pid_type type)
+{
+ if (likely(!(tsk->flags & PF_EXITING)))
+ return true;
+
+ return type == PIDTYPE_TGID && !(tsk->signal->flags & SIGNAL_GROUP_EXIT);
}
/*
@@ -663,7 +728,13 @@ static void arm_timer(struct k_itimer *timer, struct task_struct *p)
struct cpu_timer *ctmr = &timer->it.cpu;
u64 newexp = cpu_timer_getexpires(ctmr);
+ lockdep_assert_held(&p->sighand->siglock);
+
timer->it_status = POSIX_TIMER_ARMED;
+
+ if (unlikely(!task_can_enqueue_timer(p, clock_pid_type(timer->it_clock))))
+ return;
+
if (!cpu_timer_enqueue(&base->tqhead, ctmr))
return;
@@ -1201,6 +1272,20 @@ static void posix_cpu_timers_work(struct callback_head *work)
mutex_unlock(&cw->mutex);
}
+static inline void posix_cpu_timers_exit_work(void)
+{
+ /* Canceling the work is only valid for exit() but not for exec() */
+ if (!(current->flags & PF_EXITING))
+ return;
+ /*
+ * current->flags has PF_EXITING set so this can be done lockless and
+ * with interrupts enabled as PF_EXITING prevents the interrupt from
+ * scheduling the work.
+ */
+ if (current->posix_cputimers_work.scheduled)
+ task_work_cancel(current, &current->posix_cputimers_work.work);
+}
+
/*
* Invoked from the posix-timer core when a cancel operation failed because
* the timer is marked firing. The caller holds rcu_read_lock(), which
@@ -1331,6 +1416,8 @@ static inline void __run_posix_cpu_timers(struct task_struct *tsk)
lockdep_posixtimer_exit();
}
+static inline void posix_cpu_timers_exit_work(void) { }
+
static void posix_cpu_timer_wait_running(struct k_itimer *timr)
{
cpu_relax();
@@ -1477,7 +1564,7 @@ void run_posix_cpu_timers(void)
* posix_cpu_timer_del() may fail to lock_task_sighand(tsk) and
* miss timer->it.cpu.firing != 0.
*/
- if (tsk->exit_state)
+ if (tsk->flags & PF_EXITING)
return;
/*
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 436ba794cc0b..188dbedbffca 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1077,13 +1077,9 @@ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
return 0;
}
-/*
- * Invoked from do_exit() when the last thread of a thread group exits.
- * At that point no other task can access the timers of the dying
- * task anymore.
- */
-void exit_itimers(struct task_struct *tsk)
+static void posixtimer_delete_timers(void)
{
+ struct task_struct *tsk = current;
struct hlist_head timers;
struct hlist_node *next;
struct k_itimer *timer;
@@ -1120,6 +1116,24 @@ void exit_itimers(struct task_struct *tsk)
}
}
+void posixtimer_exit(bool group_dead)
+{
+ if (group_dead) {
+ hrtimer_cancel(&current->signal->real_timer);
+ posix_cpu_timers_exit_group();
+ posixtimer_delete_timers();
+ } else {
+ posix_cpu_timers_exit_task();
+ }
+}
+
+void posixtimer_exec(void)
+{
+ posix_cpu_timers_exit_task();
+ posixtimer_delete_timers();
+ flush_itimer_signals();
+}
+
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
const struct __kernel_timespec __user *, tp)
{
diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
index 4ea9611dd716..79fd7ea71046 100644
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -51,3 +51,6 @@ int common_timer_set(struct k_itimer *timr, int flags,
struct itimerspec64 *old_setting);
void posix_timer_set_common(struct k_itimer *timer, struct itimerspec64 *new_setting);
int common_timer_del(struct k_itimer *timer);
+
+void posix_cpu_timers_exit_task(void);
+void posix_cpu_timers_exit_group(void);
diff --git a/kernel/time/sleep_timeout.c b/kernel/time/sleep_timeout.c
index 3c90574bd904..ad8c415851ae 100644
--- a/kernel/time/sleep_timeout.c
+++ b/kernel/time/sleep_timeout.c
@@ -212,7 +212,7 @@ int __sched schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
hrtimer_set_expires_range_ns(&t.timer, *expires, delta);
hrtimer_sleeper_start_expires(&t, mode);
- if (likely(t.task))
+ if (likely(hrtimer_sleeper_task_get(&t)))
schedule();
hrtimer_cancel(&t.timer);
@@ -220,7 +220,7 @@ int __sched schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
__set_current_state(TASK_RUNNING);
- return !t.task ? 0 : -EINTR;
+ return !hrtimer_sleeper_task_get(&t) ? 0 : -EINTR;
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout_range_clock);
diff --git a/kernel/time/time_test.c b/kernel/time/time_test.c
index 1b99180da288..8b718767b3ba 100644
--- a/kernel/time/time_test.c
+++ b/kernel/time/time_test.c
@@ -87,8 +87,24 @@ static void time64_to_tm_test_date_range(struct kunit *test)
}
}
+static void time64_to_tm_test_wide_day_count(struct kunit *test)
+{
+ /* 2^31 days: the first count that does not fit in a 32-bit long. */
+ time64_t timestamp = (1LL << 31) * 86400;
+ struct tm result;
+
+ time64_to_tm(timestamp, 0, &result);
+
+ KUNIT_EXPECT_EQ(test, result.tm_year, 5879680);
+ KUNIT_EXPECT_EQ(test, result.tm_mon, 6);
+ KUNIT_EXPECT_EQ(test, result.tm_mday, 12);
+ KUNIT_EXPECT_EQ(test, result.tm_yday, 193);
+ KUNIT_EXPECT_EQ(test, result.tm_wday, 6);
+}
+
static struct kunit_case time_test_cases[] = {
KUNIT_CASE_SLOW(time64_to_tm_test_date_range),
+ KUNIT_CASE(time64_to_tm_test_wide_day_count),
{}
};
diff --git a/kernel/time/timeconv.c b/kernel/time/timeconv.c
index 59b922c826e7..aed3af950fa0 100644
--- a/kernel/time/timeconv.c
+++ b/kernel/time/timeconv.c
@@ -49,8 +49,9 @@ void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
u32 u32tmp, day_of_century, year_of_century, day_of_year, month, day;
u64 u64tmp, udays, century, year;
bool is_Jan_or_Feb, is_leap_year;
- long days, rem;
int remainder;
+ long rem;
+ s64 days;
days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder);
rem = remainder;
@@ -70,7 +71,8 @@ void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
result->tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
- result->tm_wday = (4 + days) % 7;
+ div_s64_rem(days + 4, 7, &remainder);
+ result->tm_wday = remainder;
if (result->tm_wday < 0)
result->tm_wday += 7;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index ea2e6e55f37b..d54c4d303db6 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -861,8 +861,10 @@ static void timekeeping_update_from_shadow(struct tk_data *tkd, unsigned int act
*
* Write xtime_sec first so that even if the memcpy() tears the store
* data integrity is provided for ktime_get_real_seconds().
+ * The same goes for ktime_sec and ktime_get_seconds().
*/
WRITE_ONCE(tkd->timekeeper.xtime_sec, tk->xtime_sec);
+ WRITE_ONCE(tkd->timekeeper.ktime_sec, tk->ktime_sec);
memcpy(&tkd->timekeeper, tk, sizeof(*tk));
write_seqcount_end(&tkd->seq);
}
@@ -1169,7 +1171,7 @@ time64_t ktime_get_seconds(void)
struct timekeeper *tk = &tk_core.timekeeper;
WARN_ON(timekeeping_suspended);
- return tk->ktime_sec;
+ return READ_ONCE(tk->ktime_sec);
}
EXPORT_SYMBOL_GPL(ktime_get_seconds);
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index ae9abf14688e..42afdcb229d8 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -890,7 +890,7 @@ static inline void detach_timer(struct timer_list *timer, bool clear_pending)
__hlist_del(entry);
if (clear_pending)
- entry->pprev = NULL;
+ WRITE_ONCE(entry->pprev, NULL);
entry->next = LIST_POISON2;
}
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 059d43355e65..f920e73fff51 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -715,7 +715,7 @@ static void __tmigr_cpu_activate(struct tmigr_cpu *tmc)
trace_tmigr_cpu_active(tmc);
- tmc->cpuevt.ignore = true;
+ WRITE_ONCE(tmc->cpuevt.ignore, true);
WRITE_ONCE(tmc->wakeup, KTIME_MAX);
walk_groups(&tmigr_active_up, &data, tmc);
@@ -1258,7 +1258,7 @@ u64 tmigr_cpu_new_timer(u64 nextexp)
ret = READ_ONCE(tmc->wakeup);
if (nextexp != KTIME_MAX) {
if (nextexp != tmc->cpuevt.nextevt.expires ||
- tmc->cpuevt.ignore) {
+ READ_ONCE(tmc->cpuevt.ignore)) {
ret = tmigr_new_timer(tmc, nextexp);
/*
* Make sure the reevaluation of timers in idle path
@@ -1362,7 +1362,7 @@ static u64 __tmigr_cpu_deactivate(struct tmigr_cpu *tmc, u64 nextexp)
* or CPU goes offline.
*/
if (nextexp != KTIME_MAX)
- tmc->cpuevt.ignore = false;
+ WRITE_ONCE(tmc->cpuevt.ignore, false);
walk_groups(&tmigr_inactive_up, &data, tmc);
return data.firstexp;