summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcheloha <cheloha@cvs.openbsd.org>2020-01-12 00:01:13 +0000
committercheloha <cheloha@cvs.openbsd.org>2020-01-12 00:01:13 +0000
commitf74a383c8bf01aae4a7f66e18dd8161466f94af4 (patch)
tree4840b7b2af534b98192efd1c22d3dabd393e2c62
parentf5c72763bd065929fc5b8419bb0db158b05791cd (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.96
-rw-r--r--sys/kern/kern_synch.c37
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);
}