Commit 7e55b6ba authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

timekeeping: Avoid double notification in do_adjtimex()



Consolidate do_adjtimex() so that it does not notify about clock changes
twice.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250519083025.779267274@linutronix.de
parent 506a54a0
Loading
Loading
Loading
Loading
+56 −42
Original line number Diff line number Diff line
@@ -1418,19 +1418,19 @@ 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);

@@ -1445,11 +1445,20 @@ static int timekeeping_inject_offset(const struct timespec64 *ts)
	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 0;
	return ret;
}

/*
@@ -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,30 +2562,28 @@ int do_adjtimex(struct __kernel_timex *txc)
		return ret;
	add_device_randomness(txc, sizeof(*txc));

	if (txc->modes & ADJ_SETOFFSET) {
		struct timespec64 delta;
	audit_ntp_init(&ad);

	ktime_get_real_ts64(&ts);
	add_device_randomness(&ts, sizeof(ts));

	scoped_guard (raw_spinlock_irqsave, &tk_core.lock) {
		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);
			ret = __timekeeping_inject_offset(&delta);
			if (ret)
				return ret;

			offset_set = delta.tv_sec != 0;
		audit_tk_injoffset(delta);
			clock_set = true;
		}

	audit_ntp_init(&ad);

	ktime_get_real_ts64(&ts);
	add_device_randomness(&ts, sizeof(ts));

	scoped_guard (raw_spinlock_irqsave, &tk_core.lock) {
		struct timekeeper *tks = &tk_core.shadow_timekeeper;
		s32 orig_tai, tai;

		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);
		}
	}

	audit_ntp_log(&ad);

		/* Update the multiplier immediately if frequency was set directly */
		if (txc->modes & (ADJ_FREQUENCY | ADJ_TICK))
		clock_set |= timekeeping_advance(TK_ADV_FREQ);
			clock_set |= __timekeeping_advance(TK_ADV_FREQ);
	}

	if (txc->modes & ADJ_SETOFFSET)
		audit_tk_injoffset(delta);

	audit_ntp_log(&ad);

	if (clock_set)
		clock_was_set(CLOCK_SET_WALL);