From: Shashank Mohan Jain <jain.sm@gmail.com>
To: John Stultz <jstultz@google.com>, Thomas Gleixner <tglx@kernel.org>
Cc: Stephen Boyd <sboyd@kernel.org>,
Miroslav Lichvar <mlichvar@redhat.com>,
Eric Dumazet <edumazet@google.com>,
Joel Granados <joel.granados@kernel.org>,
linux-kernel@vger.kernel.org
Subject: [PATCH 2/3] time/jiffies: Use the exact HZ ratio in jiffies_to_clock_t()
Date: Mon, 28 Sep 2026 15:06:00 +0530 [thread overview]
Message-ID: <20260928093601.79224-3-jain.sm@gmail.com> (raw)
In-Reply-To: <20260928093601.79224-1-jain.sm@gmail.com>
When TICK_NSEC is not a multiple of NSEC_PER_SEC / USER_HZ,
jiffies_to_clock_t() and jiffies_64_to_clock_t() convert with
div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ)
TICK_NSEC is NSEC_PER_SEC / HZ rounded to the nearest nanosecond. When
HZ does not divide NSEC_PER_SEC it is slightly short (3333333 ns at
HZ=300), so a number of jiffies that is exactly a whole number of clock
ticks converts to just below it and is truncated to one tick less. With
HZ=300 and USER_HZ=100, jiffies_to_clock_t(300) is 99, and every
multiple of 3 jiffies is one tick short.
The tick does advance jiffies every TICK_NSEC, so as a measure of
elapsed time the old result is accurate to about 1e-7. But intervals
that userspace sets in clock ticks are stored with clock_t_to_jiffies(),
which uses the nominal HZ ratio, as do jiffies_to_msecs() and
jiffies_to_usecs(), and they no longer read back. With HZ=300 the bridge
defaults read as forward_delay 1499, hello_time 199, max_age 1999 and
ageing_time 29999 through sysfs and netlink, and each read-modify-write
through sysfs loses one more tick (1500 -> 1499 -> 1498). The neighbour
sysctls that use proc_dointvec_userhz_jiffies() behave the same way:
net.ipv4.neigh.*.retrans_time, anycast_delay and locktime read 99
instead of 100, proxy_delay reads 79 instead of 80, and
net.ipv6.neigh.*.anycast_delay reads 99. Inside the kernel, IPv6 route
lifetimes from router advertisements and netlink go through
jiffies_to_clock_t() and back through clock_t_to_jiffies(), so at
HZ=300 those routes expire 3 jiffies early.
Convert with the nominal ratio, x * USER_HZ / HZ, like
clock_t_to_jiffies() does. With USER_HZ=100 the result does not change
where TICK_NSEC is exact (e.g. HZ=250). On alpha, where USER_HZ is 1024,
NSEC_PER_SEC / USER_HZ is itself truncated and the old code returned one
tick too many for large values (jiffies_to_clock_t(976562) == 976563
with HZ=1024); that is fixed too. The value returned by times() now
advances by exactly USER_HZ ticks per HZ jiffies. The product also
overflows much later than x * TICK_NSEC did in jiffies_64_to_clock_t().
The TICK_NSEC based conversion predates git. It made sense while
TICK_NSEC was the tick length measured from CLOCK_TICK_RATE, when x86
returned 99 for 300 jiffies at HZ=300 and for 1000 jiffies at HZ=1000.
Commit b3c869d35b9b ("jiffies: Remove compile time assumptions about
CLOCK_TICK_RATE") made TICK_NSEC exact for HZ values that divide
NSEC_PER_SEC, but left the others rounded.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <jain.sm@gmail.com>
---
The KUnit test in patch 3 fails without this patch on qemu x86_64 and
i386 with HZ=300 and passes with it; UML (HZ=100) is unaffected. It
also passes on UML x86_64 with HZ/USER_HZ temporarily edited to
1200/100 and 1024/1024, both of which fail without the series. In
userspace, with the code extracted verbatim, both functions equal
floor(x * USER_HZ / HZ) for every x below 5e7 and invert
clock_t_to_jiffies() for every whole-jiffy clock_t value below 2e7, for
every Kconfig HZ value with USER_HZ 100 and 1024, 32- and 64-bit. The
bridge and neighbour values above were read back on an x86_64 HZ=300
kernel in qemu, before and after this series.
Possible follow-up, not part of this series:
tools/testing/selftests/bpf/progs/bpf_iter_tcp4.c and bpf_iter_tcp6.c
carry a copy of the old TICK_NSEC formula so that they print the same
values as /proc/net/tcp. The test only does a dummy read, so nothing
breaks, but at HZ=300 the copies no longer match the kernel.
Prepared with Claude Code (Anthropic), model Claude Opus 5.5
(claude-opus-5-5).
kernel/time/time.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/kernel/time/time.c b/kernel/time/time.c
index 470195e88ca0..f19e742c9562 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -684,7 +684,11 @@ clock_t jiffies_to_clock_t(unsigned long x)
return x / (HZ / USER_HZ);
# endif
#else
- return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);
+ /*
+ * Use the nominal HZ ratio like clock_t_to_jiffies(); TICK_NSEC is
+ * rounded when HZ does not divide NSEC_PER_SEC.
+ */
+ return div_u64((u64)x * USER_HZ, HZ);
#endif
}
EXPORT_SYMBOL(jiffies_to_clock_t);
@@ -729,12 +733,8 @@ notrace u64 jiffies_64_to_clock_t(u64 x)
/* Nothing to do */
# endif
#else
- /*
- * There are better ways that don't overflow early,
- * but even this doesn't overflow in hundreds of years
- * in 64 bits, so..
- */
- x = div_u64(x * TICK_NSEC, (NSEC_PER_SEC / USER_HZ));
+ /* Nominal HZ ratio, see jiffies_to_clock_t() */
+ x = div_u64(x * USER_HZ, HZ);
#endif
return x;
}
--
2.43.0
next prev parent reply other threads:[~2026-09-28 9:36 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 9:35 [PATCH 0/3] time/jiffies: Fix usecs_to_jiffies() saturation and jiffies_to_clock_t() rounding Shashank Mohan Jain
2026-09-28 9:35 ` [PATCH 1/3] time/jiffies: Don't saturate usecs_to_jiffies() on a truncated limit Shashank Mohan Jain
2026-09-28 11:38 ` Joel Granados
2026-09-28 12:09 ` shashank Jain
2026-09-28 9:36 ` Shashank Mohan Jain [this message]
2026-09-28 9:36 ` [PATCH 3/3] time/kunit: Add tests for usecs_to_jiffies() and jiffies_to_clock_t() Shashank Mohan Jain
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=20260928093601.79224-3-jain.sm@gmail.com \
--to=jain.sm@gmail.com \
--cc=edumazet@google.com \
--cc=joel.granados@kernel.org \
--cc=jstultz@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mlichvar@redhat.com \
--cc=sboyd@kernel.org \
--cc=tglx@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
all inboxes | Powered by JetHome®