]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
timekeeping: Avoid double notification in do_adjtimex()
authorThomas Gleixner <tglx@linutronix.de>
Mon, 19 May 2025 08:33:17 +0000 (10:33 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 19 Jun 2025 12:28:22 +0000 (14:28 +0200)
Consolidate do_adjtimex() so that it does not notify about clock changes
twice.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250519083025.779267274@linutronix.de
kernel/time/timekeeping.c

index d88d19fb794cbe3b8f5424c1b8b865bb1ca35a29..fb1da87a92f1ad39e06f45635620fc35e277ca3d 100644 (file)
@@ -1418,40 +1418,49 @@ int do_settimeofday64(const struct timespec64 *ts)
 EXPORT_SYMBOL(do_settimeofday64);
 
 /**
- * timekeeping_inject_offset - Adds or subtracts from the current time.
+ * __timekeeping_inject_offset - Adds or subtracts from the current time.
  * @ts:                Pointer to the timespec variable containing the offset
  *
  * Adds or subtracts an offset value from the current time.
  */
-static int timekeeping_inject_offset(const struct timespec64 *ts)
+static int __timekeeping_inject_offset(const struct timespec64 *ts)
 {
+       struct timekeeper *tks = &tk_core.shadow_timekeeper;
+       struct timespec64 tmp;
+
        if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
                return -EINVAL;
 
-       scoped_guard (raw_spinlock_irqsave, &tk_core.lock) {
-               struct timekeeper *tks = &tk_core.shadow_timekeeper;
-               struct timespec64 tmp;
 
-               timekeeping_forward_now(tks);
-
-               /* Make sure the proposed value is valid */
-               tmp = timespec64_add(tk_xtime(tks), *ts);
-               if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
-                   !timespec64_valid_settod(&tmp)) {
-                       timekeeping_restore_shadow(&tk_core);
-                       return -EINVAL;
-               }
+       timekeeping_forward_now(tks);
 
-               tk_xtime_add(tks, ts);
-               tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
-               timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
+       /* Make sure the proposed value is valid */
+       tmp = timespec64_add(tk_xtime(tks), *ts);
+       if (timespec64_compare(&tks->wall_to_monotonic, ts) > 0 ||
+           !timespec64_valid_settod(&tmp)) {
+               timekeeping_restore_shadow(&tk_core);
+               return -EINVAL;
        }
 
-       /* Signal hrtimers about time change */
-       clock_was_set(CLOCK_SET_WALL);
+       tk_xtime_add(tks, ts);
+       tk_set_wall_to_mono(tks, timespec64_sub(tks->wall_to_monotonic, *ts));
+       timekeeping_update_from_shadow(&tk_core, TK_UPDATE_ALL);
        return 0;
 }
 
+static int timekeeping_inject_offset(const struct timespec64 *ts)
+{
+       int ret;
+
+       scoped_guard (raw_spinlock_irqsave, &tk_core.lock)
+               ret = __timekeeping_inject_offset(ts);
+
+       /* Signal hrtimers about time change */
+       if (!ret)
+               clock_was_set(CLOCK_SET_WALL);
+       return ret;
+}
+
 /*
  * Indicates if there is an offset between the system clock and the hardware
  * clock/persistent clock/rtc.
@@ -2186,7 +2195,7 @@ static u64 logarithmic_accumulation(struct timekeeper *tk, u64 offset,
  * timekeeping_advance - Updates the timekeeper to the current time and
  * current NTP tick length
  */
-static bool timekeeping_advance(enum timekeeping_adv_mode mode)
+static bool __timekeeping_advance(enum timekeeping_adv_mode mode)
 {
        struct timekeeper *tk = &tk_core.shadow_timekeeper;
        struct timekeeper *real_tk = &tk_core.timekeeper;
@@ -2194,8 +2203,6 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
        int shift = 0, maxshift;
        u64 offset, orig_offset;
 
-       guard(raw_spinlock_irqsave)(&tk_core.lock);
-
        /* Make sure we're fully resumed: */
        if (unlikely(timekeeping_suspended))
                return false;
@@ -2249,6 +2256,12 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
        return !!clock_set;
 }
 
+static bool timekeeping_advance(enum timekeeping_adv_mode mode)
+{
+       guard(raw_spinlock_irqsave)(&tk_core.lock);
+       return __timekeeping_advance(mode);
+}
+
 /**
  * update_wall_time - Uses the current clocksource to increment the wall time
  *
@@ -2537,10 +2550,10 @@ EXPORT_SYMBOL_GPL(random_get_entropy_fallback);
  */
 int do_adjtimex(struct __kernel_timex *txc)
 {
+       struct timespec64 delta, ts;
        struct audit_ntp_data ad;
        bool offset_set = false;
        bool clock_set = false;
-       struct timespec64 ts;
        int ret;
 
        /* Validate the data before disabling interrupts */
@@ -2549,21 +2562,6 @@ int do_adjtimex(struct __kernel_timex *txc)
                return ret;
        add_device_randomness(txc, sizeof(*txc));
 
-       if (txc->modes & ADJ_SETOFFSET) {
-               struct timespec64 delta;
-
-               delta.tv_sec  = txc->time.tv_sec;
-               delta.tv_nsec = txc->time.tv_usec;
-               if (!(txc->modes & ADJ_NANO))
-                       delta.tv_nsec *= 1000;
-               ret = timekeeping_inject_offset(&delta);
-               if (ret)
-                       return ret;
-
-               offset_set = delta.tv_sec != 0;
-               audit_tk_injoffset(delta);
-       }
-
        audit_ntp_init(&ad);
 
        ktime_get_real_ts64(&ts);
@@ -2573,6 +2571,19 @@ int do_adjtimex(struct __kernel_timex *txc)
                struct timekeeper *tks = &tk_core.shadow_timekeeper;
                s32 orig_tai, tai;
 
+               if (txc->modes & ADJ_SETOFFSET) {
+                       delta.tv_sec  = txc->time.tv_sec;
+                       delta.tv_nsec = txc->time.tv_usec;
+                       if (!(txc->modes & ADJ_NANO))
+                               delta.tv_nsec *= 1000;
+                       ret = __timekeeping_inject_offset(&delta);
+                       if (ret)
+                               return ret;
+
+                       offset_set = delta.tv_sec != 0;
+                       clock_set = true;
+               }
+
                orig_tai = tai = tks->tai_offset;
                ret = __do_adjtimex(txc, &ts, &tai, &ad);
 
@@ -2583,13 +2594,16 @@ int do_adjtimex(struct __kernel_timex *txc)
                } else {
                        tk_update_leap_state_all(&tk_core);
                }
+
+               /* Update the multiplier immediately if frequency was set directly */
+               if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
+                       clock_set |= __timekeeping_advance(TK_ADV_FREQ);
        }
 
-       audit_ntp_log(&ad);
+       if (txc->modes & ADJ_SETOFFSET)
+               audit_tk_injoffset(delta);
 
-       /* Update the multiplier immediately if frequency was set directly */
-       if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
-               clock_set |= timekeeping_advance(TK_ADV_FREQ);
+       audit_ntp_log(&ad);
 
        if (clock_set)
                clock_was_set(CLOCK_SET_WALL);