diff options
author | cheloha <cheloha@cvs.openbsd.org> | 2020-01-12 00:01:13 +0000 |
---|---|---|
committer | cheloha <cheloha@cvs.openbsd.org> | 2020-01-12 00:01:13 +0000 |
commit | f74a383c8bf01aae4a7f66e18dd8161466f94af4 (patch) | |
tree | 4840b7b2af534b98192efd1c22d3dabd393e2c62 | |
parent | f5c72763bd065929fc5b8419bb0db158b05791cd (diff) |
*sleep_nsec(9): sleep *at least* the given number of nanoseconds
The *sleep(9) interfaces are challenging to use when one needs to sleep
for a given minimum duration: the programmer needs to account for both
the current tick and any integer division when converting an interval
to a count of ticks. This sort of input conversion is complicated and
ugly at best and error-prone at worst.
This patch consolidates this conversion logic into the *sleep_nsec(9)
functions themselves. This will allow us to use the functions at the
syscall layer and elsewhere in the kernel where guaranteeing a minimum
sleep duration is of vital importance.
With input from bluhm@, guenther@, ratchov@, tedu@, and kettenis@.
Requested by mpi@ and kettenis@.
Conversion algorithm from mpi@.
ok mpi@, kettenis@, deraadt@
-rw-r--r-- | share/man/man9/tsleep.9 | 6 | ||||
-rw-r--r-- | sys/kern/kern_synch.c | 37 |
2 files changed, 30 insertions, 13 deletions
diff --git a/share/man/man9/tsleep.9 b/share/man/man9/tsleep.9 index 737a9741776..bc9b1f6bc25 100644 --- a/share/man/man9/tsleep.9 +++ b/share/man/man9/tsleep.9 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tsleep.9,v 1.13 2019/07/03 22:39:33 cheloha Exp $ +.\" $OpenBSD: tsleep.9,v 1.14 2020/01/12 00:01:12 cheloha Exp $ .\" $NetBSD: sleep.9,v 1.11 1999/03/24 06:15:12 mycroft Exp $ .\" .\" Copyright (c) 1996 The NetBSD Foundation, Inc. @@ -28,7 +28,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: July 3 2019 $ +.Dd $Mdocdate: January 12 2020 $ .Dt TSLEEP 9 .Os .Sh NAME @@ -225,7 +225,7 @@ If is equal to .Dv INFSLP these functions do not time out, -otherwise they sleep for at most +otherwise they sleep for at least .Fa nsecs nanoseconds. .Pp diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index ae6ca786eb7..5e33a94106d 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_synch.c,v 1.155 2019/11/30 11:19:17 visa Exp $ */ +/* $OpenBSD: kern_synch.c,v 1.156 2020/01/12 00:01:12 cheloha Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /* @@ -166,11 +166,30 @@ tsleep_nsec(const volatile void *ident, int priority, const char *wmesg, __func__, wmesg); } #endif - to_ticks = nsecs / (tick * 1000); + /* + * We want to sleep at least nsecs nanoseconds worth of ticks. + * + * - Clamp nsecs to prevent arithmetic overflow. + * + * - Round nsecs up to account for any nanoseconds that do not + * divide evenly into tick_nsec, otherwise we'll lose them to + * integer division in the next step. We add (tick_nsec - 1) + * to keep from introducing a spurious tick if there are no + * such nanoseconds, i.e. nsecs % tick_nsec == 0. + * + * - Divide the rounded value to a count of ticks. We divide + * by (tick_nsec + 1) to discard the extra tick introduced if, + * before rounding, nsecs % tick_nsec == 1. + * + * - Finally, add a tick to the result. We need to wait out + * the current tick before we can begin counting our interval, + * as we do not know how much time has elapsed since the + * current tick began. + */ + nsecs = MIN(nsecs, UINT64_MAX - tick_nsec); + to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1; if (to_ticks > INT_MAX) to_ticks = INT_MAX; - if (to_ticks == 0) - to_ticks = 1; return tsleep(ident, priority, wmesg, (int)to_ticks); } @@ -271,11 +290,10 @@ msleep_nsec(const volatile void *ident, struct mutex *mtx, int priority, __func__, wmesg); } #endif - to_ticks = nsecs / (tick * 1000); + nsecs = MIN(nsecs, UINT64_MAX - tick_nsec); + to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1; if (to_ticks > INT_MAX) to_ticks = INT_MAX; - if (to_ticks == 0) - to_ticks = 1; return msleep(ident, mtx, priority, wmesg, (int)to_ticks); } @@ -322,11 +340,10 @@ rwsleep_nsec(const volatile void *ident, struct rwlock *rwl, int priority, __func__, wmesg); } #endif - to_ticks = nsecs / (tick * 1000); + nsecs = MIN(nsecs, UINT64_MAX - tick_nsec); + to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1; if (to_ticks > INT_MAX) to_ticks = INT_MAX; - if (to_ticks == 0) - to_ticks = 1; return rwsleep(ident, rwl, priority, wmesg, (int)to_ticks); } |