clock: Fix up all the clock 1 handling
authorAlan Cox <alan@linux.intel.com>
Fri, 2 Jan 2015 20:39:02 +0000 (20:39 +0000)
committerAlan Cox <alan@linux.intel.com>
Fri, 2 Jan 2015 20:39:02 +0000 (20:39 +0000)
We can't do things simply because clock 1 runs at 10HZ (as from the last
kernel fixes). Instead try and avoid divides, and also pull a few cunning
tricks for fixed division by ten functions

Library/libs/Makefile
Library/libs/__hz.c [deleted file]
Library/libs/clock_getres.c
Library/libs/clock_gettime.c
Library/libs/nanosleep.c
Library/libs/sleep.c

index 57762f2..28acf62 100644 (file)
@@ -12,7 +12,7 @@ SRC_CRT0 = crt0.s
 OBJ_CRT0 = $(SRC_CRT0:.s=.rel)
 SRC_ASM =
 OBJ_ASM = $(SRC_ASM:.s=.rel)
-SRC_C =  __hz.c abort.c asctime.c assert.c atexit.c
+SRC_C =  abort.c asctime.c assert.c atexit.c
 SRC_C += bcmp.c bcopy.c bsearch.c bzero.c calloc.c cfree.c clock.c closedir.c
 SRC_C += clock_gettime.c clock_getres.c clock_settime.c
 SRC_C += creat.c crypt.c ctime.c ctype.c difftime.c errno.c error.c execl.c execv.c
