From: sdolan@janestreet.com
To: linux-kernel@vger.kernel.org
Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>,
Frederic Weisbecker <frederic@kernel.org>,
Thomas Gleixner <tglx@kernel.org>,
Stephen Dolan <sdolan@janestreet.com>
Subject:
Date: Mon, 30 Mar 2026 17:25:56 +0100 [thread overview]
Message-ID: <01fbc0e6-adba-329c-b2b7-d23d0b2fb6b5@ocaml.async_smtp> (raw)
When nanosleep or clock_nanosleep are interrupted by a signal, they
return EINTR and the remaining time, so that the caller can resume the
sleep afterwards. However, with a high rate of signals, nanosleep
currently ends up sleeping for much longer than requested. Here's an
example program that tries to sleep for 1s with 10k signals/sec, and
ends up sleeping for >2.1s:
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
static void handler(int sig) {(void)sig;}
int main() {
signal(SIGALRM, handler);
struct timeval intv = { .tv_sec = 0, .tv_usec = 100 };
struct itimerval it = { .it_interval = intv, .it_value = intv };
setitimer(ITIMER_REAL, &it, 0);
struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
int rc;
do {
rc = nanosleep(&ts, &ts);
} while (rc < 0 && errno == EINTR);
}
The manpage for nanosleep points out that using relative timeouts here
can cause drift, but it shouldn't take 1.1 extra seconds to handle 10k
signals.
The problem is in do_nanosleep: the 'expires' field of the hrtimer used
for the sleep is (now + timeout + timerslack), which is by default 50 us
later than the requested end time. This field is used to compute the
remaining time, which means that 50 us is added to the wakeup time every
time nanosleep returns EINTR. Since the example has signals arriving
every 100us, this causes a much later wakeup than intended. If the
example is modified to have an interval between signals shorter than
50us (or whatever timerslack is set to), then the nanosleep loop will
never complete.
Instead, the remaining time should be computed from _softexpires, which
is the original deadline before timerslack is added. This patch does
that, which makes the example above run in about 1.040 seconds. (The
extra 40ms is the drift the manpage was warning about, and is more in
line with how long it takes to handle 10k signals).
Signed-off-by: Stephen Dolan <sdolan@janestreet.com>
---
include/linux/hrtimer.h | 5 +++++
kernel/time/hrtimer.c | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 74adbd4e7003..c6f0b9ad3dc3 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -146,6 +146,11 @@ static inline ktime_t hrtimer_expires_remaining(const struct hrtimer *timer)
return ktime_sub(timer->node.expires, hrtimer_cb_get_time(timer));
}
+static inline ktime_t hrtimer_softexpires_remaining(const struct hrtimer *timer)
+{
+ return ktime_sub(timer->_softexpires, hrtimer_cb_get_time(timer));
+}
+
static inline int hrtimer_is_hres_active(struct hrtimer *timer)
{
return IS_ENABLED(CONFIG_HIGH_RES_TIMERS) ?
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 860af7a58428..12310967ca84 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -2135,7 +2135,7 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
restart = ¤t->restart_block;
if (restart->nanosleep.type != TT_NONE) {
- ktime_t rem = hrtimer_expires_remaining(&t->timer);
+ ktime_t rem = hrtimer_softexpires_remaining(&t->timer);
struct timespec64 rmt;
if (rem <= 0)
--
2.47.3
reply other threads:[~2026-03-30 16:31 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=01fbc0e6-adba-329c-b2b7-d23d0b2fb6b5@ocaml.async_smtp \
--to=sdolan@janestreet.com \
--cc=anna-maria@linutronix.de \
--cc=frederic@kernel.org \
--cc=linux-kernel@vger.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®