Skip to content

Commit

Permalink
Merge tag 'timers-urgent-2025-05-11' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/tip/tip

Pull misc timers fixes from Ingo Molnar:

 - Fix time keeping bugs in CLOCK_MONOTONIC_COARSE clocks

 - Work around absolute relocations into vDSO code that GCC erroneously
   emits in certain arm64 build environments

 - Fix a false positive lockdep warning in the i8253 clocksource driver

* tag 'timers-urgent-2025-05-11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  clocksource/i8253: Use raw_spinlock_irqsave() in clockevent_i8253_disable()
  arm64: vdso: Work around invalid absolute relocations from GCC
  timekeeping: Prevent coarse clocks going backwards
  • Loading branch information
Linus Torvalds committed May 11, 2025
2 parents fea9123 + 94cff94 commit ac814cb
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 16 deletions.
13 changes: 13 additions & 0 deletions arch/arm64/include/asm/vdso/gettimeofday.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
return res;
}

#if IS_ENABLED(CONFIG_CC_IS_GCC) && IS_ENABLED(CONFIG_PAGE_SIZE_64KB)
static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void)
{
const struct vdso_time_data *ret = &vdso_u_time_data;

/* Work around invalid absolute relocations */
OPTIMIZER_HIDE_VAR(ret);

return ret;
}
#define __arch_get_vdso_u_time_data __arch_get_vdso_u_time_data
#endif /* IS_ENABLED(CONFIG_CC_IS_GCC) && IS_ENABLED(CONFIG_PAGE_SIZE_64KB) */

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
4 changes: 1 addition & 3 deletions drivers/clocksource/i8253.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ int __init clocksource_i8253_init(void)
#ifdef CONFIG_CLKEVT_I8253
void clockevent_i8253_disable(void)
{
raw_spin_lock(&i8253_lock);
guard(raw_spinlock_irqsave)(&i8253_lock);

/*
* Writing the MODE register should stop the counter, according to
Expand Down Expand Up @@ -132,8 +132,6 @@ void clockevent_i8253_disable(void)
outb_p(0, PIT_CH0);

outb_p(0x30, PIT_MODE);

raw_spin_unlock(&i8253_lock);
}

static int pit_shutdown(struct clock_event_device *evt)
Expand Down
8 changes: 5 additions & 3 deletions include/linux/timekeeper_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ struct tk_read_base {
* @offs_real: Offset clock monotonic -> clock realtime
* @offs_boot: Offset clock monotonic -> clock boottime
* @offs_tai: Offset clock monotonic -> clock tai
* @tai_offset: The current UTC to TAI offset in seconds
* @coarse_nsec: The nanoseconds part for coarse time getters
* @tkr_raw: The readout base structure for CLOCK_MONOTONIC_RAW
* @raw_sec: CLOCK_MONOTONIC_RAW time in seconds
* @clock_was_set_seq: The sequence number of clock was set events
Expand All @@ -76,6 +76,7 @@ struct tk_read_base {
* ntp shifted nano seconds.
* @ntp_err_mult: Multiplication factor for scaled math conversion
* @skip_second_overflow: Flag used to avoid updating NTP twice with same second
* @tai_offset: The current UTC to TAI offset in seconds
*
* Note: For timespec(64) based interfaces wall_to_monotonic is what
* we need to add to xtime (or xtime corrected for sub jiffy times)
Expand All @@ -100,7 +101,7 @@ struct tk_read_base {
* which results in the following cacheline layout:
*
* 0: seqcount, tkr_mono
* 1: xtime_sec ... tai_offset
* 1: xtime_sec ... coarse_nsec
* 2: tkr_raw, raw_sec
* 3,4: Internal variables
*
Expand All @@ -121,7 +122,7 @@ struct timekeeper {
ktime_t offs_real;
ktime_t offs_boot;
ktime_t offs_tai;
s32 tai_offset;
u32 coarse_nsec;

/* Cacheline 2: */
struct tk_read_base tkr_raw;
Expand All @@ -144,6 +145,7 @@ struct timekeeper {
u32 ntp_error_shift;
u32 ntp_err_mult;
u32 skip_second_overflow;
s32 tai_offset;
};

#ifdef CONFIG_GENERIC_TIME_VSYSCALL
Expand Down
50 changes: 42 additions & 8 deletions kernel/time/timekeeping.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,42 @@ static inline struct timespec64 tk_xtime(const struct timekeeper *tk)
return ts;
}

static inline struct timespec64 tk_xtime_coarse(const struct timekeeper *tk)
{
struct timespec64 ts;

ts.tv_sec = tk->xtime_sec;
ts.tv_nsec = tk->coarse_nsec;
return ts;
}

/*
* Update the nanoseconds part for the coarse time keepers. They can't rely
* on xtime_nsec because xtime_nsec could be adjusted by a small negative
* amount when the multiplication factor of the clock is adjusted, which
* could cause the coarse clocks to go slightly backwards. See
* timekeeping_apply_adjustment(). Thus we keep a separate copy for the coarse
* clockids which only is updated when the clock has been set or we have
* accumulated time.
*/
static inline void tk_update_coarse_nsecs(struct timekeeper *tk)
{
tk->coarse_nsec = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
}

