clock_gettime: add time64 syscall support, decouple 32-bit time_t
authorRich Felker <dalias@aerifal.cx>
Wed, 31 Jul 2019 04:26:16 +0000 (00:26 -0400)
committerRich Felker <dalias@aerifal.cx>
Fri, 2 Aug 2019 04:08:23 +0000 (00:08 -0400)
the time64 syscall has to be used if time_t is 64-bit, since there's
no way of knowing before making a syscall whether the result will fit
in 32 bits, and the 32-bit syscalls do not report overflow as an
error.

on 64-bit archs, there is no change to the code after preprocessing.
on current 32-bit archs, the result is now read from the kernel
through long[2] array, then copied into the timespec, to remove the
assumption that time_t is the same as long.

vdso clock_gettime is still used in place of a syscall if available.
32-bit archs with 64-bit time_t must use the time64 version of the
vdso function; if it's not available, performance will significantly
suffer. support for both vdso functions could be added, but would
break the ability to move a long-lived process from a pre-time64
kernel to one that can outlast Y2038 with checkpoint/resume, at least
without added hacks to identify that the 32-bit function is no longer
usable and stop using it (e.g. by seeing negative tv_sec). this
possibility may be explored in future work on the function.

src/time/clock_gettime.c

index 8fd1b8f..4608375 100644 (file)
@@ -40,6 +40,24 @@ int __clock_gettime(clockid_t clk, struct timespec *ts)
        }
 #endif
 
+#ifdef SYS_clock_gettime64
+       if (sizeof(time_t) > 4)
+               r = __syscall(SYS_clock_gettime64, clk, ts);
+       if (SYS_clock_gettime == SYS_clock_gettime64 || r!=-ENOSYS)
+               return __syscall_ret(r);
+       long ts32[2];
+       r = __syscall(SYS_clock_gettime, clk, ts32);
+       if (r==-ENOSYS && clk==CLOCK_REALTIME) {
+               r = __syscall(SYS_gettimeofday, ts32, 0);
+               ts32[1] *= 1000;
+       }
+       if (!r) {
+               ts->tv_sec = ts32[0];
+               ts->tv_nsec = ts32[1];
+               return r;
+       }
+       return __syscall_ret(r);
+#else
        r = __syscall(SYS_clock_gettime, clk, ts);
        if (r == -ENOSYS) {
                if (clk == CLOCK_REALTIME) {
@@ -50,6 +68,7 @@ int __clock_gettime(clockid_t clk, struct timespec *ts)
                r = -EINVAL;
        }
        return __syscall_ret(r);
+#endif
 }
 
 weak_alias(__clock_gettime, clock_gettime);