diff options
author | Justin T. Gibbs <gibbs@FreeBSD.org> | 2016-03-12 23:02:53 +0000 |
---|---|---|
committer | Justin T. Gibbs <gibbs@FreeBSD.org> | 2016-03-12 23:02:53 +0000 |
commit | 5405e7e2ee49d15d34cd0ce4584ddab9ef43ee2a (patch) | |
tree | e8fdbe2776ce60793d24c1590fcbc4ae1d7989ed /sys/kern/kern_event.c | |
parent | 823590d40e5c27ebae445ab8d67fa4b38ee48b2c (diff) | |
download | src-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.c | 59 |
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 |