diff --git a/Library/libs/__hz.c b/Library/libs/__hz.c
deleted file mode 100644 (file)
index 171f4a2..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <unistd.h>
-#include <time.h>
-
-static int hz;
-
-int __hz(void)
-{
-  if (!hz) {
-    struct _uzisysinfoblk info;
-    _uname(&info, sizeof(info));
-    hz = info.ticks;
-  }
-  return hz;
-}
-
-  
index c5bc700..d893807 100644 (file)
@@ -4,11 +4,14 @@
 
 int clock_getres(clockid_t clk_id, struct timespec *res)
 {
-  res->tv_nsec = 0;
-  res->tv_sec = 1;
   switch(clk_id) {
   case CLOCK_REALTIME:
+    res->tv_nsec = 0;
+    res->tv_sec = 1;
+    return 0;
   case CLOCK_MONOTONIC:
+    res->tv_nsec = 100000000UL;
+    res->tv_sec = 0;
     return 0;
   default:
     errno = EINVAL;
index 6b60576..08053bf 100644 (file)
@@ -2,8 +2,29 @@
 #include <unistd.h>
 #include <errno.h>
 
+/* Divide *tp by 10 and also return the remainder */
+/* On processors with real idiv we should probably just use that */
+unsigned long div10quickm(unsigned long *tp)
+{
+       unsigned long q, r, t;
+       t = *tp;
+       q = (t >> 1) + (t >> 2);
+       q = q + (q >> 4);
+       q = q + (q >> 8);
+       q = q + (q >> 16);
+       q >>= 3;
+       r = t - (((q << 2) + q) << 1);
+       t = q + (r >> 9);
+       *tp = t;
+       t <<= 1;
+       t = (t << 2) + t;
+       return *tp - t;
+}
+
 int clock_gettime(clockid_t clk_id, struct timespec *res)
 {
+  int r;
+  unsigned long d;
   res->tv_nsec = 0;
   switch(clk_id) {
   case CLOCK_REALTIME:
@@ -11,6 +32,12 @@ int clock_gettime(clockid_t clk_id, struct timespec *res)
     return 0;
   case CLOCK_MONOTONIC:
     _time(&res->tv_sec, 1);
+    d = res->tv_sec;
+    /* We know that this wraps at 2^32 ticks which also means we know
+       it'll fit 32bits */
+    r = div10quickm(&d);
+    res->tv_nsec = 100000UL * r;
+    res->tv_sec = d;
     return 0;
   default:
     errno = EINVAL;
index c85e599..daeeb2d 100644 (file)
@@ -5,38 +5,61 @@
 /* Ok so this doesn't score too highly on accuracy but it'll wrap code using
    the modern API's for long sleeps just fine */
 
-int clock_nanosleep(clockid_t clock, int flags, const struct timespec *req, struct timespec *rem)
+int clock_nanosleep(clockid_t clock, int flags, const struct timespec *reqp, struct timespec *rem)
 {
-  time_t tbase;
-  time_t tend;
-  uint16_t t = req->tv_sec * 10;
-  long nsec = req->tv_nsec;
+  struct timespec tbase, tend, req;
+  uint16_t t;
 
-  if (clock != CLOCK_REALTIME && clock != CLOCK_MONOTONIC) {
-   errno = EINVAL;
+  if (clock_gettime(clock, &tbase))
    return -1;
-  }
-
-  /* Will be 0-9 range so avoid costly divides */
-  while(nsec > 50000000UL) {
-    nsec -= 50000000UL;
-    t++;
-  }
  
-  if (_time(&tbase, clock) == -1)
-    return -1;
+  req.tv_sec = reqp->tv_sec;
+  req.tv_nsec = reqp->tv_nsec;
 
+  /* Absolute time means we are handed a time relative to system 0 not
+     a time relative to 'now' */
   if (flags & TIMER_ABSTIME) {
-   if (tbase < req->tv_sec)
-    return 0;
-   t += (tbase - req->tv_sec) * 10;
+    req.tv_sec -= tbase.tv_sec;
+    req.tv_nsec -= tbase.tv_nsec;
+    if (req.tv_nsec < 0) {
+     req.tv_nsec += 1000000L;
+     --req.tv_sec;
+    }
+    if (req.tv_sec < 0)
+     return 0;
+    /* req is now the relative time for all cases */
+  }
+  /* Convert duration into 16bit 10ths of a second */
+  t = ((uint16_t)req.tv_sec) * 10;
+  while(req.tv_nsec > 100000000UL) {
+    req.tv_nsec -= 100000000UL;
+    t++;
   }
-  if (_pause(t) == 0)
+  if (t == 0 || _pause(t) == 0)
     return 0;
-  _time(&tend, clock);
-  if (rem) {
-    rem->tv_sec = tbase + t/10 - tend;
-    rem->tv_nsec = 0;
+  /* When did we finish */
+  clock_gettime(clock, &tend);
+
+  /* Work out the time left avoiding divisions */
+  if (!(flags  & TIMER_ABSTIME)) {
+    tend.tv_sec -= tbase.tv_sec;
+    tend.tv_nsec -= tbase.tv_nsec;
+    if (tend.tv_nsec < 0) {
+     tend.tv_sec--;
+     tend.tv_nsec += 1000000000UL;
+   }
+   if (tend.tv_sec < 0)
+      return 0;
+   if (rem) {
+     rem->tv_sec = tend.tv_sec;
+     rem->tv_nsec = tend.tv_nsec;
+   }
+  } else {
+    /* In absolute mode rem is req, easy as that */
+    if (rem) {
+      rem->tv_sec = reqp->tv_sec;
+      rem->tv_nsec = reqp->tv_nsec;
+    }
   }
   return -1;
 }
index 6a94d6f..4fe3505 100644 (file)
@@ -5,13 +5,27 @@
 #include <signal.h>
 #include <syscalls.h>
 
+/* Divide by ten in shifts. Would be nice if the compiler did that for us 8)
+
+   FIXME: probably worth having a Z80 asm version of this */
+static div10quicki(unsigned int i)
+{
+       unsigned int q, r;
+       q = (i >> 1) + (i >> 2);
+       q = q + (q >> 4);
+       q = q + (q >> 8);
+       q >>= 3;
+       r = i - (((q << 2) + q) << 1);
+       return q + (r >> 9);
+}
+
 unsigned int sleep(unsigned int seconds)
 {
        time_t end, now;
-       _time(&end, 1);
-       end += seconds;
+       _time(&end, 1); /* in 1/10ths */
+       end += seconds * 10;
        if (_pause(seconds * 10) == 0)
                return 0;
        _time(&now, 1);
-       return (int)(end - now);
+       return div10quicki(end - now);
 }