public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Thomas Gleixner <tglx@linutronix.de>
To: Benjamin ROBIN <dev@benjarobin.fr>, jstultz@google.com
Cc: sboyd@kernel.org, linux-kernel@vger.kernel.org,
	Benjamin ROBIN <dev@benjarobin.fr>
Subject: Re: [PATCH] ntp: Make sure RTC is synchronized when time goes backwards
Date: Sat, 07 Sep 2024 23:42:55 +0200	[thread overview]
Message-ID: <87zfojcf8g.ffs@tglx> (raw)
In-Reply-To: <20240907190900.55421-1-dev@benjarobin.fr>

On Sat, Sep 07 2024 at 21:09, Benjamin ROBIN wrote:
> The "sync_hw_clock" is normally called every 11 minutes when time is

s/The "sync_hw_clock"/sync_hw_clock()/

See: https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#function-references-in-changelogs

> synchronized. This issue is that this periodic timer uses the REALTIME
> clock, so when time moves backwards (the NTP server jumps into the past),
> the next call to "sync_hw_clock" could be realized after a very long

s/the next .../the timer expires late./

And then please explain what the consequence is when it expires
late. See the changelog section of the above link.

> period.

Cute.

> A normal NTP server should not jump in the past like that, but it is
> possible... Another way to reproduce this issue is using phc2sys to
> synchronize the REALTIME clock with for example an IRIG timecode with
> the source always starting at the same date (not synchronized).
>
> This patch cancels the periodic timer on a time jump (ADJ_SETOFFSET).

s/This patch cancels/Cancel/

For explanation:
# git grep 'This patch' Documentation/process 

> The timer will be relaunched at the end of "do_adjtimex" if NTP is still
> considered synced. Otherwise the timer will be relaunched later when NTP
> is synced. This way, when the time is synchronized again, the RTC is
> updated after less than 2 seconds.
>
> Signed-off-by: Benjamin ROBIN <dev@benjarobin.fr>
> ---
>  kernel/time/ntp.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
> index 8d2dd214ec68..5c8dd92cf012 100644
> --- a/kernel/time/ntp.c
> +++ b/kernel/time/ntp.c
> @@ -751,6 +751,9 @@ static inline void process_adjtimex_modes(const struct __kernel_timex *txc,
>  
>  	if (txc->modes & (ADJ_TICK|ADJ_FREQUENCY|ADJ_OFFSET))
>  		ntp_update_frequency();
> +
> +	if (txc->modes & ADJ_SETOFFSET)
> +		hrtimer_cancel(&sync_hrtimer);

Did you test this with lockdep enabled?

The caller holds timekeeping_lock and has the time keeper sequence write
held. There is an existing dependency chain which is invers. Assume the
sync_hrtimer is queued on a different CPU, CPU1 in this example:

CPU 0                                       CPU1

lock(&timekeeper_lock);                     lock_hrtimer_base(CPU1);    

write_seqcount_begin(&tk_core.seq); <- Makes tk_core.seq odd

__do_adjtimex()
  process_adjtimex_modes()                  base->get_time()
    hrtimer_cancel()                          do {
      hrtimer_try_to_cancel()                       seq = read_seqcount_begin(&tk_core.seq);
        lock_hrtimer_base(CPU1);                          ^^^
        ^^^ deadlock                                      Spin waits for tk_core.seq
                                                          to become even

You can do that cancel only outside of timekeeper_lock:

--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2426,6 +2426,7 @@ int do_adjtimex(struct __kernel_timex *t
 {
 	struct timekeeper *tk = &tk_core.timekeeper;
 	struct audit_ntp_data ad;
+	bool offset_set = false;
 	bool clock_set = false;
 	struct timespec64 ts;
 	unsigned long flags;
@@ -2448,6 +2449,7 @@ int do_adjtimex(struct __kernel_timex *t
 		if (ret)
 			return ret;
 
+		offset_set = delta.tv_sec < 0;
 		audit_tk_injoffset(delta);
 	}
 
@@ -2481,7 +2483,7 @@ int do_adjtimex(struct __kernel_timex *t
 	if (clock_set)
 		clock_was_set(CLOCK_REALTIME);
 
-	ntp_notify_cmos_timer();
+	ntp_notify_cmos_timer(offset_set);
 
 	return ret;
 }

Now you can fix that up in ntp_notify_cmos_timer() which is outside of
the timekeeper_lock held region for the very same reason and it's the
proper place to do that.

Thanks,

        tglx

  reply	other threads:[~2024-09-07 21:42 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-07 19:09 [PATCH] ntp: Make sure RTC is synchronized when time goes backwards Benjamin ROBIN
2024-09-07 21:42 ` Thomas Gleixner [this message]
2024-09-08 11:45   ` Benjamin ROBIN
2024-09-08 11:45   ` [PATCH v2] " Benjamin ROBIN
2024-09-08 12:39 ` [PATCH] " kernel test robot
2024-09-08 12:39 ` kernel test robot
2024-09-08 14:08 ` [PATCH v3] " Benjamin ROBIN
2024-09-10 11:59   ` [tip: timers/core] " tip-bot2 for Benjamin ROBIN

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zfojcf8g.ffs@tglx \
    --to=tglx@linutronix.de \
    --cc=dev@benjarobin.fr \
    --cc=jstultz@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sboyd@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox