Module Name: src
Committed By: riastradh
Date: Wed Mar 5 14:01:55 UTC 2025
Modified Files:
src/sys/kern: sys_futex.c
Log Message:
futex(2): Avoid returning early on timeout.
Rounding in the arithmetic leading into cv_timedwait_sig, and any
skew between the timecounter used by clock_gettime and the hardclock
timer used to wake cv_timedwait_sig, can lead cv_timedwait_sig to
wake up before the deadline as observable by clock_gettime.
futex(FUTEX_WAIT) is not supposed to do that, so ignore when
cv_timedwait_sig returns EWOULDBLOCK -- we'll notice the deadline has
passed in the next iteration anyway, if it has actually passed.
While here, make sure that we never pass less than 1 tick to
cv_timedwait_sig -- that turns it into cv_wait_sig, to wait
indefinitely with no timeout.
With this change, I have not seen any failures as reported in:
PR kern/59132: t_futex_ops:futex_wait_timeout_* sometimes fails on
early wakeup
Some instrumentation in futex_wait to count when cv_timedwait_sig
returns early as measured by clock_gettime (not committed in this
change, just local experiments) supports this hypothesis for the
symptoms observed in the PR.
To generate a diff of this commit:
cvs rdiff -u -r1.25 -r1.26 src/sys/kern/sys_futex.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/sys_futex.c
diff -u src/sys/kern/sys_futex.c:1.25 src/sys/kern/sys_futex.c:1.26
--- src/sys/kern/sys_futex.c:1.25 Wed Mar 5 14:01:34 2025
+++ src/sys/kern/sys_futex.c Wed Mar 5 14:01:55 2025
@@ -1,4 +1,4 @@
-/* $NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $ */
+/* $NetBSD: sys_futex.c,v 1.26 2025/03/05 14:01:55 riastradh Exp $ */
/*-
* Copyright (c) 2018, 2019, 2020 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.26 2025/03/05 14:01:55 riastradh Exp $");
/*
* Futexes
@@ -966,9 +966,17 @@ futex_wait(struct futex_wait *fw, const
/* Count how much time is left. */
timespecsub(deadline, &ts, &ts);
- /* Wait for that much time, allowing signals. */
+ /*
+ * Wait for that much time, allowing signals.
+ * Ignore EWOULDBLOCK, however: we will detect
+ * timeout ourselves on the next iteration of
+ * the loop -- and the timeout may have been
+ * truncated by tstohz, anyway.
+ */
error = cv_timedwait_sig(&fw->fw_cv, &fw->fw_lock,
- tstohz(&ts));
+ MAX(1, tstohz(&ts)));
+ if (error == EWOULDBLOCK)
+ error = 0;
} else {
/* Wait indefinitely, allowing signals. */
error = cv_wait_sig(&fw->fw_cv, &fw->fw_lock);
@@ -978,14 +986,10 @@ futex_wait(struct futex_wait *fw, const
/*
* If we were woken up, the waker will have removed fw from the
* queue. But if anything went wrong, we must remove fw from
- * the queue ourselves. While here, convert EWOULDBLOCK to
- * ETIMEDOUT.
+ * the queue ourselves.
*/
- if (error) {
+ if (error)
futex_wait_abort(fw);
- if (error == EWOULDBLOCK)
- error = ETIMEDOUT;
- }
mutex_exit(&fw->fw_lock);