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