static void tk_set_xtime(struct timekeeper *tk, const struct timespec64 *ts)
{
tk->xtime_sec = ts->tv_sec;
tk->tkr_mono.xtime_nsec = (u64)ts->tv_nsec << tk->tkr_mono.shift;
tk_update_coarse_nsecs(tk);
}

static void tk_xtime_add(struct timekeeper *tk, const struct timespec64 *ts)
{
tk->xtime_sec += ts->tv_sec;
tk->tkr_mono.xtime_nsec += (u64)ts->tv_nsec << tk->tkr_mono.shift;
tk_normalize_xtime(tk);
tk_update_coarse_nsecs(tk);
}

static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm)
Expand Down Expand Up @@ -708,6 +733,7 @@ static void timekeeping_forward_now(struct timekeeper *tk)
tk_normalize_xtime(tk);
delta -= incr;
}
tk_update_coarse_nsecs(tk);
}

/**
Expand Down Expand Up @@ -804,16 +830,16 @@ EXPORT_SYMBOL_GPL(ktime_get_with_offset);
ktime_t ktime_get_coarse_with_offset(enum tk_offsets offs)
{
struct timekeeper *tk = &tk_core.timekeeper;
unsigned int seq;
ktime_t base, *offset = offsets[offs];
unsigned int seq;
u64 nsecs;

WARN_ON(timekeeping_suspended);

do {
seq = read_seqcount_begin(&tk_core.seq);
base = ktime_add(tk->tkr_mono.base, *offset);
nsecs = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
nsecs = tk->coarse_nsec;

} while (read_seqcount_retry(&tk_core.seq, seq));

Expand Down Expand Up @@ -2161,7 +2187,7 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
struct timekeeper *real_tk = &tk_core.timekeeper;
unsigned int clock_set = 0;
int shift = 0, maxshift;
u64 offset;
u64 offset, orig_offset;

guard(raw_spinlock_irqsave)(&tk_core.lock);

Expand All @@ -2172,7 +2198,7 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
offset = clocksource_delta(tk_clock_read(&tk->tkr_mono),
tk->tkr_mono.cycle_last, tk->tkr_mono.mask,
tk->tkr_mono.clock->max_raw_delta);

orig_offset = offset;
/* Check if there's really nothing to do */
if (offset < real_tk->cycle_interval && mode == TK_ADV_TICK)
return false;
Expand Down Expand Up @@ -2205,6 +2231,14 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
*/
clock_set |= accumulate_nsecs_to_secs(tk);

/*
* To avoid inconsistencies caused adjtimex TK_ADV_FREQ calls
* making small negative adjustments to the base xtime_nsec
* value, only update the coarse clocks if we accumulated time
*/
if (orig_offset != offset)
tk_update_coarse_nsecs(tk);

timekeeping_update_from_shadow(&tk_core, clock_set);

return !!clock_set;
Expand Down Expand Up @@ -2248,7 +2282,7 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
do {
seq = read_seqcount_begin(&tk_core.seq);

*ts = tk_xtime(tk);
*ts = tk_xtime_coarse(tk);
} while (read_seqcount_retry(&tk_core.seq, seq));
}
EXPORT_SYMBOL(ktime_get_coarse_real_ts64);
Expand All @@ -2271,7 +2305,7 @@ void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)

do {
seq = read_seqcount_begin(&tk_core.seq);
*ts = tk_xtime(tk);
*ts = tk_xtime_coarse(tk);
offset = tk_core.timekeeper.offs_real;
} while (read_seqcount_retry(&tk_core.seq, seq));

Expand Down Expand Up @@ -2350,12 +2384,12 @@ void ktime_get_coarse_ts64(struct timespec64 *ts)
do {
seq = read_seqcount_begin(&tk_core.seq);

now = tk_xtime(tk);
now = tk_xtime_coarse(tk);
mono = tk->wall_to_monotonic;
} while (read_seqcount_retry(&tk_core.seq, seq));

set_normalized_timespec64(ts, now.tv_sec + mono.tv_sec,
now.tv_nsec + mono.tv_nsec);
now.tv_nsec + mono.tv_nsec);
}
EXPORT_SYMBOL(ktime_get_coarse_ts64);

Expand Down
4 changes: 2 additions & 2 deletions kernel/time/vsyscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ void update_vsyscall(struct timekeeper *tk)
/* CLOCK_REALTIME_COARSE */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_REALTIME_COARSE];
vdso_ts->sec = tk->xtime_sec;
vdso_ts->nsec = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
vdso_ts->nsec = tk->coarse_nsec;

/* CLOCK_MONOTONIC_COARSE */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC_COARSE];
vdso_ts->sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
nsec = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
nsec = tk->coarse_nsec;
nsec = nsec + tk->wall_to_monotonic.tv_nsec;
vdso_ts->sec += __iter_div_u64_rem(nsec, NSEC_PER_SEC, &vdso_ts->nsec);

Expand Down

0 comments on commit ac814cb

Please sign in to comment.