Author: gibbs
Date: Sat Mar 12 23:02:53 2016
New Revision: 296775
URL: https://svnweb.freebsd.org/changeset/base/296775

Log:
  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

Modified:
  head/sys/kern/kern_event.c

Modified: head/sys/kern/kern_event.c
==============================================================================
--- head/sys/kern/kern_event.c  Sat Mar 12 22:55:07 2016        (r296774)
+++ head/sys/kern/kern_event.c  Sat Mar 12 23:02:53 2016        (r296775)
@@ -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
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to