aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_event.c
diff options
context:
space:
mode:
authorJustin T. Gibbs <gibbs@FreeBSD.org>2016-03-12 23:02:53 +0000
committerJustin T. Gibbs <gibbs@FreeBSD.org>2016-03-12 23:02:53 +0000
commit5405e7e2ee49d15d34cd0ce4584ddab9ef43ee2a (patch)
treee8fdbe2776ce60793d24c1590fcbc4ae1d7989ed /sys/kern/kern_event.c
parent823590d40e5c27ebae445ab8d67fa4b38ee48b2c (diff)
downloadsrc-5405e7e2ee49d15d34cd0ce4584ddab9ef43ee2a.tar.gz
src-5405e7e2ee49d15d34cd0ce4584ddab9ef43ee2a.zip
Provide high precision conversion from ns,us,ms -> sbintime in kevent
In timer2sbintime(), calculate the second and fractional second portions of the sbintime separately. When calculating the the fractional second portion, use a 64bit multiply to prevent excess truncation. This avoids the ~7% error in the original conversion for ns, and smaller errors of the same type for us and ms. PR: 198139 Reviewed by: jhb MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D5397
Notes
Notes: svn path=/head/; revision=296775
Diffstat (limited to 'sys/kern/kern_event.c')
-rw-r--r--sys/kern/kern_event.c59
1 files changed, 42 insertions, 17 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 0b6f7daeb513..5abba169fa41 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -564,34 +564,59 @@ knote_fork(struct knlist *list, int pid)
#define NOTE_TIMER_PRECMASK (NOTE_SECONDS|NOTE_MSECONDS|NOTE_USECONDS| \
NOTE_NSECONDS)
-static __inline sbintime_t
+static sbintime_t
timer2sbintime(intptr_t data, int flags)
{
- sbintime_t modifier;
+ /*
+ * Macros for converting to the fractional second portion of an
+ * sbintime_t using 64bit multiplication to improve precision.
+ */
+#define NS_TO_SBT(ns) (((ns) * (((uint64_t)1 << 63) / 500000000)) >> 32)
+#define US_TO_SBT(us) (((us) * (((uint64_t)1 << 63) / 500000)) >> 32)
+#define MS_TO_SBT(ms) (((ms) * (((uint64_t)1 << 63) / 500)) >> 32)
switch (flags & NOTE_TIMER_PRECMASK) {
case NOTE_SECONDS:
- modifier = SBT_1S;
- break;
+#ifdef __LP64__
+ if (data > (SBT_MAX / SBT_1S))
+ return SBT_MAX;
+#endif
+ return ((sbintime_t)data << 32);
case NOTE_MSECONDS: /* FALLTHROUGH */
case 0:
- modifier = SBT_1MS;
- break;
+ if (data >= 1000) {
+ int64_t secs = data / 1000;
+#ifdef __LP64__
+ if (secs > (SBT_MAX / SBT_1S))
+ return SBT_MAX;
+#endif
+ return (secs << 32 | MS_TO_SBT(data % 1000));
+ }
+ return MS_TO_SBT(data);
case NOTE_USECONDS:
- modifier = SBT_1US;
- break;
+ if (data >= 1000000) {
+ int64_t secs = data / 1000000;
+#ifdef __LP64__
+ if (secs > (SBT_MAX / SBT_1S))
+ return SBT_MAX;
+#endif
+ return (secs << 32 | US_TO_SBT(data % 1000000));
+ }
+ return US_TO_SBT(data);
case NOTE_NSECONDS:
- modifier = SBT_1NS;
- break;
- default:
- return (-1);
- }
-
+ if (data >= 1000000000) {
+ int64_t secs = data / 1000000000;
#ifdef __LP64__
- if (data > SBT_MAX / modifier)
- return (SBT_MAX);
+ if (secs > (SBT_MAX / SBT_1S))
+ return SBT_MAX;
#endif
- return (modifier * data);
+ return (secs << 32 | US_TO_SBT(data % 1000000000));
+ }
+ return NS_TO_SBT(data);
+ default:
+ break;
+ }
+ return (-1);
}
static void