On Tue, Nov 11, 2025 at 04:07:58PM +0100, Arnd Bergmann wrote:
> On Tue, Nov 11, 2025, at 15:46, Thomas Weißschuh wrote:
> > On Tue, Nov 11, 2025 at 03:19:00PM +0100, Arnd Bergmann wrote:

(...)

> >> >> If we are trying to validate the interface here, we should probably
> >> >> call both if available. If we just want to know the result and
> >> >> trust that both work correctly, I'd always use __NR_clock_getres_time64
> >> >> on 32-bit systems and __NR_clock_getres on 64-bit systems.
> >> >
> >> > As these are vDSO and not timer selftests I think we trust the syscalls.
> >> > But how do we detect a native 64-bit time system? The preprocessor 
> >> > builtins
> >> > won't work as a 32-bit pointer system may use 64-bit time syscalls. I am 
> >> > not
> >> > aware of the UAPI #define, beyond the absence of 
> >> > __NR_clock_getres_time64.
> >> 
> >> I would just check __BITS_PER_LONG=32 and require a linux-5.6+ kernel
> >> at runtime to ensure the 64-bit calls are available.
> >
> > That doesn't work for x32. It uses __BITS_PER_LONG but does not have
> > __NR_clock_getres_time64.
> 
> Right. In C code, we can usually check for
> 'sizeof(time_t) > sizeof(__kernel_long_t)' to catch that case,
> but that doesn't work as easily with the preprocessor.
> 
> A more complex setup using both compile-time and run-time fallbacks
> would work, e.g.
> 
> static int
> syscall_clock_getres_old(clockid_t clockid, struct timespec *res);
> #ifdef __NR_clock_getres
>        struct __kernel_old_timespec ts_old;
>        ret = syscall(__NR_clock_getres, clockid, &ts_old);
>        if (ret)
>               return ret;
>        res->tv_sec = ts_old.sec;
>        res->tv_nsec = ts_old.tv_nsec;
>        return 0;
> #else
>        return -ENOSYS;
> #endif
> }
> 
> static int
> syscall_clock_getres_time64(clockid_t clockid, struct timespec *res);
> #ifdef __NR_clock_getres_time64
>        struct __kernel_timespec ts_64;
>        ret = syscall(__NR_clock_getres_time64, clockid, &ts_64);
>        if (ret)
>               return ret;
>        res->tv_sec = ts_64.sec;
>        res->tv_nsec = ts_64.tv_nsec;
>        return 0;
> #else
>        return -ENOSYS;
> #endif
> }
> 
> static int
> syscall_clock_getres(clockid_t clockid, struct timespec *res)
> {
>        ret = syscall_clock_getres_time64(clockid, res);
>        if (ret != -ENOSYS)
>               return ret;
>        return syscall_clock_getres_old(clockid, &ts_old);
> }

This is exactly what I tried to avoid.

> but the simpler check falling back to the 'old' version
> is probably sufficient.

On musl there is no SYS_clock_getres_time64 but instead there is
SYS_clock_getres_time32. Switching to __NR gives us back the more natural
fallback logic. We are back at the nolibc 64-bit time functions.
We can add a static_assert() to gain some compile-time safety.

static int
syscall_clock_getres(__kernel_clockid_t clockid, struct __kernel_timespec *res)
{
#ifdef __NR_clock_getres_time64
        return syscall(__NR_clock_getres_time64, clockid, res);
#else
        /*
         * __NR_clock_getres expects __kernel_old_timespec.
         * Make sure the actual parameter is compatible.
         */
        _Static_assert(sizeof(struct __kernel_old_timespec) == sizeof(*res));
        return syscall(__NR_clock_getres, clockid, res);
#endif
}

And stick all of those wrappers into another helper header.


Thomas

Reply via email to