Currently the vDSO selftests use the time-related types from libc. This works on glibc by chance today but will break with other libc implementations or on distributions which switch to 64-bit times everywhere.
The kernel's UAPI headers provide the proper types to use with the vDSO (and raw syscalls) but are not necessarily compatible with libc types. Introduce a new header which makes the UAPI headers compatible with the libc.
Also contains some related cleanups.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Changes in v2: - Use __kernel_old_time_t in vdso_time_t. - Add vdso_syscalls.h. - Add a test for the time() function. - Validate return value of syscall(clock_getres) in vdso_test_abi - Link to v1: https://lore.kernel.org/r/20251111-vdso-test-types-v1-0-03b31f88c659@linutro...
--- Thomas Weißschuh (14): Revert "selftests: vDSO: parse_vdso: Use UAPI headers instead of libc headers" selftests: vDSO: Introduce vdso_types.h selftests: vDSO: Introduce vdso_syscalls.h selftests: vDSO: vdso_test_gettimeofday: Remove nolibc checks selftests: vDSO: vdso_test_gettimeofday: Use types from vdso_types.h selftests: vDSO: vdso_test_abi: Use types from vdso_types.h selftests: vDSO: vdso_test_abi: Validate return value of syscall(clock_getres) selftests: vDSO: vdso_test_abi: Use system call wrappers from vdso_syscalls.h selftests: vDSO: vdso_test_correctness: Drop SYS_getcpu fallbacks selftests: vDSO: vdso_test_correctness: Make ts_leq() and tv_leq() more generic selftests: vDSO: vdso_test_correctness: Use types from vdso_types.h selftests: vDSO: vdso_test_correctness: Use system call wrappers from vdso_syscalls.h selftests: vDSO: vdso_test_correctness: Use facilities from parse_vdso.c selftests: vDSO: vdso_test_correctness: Add a test for time()
tools/testing/selftests/vDSO/Makefile | 6 +- tools/testing/selftests/vDSO/parse_vdso.c | 3 +- tools/testing/selftests/vDSO/vdso_syscalls.h | 93 ++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 46 +++-- .../testing/selftests/vDSO/vdso_test_correctness.c | 190 +++++++++++---------- .../selftests/vDSO/vdso_test_gettimeofday.c | 9 +- tools/testing/selftests/vDSO/vdso_types.h | 70 ++++++++ 7 files changed, 285 insertions(+), 132 deletions(-) --- base-commit: 1b2eb8c1324859864f4aa79dc3cfbb2f7ef5c524 change-id: 20251110-vdso-test-types-68ce0c712b79
Best regards,
This reverts commit c9fbaa879508 ("selftests: vDSO: parse_vdso: Use UAPI headers instead of libc headers")
The kernel headers were used to make parse_vdso.c compatible with nolibc. Unfortunately linux/elf.h is incompatible with glibc's sys/auxv.h. When using glibc it is therefore not possible build parse_vdso.c as part of the same compilation unit as its caller as sys/auxv.h is needed for getauxval().
In the meantime nolibc gained its own elf.h, providing compatibility with the documented libc interfaces.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/Makefile | 2 -- tools/testing/selftests/vDSO/parse_vdso.c | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index f94ea07cddc97ea8b13d8bed2b35a32bb99a2423..74dfc60e636edce91cc1df9643ca8aa008ecfe65 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -19,8 +19,6 @@ endif
include ../lib.mk
-CFLAGS += $(TOOLS_INCLUDES) - CFLAGS_NOLIBC := -nostdlib -nostdinc -ffreestanding -fno-asynchronous-unwind-tables \ -fno-stack-protector -include $(top_srcdir)/tools/include/nolibc/nolibc.h \ -I$(top_srcdir)/tools/include/nolibc/ $(KHDR_INCLUDES) diff --git a/tools/testing/selftests/vDSO/parse_vdso.c b/tools/testing/selftests/vDSO/parse_vdso.c index 3ff00fb624a44b964cc54954f1f088cabe11a901..c6ff4413ea367ae57bc6a60073314b29f938c99d 100644 --- a/tools/testing/selftests/vDSO/parse_vdso.c +++ b/tools/testing/selftests/vDSO/parse_vdso.c @@ -19,8 +19,7 @@ #include <stdint.h> #include <string.h> #include <limits.h> -#include <linux/auxvec.h> -#include <linux/elf.h> +#include <elf.h>
#include "parse_vdso.h"
Currently the vDSO selftests use the time-related types from libc. This works on glibc by chance today but will break with other libc implementations or on distributions which switch to 64-bit times everywhere.
The kernel's UAPI headers provide the proper types to use with the vDSO (and raw syscalls) but are not necessarily compatible with libc types. Introduce a new header which makes the UAPI headers compatible with the libc.
Tested with glibc, musl and nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_types.h | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+)
diff --git a/tools/testing/selftests/vDSO/vdso_types.h b/tools/testing/selftests/vDSO/vdso_types.h new file mode 100644 index 0000000000000000000000000000000000000000..75f2b108ed0fb6956d05e2498879658608aa2f51 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_types.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de, Linutronix GmbH + * + * Types to use with vDSO functions. + * + * The types used by the vDSO functions do not necessarily match the ones used + * by libc. The kernel's UAPI headers do provide definitions for those types + * but are often not compatible with libc which applications need to use. + * As the types differ between platforms listing them manually is a lot of work + * and error prone. Instead hack around the incompatibilities of the libc and + * UAPI headers and use the UAPI types. + */ +#ifndef __VDSO_TYPES_H__ +#define __VDSO_TYPES_H__ + +/* + * Include the headers from libc first to not override any of its types later. + */ +#include <time.h> +#include <sys/time.h> + +/* + * Avoid collisions. + */ +#define timeval kernel_timeval_moved +#define itimerspec kernel_itimerspec_moved +#define itimerval kernel_itimerval_moved +#define timezone kernel_timezone_moved + +/* + * Get the UAPI types. + */ +#include <linux/time.h> + +#undef timeval +#undef itimerspec +#undef itimerval +#undef timezone + +/* + * The UAPI headers do not provide their own 'struct __kernel_timezone'. + * 'struct timezone' is the one from libc. + */ +struct kernel_timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +#include <linux/version.h> + +/* + * UAPI headers from the libc may be older and not provide these. + */ +#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE +typedef __kernel_long_t __kernel_old_time_t; + +struct __kernel_old_timespec { + __kernel_old_time_t tv_sec; + long tv_nsec; +}; +#endif + +typedef long (*vdso_gettimeofday_t)(struct __kernel_old_timeval *tv, struct kernel_timezone *tz); +typedef long (*vdso_clock_gettime_t)(__kernel_clockid_t clk_id, struct __kernel_old_timespec *ts); +typedef long (*vdso_clock_gettime64_t)(__kernel_clockid_t clk_id, struct __kernel_timespec *ts); +typedef long (*vdso_clock_getres_t)(__kernel_clockid_t clk_id, struct __kernel_old_timespec *ts); +typedef __kernel_old_time_t (*vdso_time_t)(__kernel_old_time_t *t); + +#endif /* __VDSO_TYPES_H__ */
The vDSO selftests use raw system call wrapper to validate the correctness of the vDSO implementation. The exactly available system calls differ between architectures and kernel configurations. Raw system calls should not use libc types as these are not necessarily compatible.
Introduce a helper header which uses the correct types and fallbacks.
Link: https://lore.kernel.org/lkml/29dd9e11-9ae8-415a-acb3-b96af56550b0@app.fastma... Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_syscalls.h | 93 ++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+)
diff --git a/tools/testing/selftests/vDSO/vdso_syscalls.h b/tools/testing/selftests/vDSO/vdso_syscalls.h new file mode 100644 index 0000000000000000000000000000000000000000..1419f8dd3ea831beaf582c47f6acf2ce5d5d12f8 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_syscalls.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de, Linutronix GmbH + * + * System call wrappers to use for vDSO testing. + * + * vDSO calls are expected to return the same data as the equivalent system call. + * To ensure this the tests need to trigger system calls. Calling into libc may + * silently use the vDSO, so explicit system calls are necessary. + * Not all system calls are available on all platforms, so some fallback logic + * is needed. Use __NR_ constants from the kernel's UAPI headers over SYS_ from + * ones from libc to avoid any potential interference from libc. + * Always prefer the 64-bit time variants of the system calls as 32-bit ones + * may not be present on the platform or disabled in the kernel configuration. + */ +#ifndef __VDSO_SYSCALLS_H__ +#define __VDSO_SYSCALLS_H__ + +#include "vdso_types.h" + +#include <stddef.h> +#include <sys/syscall.h> +#include <linux/unistd.h> + +#define typeof_member(T, m) typeof(((T*)0)->m) +#define sizeof_member(T, m) sizeof(typeof_member(T, m)) + +/* + * To keep the fallback logic simple we assume that although the types between + * the wrapper and the system call are different they are compatible. + * Validate that assumption. + * On x32 tv_nsec of __kernel_old_timespec is smaller than the one from + * __kernel_timespec. This is fine, as only the lower 4 bytes are relevant and + * it is a little-endian architecture. + */ +#define ASSERT_TIMESPEC_COMPATIBLE(T1, T2) \ + do { \ + _Static_assert(sizeof(T2) == sizeof(T2)); \ + _Static_assert(offsetof(T1, tv_sec) == offsetof(T2, tv_sec)); \ + _Static_assert(sizeof_member(T1, tv_sec) == sizeof_member(T2, tv_sec)); \ + _Static_assert(offsetof(T1, tv_nsec) == offsetof(T2, tv_nsec)); \ + _Static_assert(sizeof_member(T1, tv_nsec) == sizeof_member(T2, tv_nsec) || \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && \ + sizeof_member(T1, tv_nsec) > sizeof_member(T2, tv_nsec))); \ + } while(0) + +static inline +int sys_clock_getres(__kernel_clockid_t clock, struct __kernel_timespec *ts) +{ +#ifdef __NR_clock_getres_time64 + return syscall(__NR_clock_getres_time64, clock, ts); +#else + ASSERT_TIMESPEC_COMPATIBLE(typeof(*ts), struct __kernel_old_timespec); + return syscall(__NR_clock_getres, clock, ts); +#endif +} + +static inline +int sys_clock_gettime(__kernel_clockid_t clock, struct __kernel_timespec *ts) +{ +#ifdef __NR_clock_gettime64 + return syscall(__NR_clock_gettime64, clock, ts); +#else + ASSERT_TIMESPEC_COMPATIBLE(typeof(*ts), struct __kernel_old_timespec); + return syscall(__NR_clock_gettime, clock, ts); +#endif +} + +static inline +int sys_gettimeofday(struct __kernel_old_timeval *tv, struct kernel_timezone *tz) +{ +#ifdef __NR_gettimeofday + return syscall(__NR_gettimeofday, tv, tz); +#else + /* Architectures with vdso_gettimeofday() also have __NR_gettimeofday */ + errno = ENOSYS; + return -1; +#endif +} + +static inline +__kernel_old_time_t sys_time(__kernel_old_time_t *tloc) +{ +#ifdef __NR_time + return syscall(__NR_time, tloc); +#else + /* Architectures with vdso_time() also have __NR_time */ + errno = ENOSYS; + return -1; +#endif +} + +#endif /* __VDSO_SYSCALLS_H__ */
On Thu, Nov 13, 2025, at 16:30, Thomas Weißschuh wrote:
The vDSO selftests use raw system call wrapper to validate the correctness of the vDSO implementation. The exactly available system calls differ between architectures and kernel configurations. Raw system calls should not use libc types as these are not necessarily compatible.
Introduce a helper header which uses the correct types and fallbacks.
After I looked at how these are used in patch 8, I think it's much easier to just always use the same types as the kernel interfaces here and skip the type mangling entirely:
+static inline +int sys_clock_getres(__kernel_clockid_t clock, struct __kernel_timespec *ts) +{ +#ifdef __NR_clock_getres_time64
- return syscall(__NR_clock_getres_time64, clock, ts);
+#else
- ASSERT_TIMESPEC_COMPATIBLE(typeof(*ts), struct __kernel_old_timespec);
- return syscall(__NR_clock_getres, clock, ts);
+#endif +}
__NR_clock_getres and vdso_clock_getres() both always return a __kernel_old_timespec, so I now think it's best to return that from sys_clock_getres() without the __NR_clock_getres_time64 alternative here and not worry about whether that is a 32-bit or 64-bit type,
I should have thought this through better in my comments to the previous version.
In kernels without CONFIG_COMPAT_32BIT_TIME, we currently leave out the clock_getres/clock_gettime/gettimeofday/time syscalls, but still provide the vdso interfaces. For consistency we should probably leave out both syscall and vdso in that configuration, and then we also don't need to compare the vdso_getres result against sys_getres_time64.
+static inline +int sys_clock_gettime(__kernel_clockid_t clock, struct __kernel_timespec *ts) +{ +#ifdef __NR_clock_gettime64
- return syscall(__NR_clock_gettime64, clock, ts);
+#else
- ASSERT_TIMESPEC_COMPATIBLE(typeof(*ts), struct __kernel_old_timespec);
- return syscall(__NR_clock_gettime, clock, ts);
+#endif +}
Same here.
+static inline +int sys_gettimeofday(struct __kernel_old_timeval *tv, struct kernel_timezone *tz) +{ +#ifdef __NR_gettimeofday
- return syscall(__NR_gettimeofday, tv, tz);
+#else
- /* Architectures with vdso_gettimeofday() also have __NR_gettimeofday
*/
- errno = ENOSYS;
- return -1;
+#endif +}
+static inline +__kernel_old_time_t sys_time(__kernel_old_time_t *tloc) +{ +#ifdef __NR_time
- return syscall(__NR_time, tloc);
+#else
- /* Architectures with vdso_time() also have __NR_time */
- errno = ENOSYS;
- return -1;
+#endif +}
These both look good to me.
Arnd
On Fri, Nov 14, 2025 at 09:13:02AM +0100, Arnd Bergmann wrote:
On Thu, Nov 13, 2025, at 16:30, Thomas Weißschuh wrote:
The vDSO selftests use raw system call wrapper to validate the correctness of the vDSO implementation. The exactly available system calls differ between architectures and kernel configurations. Raw system calls should not use libc types as these are not necessarily compatible.
Introduce a helper header which uses the correct types and fallbacks.
After I looked at how these are used in patch 8, I think it's much easier to just always use the same types as the kernel interfaces here and skip the type mangling entirely:
Please see below.
+static inline +int sys_clock_getres(__kernel_clockid_t clock, struct __kernel_timespec *ts) +{ +#ifdef __NR_clock_getres_time64
- return syscall(__NR_clock_getres_time64, clock, ts);
+#else
- ASSERT_TIMESPEC_COMPATIBLE(typeof(*ts), struct __kernel_old_timespec);
- return syscall(__NR_clock_getres, clock, ts);
+#endif +}
__NR_clock_getres and vdso_clock_getres() both always return a __kernel_old_timespec, so I now think it's best to return that from sys_clock_getres() without the __NR_clock_getres_time64 alternative here and not worry about whether that is a 32-bit or 64-bit type,
I should have thought this through better in my comments to the previous version.
In kernels without CONFIG_COMPAT_32BIT_TIME, we currently leave out the clock_getres/clock_gettime/gettimeofday/time syscalls, but still provide the vdso interfaces. For consistency we should probably leave out both syscall and vdso in that configuration, and then we also don't need to compare the vdso_getres result against sys_getres_time64.
That sounds good. But today no vDSO provides clock_getres_time64, so removing clock_getres from the vDSO will affect users. So we will end up with some sort of inconsistency in any case. While I agree that it would be nice if the type mangling was unnecessary, I prefer to correctly test what we have today. If we decide to simplify the vDSO itself then we have working tests.
sys_clock_gettime() should probably be called sys_clock_gettime64(), as that is what it actually is.
FYI: gettimeoday() seems to be available even in kernels without CONFIG_COMPAT_32BIT_TIME.
Thomas
On Fri, Nov 14, 2025, at 09:48, Thomas Weißschuh wrote:
On Fri, Nov 14, 2025 at 09:13:02AM +0100, Arnd Bergmann wrote:
On Thu, Nov 13, 2025, at 16:30, Thomas Weißschuh wrote:
__NR_clock_getres and vdso_clock_getres() both always return a __kernel_old_timespec, so I now think it's best to return that from sys_clock_getres() without the __NR_clock_getres_time64 alternative here and not worry about whether that is a 32-bit or 64-bit type,
I should have thought this through better in my comments to the previous version.
In kernels without CONFIG_COMPAT_32BIT_TIME, we currently leave out the clock_getres/clock_gettime/gettimeofday/time syscalls, but still provide the vdso interfaces. For consistency we should probably leave out both syscall and vdso in that configuration, and then we also don't need to compare the vdso_getres result against sys_getres_time64.
That sounds good. But today no vDSO provides clock_getres_time64, so removing clock_getres from the vDSO will affect users.
In what way? When we introduced the clock_gettime64() vdso call, we debated also adding time64(), gettimeofday_time64() and clock_getres_time64() but decided against that based on the argument that the libc can implement all of these efficiently with just clock_gettime64().
If you think that clock_getres_time64() is important, I don't mind changing that, especially now that we have a shared vdso for all architectures. The arguments here is a bit different, since an efficient clock_getres() function in libc requires caching the values in userspace, while an efficient gettimeofday() is much simpler, by calling vdso_clock_gettime_time64()
So we will end up with some sort of inconsistency in any case. While I agree that it would be nice if the type mangling was unnecessary, I prefer to correctly test what we have today. If we decide to simplify the vDSO itself then we have working tests.
Sorry, I'm not following. Do you mean we need the mangling since we support the vdso for configurations without the direct syscalls, or do you mean something else?
I don't think we can actually build a full userspace (other than nolibc) that works with CONFIG_COMPAT_32BIT_TIME=n, so I'm not particularly worried about testing the vdso for that case. You already skip testing vdso_time() if sys_time() is unavailable, and I think we can do it the exact same way for all five vdso calls.
sys_clock_gettime() should probably be called sys_clock_gettime64(), as that is what it actually is.
That also seems wrong, as there is no clock_gettime64 on 64-bit architectures, only clock_gettime.
FYI: gettimeoday() seems to be available even in kernels without CONFIG_COMPAT_32BIT_TIME.
I see, that does sound like a mistake. It's relatively harmless, but I think it would be safe to change this along with changing the vdso to only expose the time32 interfaces when COMPAT_32BIT_TIME is enabled.
Arnd
On Fri, Nov 14, 2025 at 10:16:01AM +0100, Arnd Bergmann wrote:
On Fri, Nov 14, 2025, at 09:48, Thomas Weißschuh wrote:
On Fri, Nov 14, 2025 at 09:13:02AM +0100, Arnd Bergmann wrote:
On Thu, Nov 13, 2025, at 16:30, Thomas Weißschuh wrote:
__NR_clock_getres and vdso_clock_getres() both always return a __kernel_old_timespec, so I now think it's best to return that from sys_clock_getres() without the __NR_clock_getres_time64 alternative here and not worry about whether that is a 32-bit or 64-bit type,
I should have thought this through better in my comments to the previous version.
In kernels without CONFIG_COMPAT_32BIT_TIME, we currently leave out the clock_getres/clock_gettime/gettimeofday/time syscalls, but still provide the vdso interfaces. For consistency we should probably leave out both syscall and vdso in that configuration, and then we also don't need to compare the vdso_getres result against sys_getres_time64.
That sounds good. But today no vDSO provides clock_getres_time64, so removing clock_getres from the vDSO will affect users.
In what way? When we introduced the clock_gettime64() vdso call, we debated also adding time64(), gettimeofday_time64() and clock_getres_time64() but decided against that based on the argument that the libc can implement all of these efficiently with just clock_gettime64().
clock_getres_time64() can't be implemented with vdso_clock_gettime64(). It could use vdso_clock_getres() as the resolution should never overflow the type. But nobody seems to do this either.
If you think that clock_getres_time64() is important, I don't mind changing that, especially now that we have a shared vdso for all architectures. The arguments here is a bit different, since an efficient clock_getres() function in libc requires caching the values in userspace, while an efficient gettimeofday() is much simpler, by calling vdso_clock_gettime_time64()
I don't think it is important. For my SPARC vDSO series I even dropped the regular clock_getres() after your request. But because it doesn't exist we need to handle the presence of vdso_clock_getres() and the simultaneous absence of sys_clock_getres() in the test.
So we will end up with some sort of inconsistency in any case. While I agree that it would be nice if the type mangling was unnecessary, I prefer to correctly test what we have today. If we decide to simplify the vDSO itself then we have working tests.
Sorry, I'm not following. Do you mean we need the mangling since we support the vdso for configurations without the direct syscalls, or do you mean something else?
Exactly.
I don't think we can actually build a full userspace (other than nolibc) that works with CONFIG_COMPAT_32BIT_TIME=n, so I'm not particularly worried about testing the vdso for that case.
musl 1.2 started to always use 64-bit times. Looking at both the musl and glibc code, they always try the 64-bit variant first. I think they should work fine.
Personally I'd like to have tests for the functionality that exists. Even if there are currently no users.
You already skip testing vdso_time() if sys_time() is unavailable, and I think we can do it the exact same way for all five vdso calls.
That was an oversight.
sys_clock_gettime() should probably be called sys_clock_gettime64(), as that is what it actually is.
That also seems wrong, as there is no clock_gettime64 on 64-bit architectures, only clock_gettime.
I referred to the type that it returns, which is always 64-bit. Another name, without the sys_ prefix, would be better.
FYI: gettimeoday() seems to be available even in kernels without CONFIG_COMPAT_32BIT_TIME.
I see, that does sound like a mistake. It's relatively harmless, but I think it would be safe to change this along with changing the vdso to only expose the time32 interfaces when COMPAT_32BIT_TIME is enabled.
IMO that would need to be another series with its own discussion.
Thomas
On Fri, Nov 14, 2025, at 11:02, Thomas Weißschuh wrote:
On Fri, Nov 14, 2025 at 10:16:01AM +0100, Arnd Bergmann wrote:
On Fri, Nov 14, 2025, at 09:48, Thomas Weißschuh wrote: If you think that clock_getres_time64() is important, I don't mind changing that, especially now that we have a shared vdso for all architectures. The arguments here is a bit different, since an efficient clock_getres() function in libc requires caching the values in userspace, while an efficient gettimeofday() is much simpler, by calling vdso_clock_gettime_time64()
I don't think it is important. For my SPARC vDSO series I even dropped the regular clock_getres() after your request. But because it doesn't exist we need to handle the presence of vdso_clock_getres() and the simultaneous absence of sys_clock_getres() in the test.
But that is the other way round, right? On sparc32 we have (optionally) sys_clock_getres() but never vdso_clock_getres().
I don't think we can actually build a full userspace (other than nolibc) that works with CONFIG_COMPAT_32BIT_TIME=n, so I'm not particularly worried about testing the vdso for that case.
musl 1.2 started to always use 64-bit times. Looking at both the musl and glibc code, they always try the 64-bit variant first. I think they should work fine.
No, musl only uses the time64 syscalls when it actually passes a 64-bit time value, but e.g. still uses __NR_futex instead of __NR_futex_time64 when waiting for a futex without a timeout, and it uses __NR_clock_settime instead of __NR_clock_settime_time64 when setting a time within the 32-bit time_t range (1902..2037).
Personally I'd like to have tests for the functionality that exists. Even if there are currently no users.
You already skip testing vdso_time() if sys_time() is unavailable, and I think we can do it the exact same way for all five vdso calls.
That was an oversight.
Ok. So you'd want to check all the time32 and time64 vdso calls against the __kernel_timespec values returned from sys_clock_get{res_time64,time64} and their 64-bit equivalents?
I think in this case we have to actually address the inconsistency in the rounding between the interfaces, which I don't think is well documented and possibly differs across implementations.
As far as I can tell, gettimeofday() always returns the CLOCK_REALTIME value rounded down to full microseconds and truncated to signed 'long' seconds, while time() returns the CLOCK_REALTIME_COARSE value rounded down to full seconds. This can be a second earlier than a previous CLOCK_REALTIME value.
I see that glibc's time() function uses CLOCK_REALTIME_COARSE to be consistent with the Linux sys_time() and vdso_time(), while musl's time() uses CLOCK_REALTIME for consistency with gettimeofday() and sensible user expectations.
sys_clock_gettime() should probably be called sys_clock_gettime64(), as that is what it actually is.
That also seems wrong, as there is no clock_gettime64 on 64-bit architectures, only clock_gettime.
I referred to the type that it returns, which is always 64-bit. Another name, without the sys_ prefix, would be better.
Right, but then I would make it return 'struct timespec', not 'struct __kernel_timespec', because it's no longer the kernel interface.
FYI: gettimeoday() seems to be available even in kernels without CONFIG_COMPAT_32BIT_TIME.
I see, that does sound like a mistake. It's relatively harmless, but I think it would be safe to change this along with changing the vdso to only expose the time32 interfaces when COMPAT_32BIT_TIME is enabled.
IMO that would need to be another series with its own discussion.
Sure.
Arnd
nolibc now provides these headers, making the check unnecessary.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_gettimeofday.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index 9ce795b806f0992b83cef78c7e16fac0e54750da..636a56ccf8e4e7943ca446fe3fad6897598ca77f 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -11,10 +11,8 @@ */
#include <stdio.h> -#ifndef NOLIBC #include <sys/auxv.h> #include <sys/time.h> -#endif
#include "../kselftest.h" #include "parse_vdso.h"
The libc types are not necessarily compatible with the vDSO functions.
Use the dedicated types from vdso_types.h instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_gettimeofday.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index 636a56ccf8e4e7943ca446fe3fad6897598ca77f..3c14ed654428e0dbe08e9d44671e82737ac61486 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -12,12 +12,12 @@
#include <stdio.h> #include <sys/auxv.h> -#include <sys/time.h>
#include "../kselftest.h" #include "parse_vdso.h" #include "vdso_config.h" #include "vdso_call.h" +#include "vdso_types.h"
int main(int argc, char **argv) { @@ -33,15 +33,14 @@ int main(int argc, char **argv) vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
/* Find gettimeofday. */ - typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); - gtod_t gtod = (gtod_t)vdso_sym(version, name[0]); + vdso_gettimeofday_t gtod = (vdso_gettimeofday_t)vdso_sym(version, name[0]);
if (!gtod) { printf("Could not find %s\n", name[0]); return KSFT_SKIP; }
- struct timeval tv; + struct __kernel_old_timeval tv; long ret = VDSO_CALL(gtod, 2, &tv, 0);
if (ret == 0) {
The libc types are not necessarily compatible with the vDSO functions.
Use the dedicated types from vdso_types.h instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_abi.c | 32 +++++++++------------------- 1 file changed, 10 insertions(+), 22 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 238d609a457a281d802734b40d6a2c35ba7f6d72..7826d9c10ccaf313dc003e8959d9eb84b2cda874 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -11,9 +11,7 @@ #include <stdint.h> #include <elf.h> #include <stdio.h> -#include <time.h> #include <sys/auxv.h> -#include <sys/time.h> #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> @@ -21,23 +19,12 @@ #include "../kselftest.h" #include "vdso_config.h" #include "vdso_call.h" +#include "vdso_types.h" #include "parse_vdso.h"
static const char *version; static const char **name;
-/* The same as struct __kernel_timespec */ -struct vdso_timespec64 { - uint64_t tv_sec; - uint64_t tv_nsec; -}; - -typedef long (*vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz); -typedef long (*vdso_clock_gettime_t)(clockid_t clk_id, struct timespec *ts); -typedef long (*vdso_clock_gettime64_t)(clockid_t clk_id, struct vdso_timespec64 *ts); -typedef long (*vdso_clock_getres_t)(clockid_t clk_id, struct timespec *ts); -typedef time_t (*vdso_time_t)(time_t *t); - static const char * const vdso_clock_name[] = { [CLOCK_REALTIME] = "CLOCK_REALTIME", [CLOCK_MONOTONIC] = "CLOCK_MONOTONIC", @@ -65,7 +52,7 @@ static void vdso_test_gettimeofday(void) return; }
- struct timeval tv; + struct __kernel_old_timeval tv; long ret = VDSO_CALL(vdso_gettimeofday, 2, &tv, 0);
if (ret == 0) { @@ -77,7 +64,7 @@ static void vdso_test_gettimeofday(void) } }
-static void vdso_test_clock_gettime64(clockid_t clk_id) +static void vdso_test_clock_gettime64(__kernel_clockid_t clk_id) { /* Find clock_gettime64. */ vdso_clock_gettime64_t vdso_clock_gettime64 = @@ -90,7 +77,7 @@ static void vdso_test_clock_gettime64(clockid_t clk_id) return; }
- struct vdso_timespec64 ts; + struct __kernel_timespec ts; long ret = VDSO_CALL(vdso_clock_gettime64, 2, clk_id, &ts);
if (ret == 0) { @@ -104,7 +91,7 @@ static void vdso_test_clock_gettime64(clockid_t clk_id) } }
-static void vdso_test_clock_gettime(clockid_t clk_id) +static void vdso_test_clock_gettime(__kernel_clockid_t clk_id) { /* Find clock_gettime. */ vdso_clock_gettime_t vdso_clock_gettime = @@ -117,7 +104,7 @@ static void vdso_test_clock_gettime(clockid_t clk_id) return; }
- struct timespec ts; + struct __kernel_old_timespec ts; long ret = VDSO_CALL(vdso_clock_gettime, 2, clk_id, &ts);
if (ret == 0) { @@ -154,7 +141,7 @@ static void vdso_test_time(void) } }
-static void vdso_test_clock_getres(clockid_t clk_id) +static void vdso_test_clock_getres(__kernel_clockid_t clk_id) { int clock_getres_fail = 0;
@@ -169,7 +156,8 @@ static void vdso_test_clock_getres(clockid_t clk_id) return; }
- struct timespec ts, sys_ts; + struct __kernel_old_timespec ts; + struct timespec sys_ts; long ret = VDSO_CALL(vdso_clock_getres, 2, clk_id, &ts);
if (ret == 0) { @@ -200,7 +188,7 @@ static void vdso_test_clock_getres(clockid_t clk_id) * This function calls vdso_test_clock_gettime and vdso_test_clock_getres * with different values for clock_id. */ -static inline void vdso_test_clock(clockid_t clock_id) +static inline void vdso_test_clock(__kernel_clockid_t clock_id) { ksft_print_msg("clock_id: %s\n", vdso_clock_name[clock_id]);
If the system call failed the output memory is not usable.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_abi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 7826d9c10ccaf313dc003e8959d9eb84b2cda874..ff142b3b9075b18e60a46b77492d285e9937fa71 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -168,12 +168,15 @@ static void vdso_test_clock_getres(__kernel_clockid_t clk_id) }
ret = syscall(SYS_clock_getres, clk_id, &sys_ts); + if (ret == 0) { + ksft_print_msg("The syscall resolution is %lld %lld\n", + (long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec);
- ksft_print_msg("The syscall resolution is %lld %lld\n", - (long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec); - - if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) + if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) + clock_getres_fail++; + } else { clock_getres_fail++; + }
if (clock_getres_fail > 0) { ksft_test_result_fail("%s %s\n", name[3],
Using syscall(SYS_getcpu) is problematic for the reasons outlined in vdso_syscalls.h.
Use the wrappers from the utility header instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_abi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index ff142b3b9075b18e60a46b77492d285e9937fa71..5a1bd0c2742ae14053d826c8d9f3bdc1018d91f3 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -19,6 +19,7 @@ #include "../kselftest.h" #include "vdso_config.h" #include "vdso_call.h" +#include "vdso_syscalls.h" #include "vdso_types.h" #include "parse_vdso.h"
@@ -157,7 +158,7 @@ static void vdso_test_clock_getres(__kernel_clockid_t clk_id) }
struct __kernel_old_timespec ts; - struct timespec sys_ts; + struct __kernel_timespec sys_ts; long ret = VDSO_CALL(vdso_clock_getres, 2, clk_id, &ts);
if (ret == 0) { @@ -167,7 +168,7 @@ static void vdso_test_clock_getres(__kernel_clockid_t clk_id) clock_getres_fail++; }
- ret = syscall(SYS_clock_getres, clk_id, &sys_ts); + ret = sys_clock_getres(clk_id, &sys_ts); if (ret == 0) { ksft_print_msg("The syscall resolution is %lld %lld\n", (long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec);
These fallbacks are only valid on x86 and unused in the first place.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/vdso_test_correctness.c | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index da651cf53c6ca4242085de109c7fc57bd807297c..66cd1d4c19872e78c0e608f5e0fb5215cf902b50 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -25,14 +25,6 @@
static const char **name;
-#ifndef SYS_getcpu -# ifdef __x86_64__ -# define SYS_getcpu 309 -# else -# define SYS_getcpu 318 -# endif -#endif - #ifndef __NR_clock_gettime64 #define __NR_clock_gettime64 403 #endif
Upcoming changes will shuffle around the timespec and timeval types. To keep ts_leq() and tv_leq() working with all of those types, transform them into macros.
Also drop ts64_leq() as it can now be replaced by the regular ts_leq().
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- .../testing/selftests/vDSO/vdso_test_correctness.c | 47 ++++++++++------------ 1 file changed, 22 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 66cd1d4c19872e78c0e608f5e0fb5215cf902b50..5a127fc93e7368cbe8d9fb4a4482a6a735ffd46c 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -213,30 +213,27 @@ static void test_getcpu(void) } }
-static bool ts_leq(const struct timespec *a, const struct timespec *b) -{ - if (a->tv_sec != b->tv_sec) - return a->tv_sec < b->tv_sec; - else - return a->tv_nsec <= b->tv_nsec; -} - -static bool ts64_leq(const struct __kernel_timespec *a, - const struct __kernel_timespec *b) -{ - if (a->tv_sec != b->tv_sec) - return a->tv_sec < b->tv_sec; - else - return a->tv_nsec <= b->tv_nsec; -} - -static bool tv_leq(const struct timeval *a, const struct timeval *b) -{ - if (a->tv_sec != b->tv_sec) - return a->tv_sec < b->tv_sec; - else - return a->tv_usec <= b->tv_usec; -} +#define ts_leq(_a, _b) ({ \ + bool _ret; \ + \ + if ((_a)->tv_sec != (_b)->tv_sec) \ + _ret = (_a)->tv_sec < (_b)->tv_sec; \ + else \ + _ret = (_a)->tv_nsec <= (_b)->tv_nsec; \ + \ + _ret; \ +}) + +#define tv_leq(_a, _b) ({ \ + bool _ret; \ + \ + if ((_a)->tv_sec != (_b)->tv_sec) \ + _ret = (_a)->tv_sec < (_b)->tv_sec; \ + else \ + _ret = (_a)->tv_usec <= (_b)->tv_usec; \ + \ + _ret; \ +})
static char const * const clocknames[] = { [0] = "CLOCK_REALTIME", @@ -352,7 +349,7 @@ static void test_one_clock_gettime64(int clock, const char *name) (unsigned long long)vdso.tv_sec, vdso.tv_nsec, (unsigned long long)end.tv_sec, end.tv_nsec);
- if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) { + if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) { printf("[FAIL]\tTimes are out of sequence\n"); nerrs++; return;
The libc types are not necessarily compatible with the vDSO functions.
Use the dedicated types from vdso_types.h instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- .../testing/selftests/vDSO/vdso_test_correctness.c | 38 ++++++++-------------- 1 file changed, 13 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 5a127fc93e7368cbe8d9fb4a4482a6a735ffd46c..276446d24f55d6b28910320e3d601bac501c2ca1 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -7,7 +7,6 @@ #define _GNU_SOURCE
#include <stdio.h> -#include <sys/time.h> #include <time.h> #include <stdlib.h> #include <unistd.h> @@ -21,6 +20,7 @@
#include "vdso_config.h" #include "vdso_call.h" +#include "vdso_types.h" #include "../kselftest.h"
static const char **name; @@ -29,29 +29,14 @@ static const char **name; #define __NR_clock_gettime64 403 #endif
-#ifndef __kernel_timespec -struct __kernel_timespec { - long long tv_sec; - long long tv_nsec; -}; -#endif - /* max length of lines in /proc/self/maps - anything longer is skipped here */ #define MAPS_LINE_LEN 128
int nerrs = 0;
-typedef int (*vgettime_t)(clockid_t, struct timespec *); - -vgettime_t vdso_clock_gettime; - -typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *); - -vgettime64_t vdso_clock_gettime64; - -typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz); - -vgtod_t vdso_gettimeofday; +vdso_clock_gettime_t vdso_clock_gettime; +vdso_clock_gettime64_t vdso_clock_gettime64; +vdso_gettimeofday_t vdso_gettimeofday;
typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
@@ -124,17 +109,17 @@ static void fill_function_pointers(void)
vgetcpu = (getcpu_t) vsyscall_getcpu();
- vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]); + vdso_clock_gettime = (vdso_clock_gettime_t)dlsym(vdso, name[1]); if (!vdso_clock_gettime) printf("Warning: failed to find clock_gettime in vDSO\n");
#if defined(VDSO_32BIT) - vdso_clock_gettime64 = (vgettime64_t)dlsym(vdso, name[5]); + vdso_clock_gettime64 = (vdso_clock_gettime64_t)dlsym(vdso, name[5]); if (!vdso_clock_gettime64) printf("Warning: failed to find clock_gettime64 in vDSO\n"); #endif
- vdso_gettimeofday = (vgtod_t)dlsym(vdso, name[0]); + vdso_gettimeofday = (vdso_gettimeofday_t)dlsym(vdso, name[0]); if (!vdso_gettimeofday) printf("Warning: failed to find gettimeofday in vDSO\n");
@@ -252,7 +237,8 @@ static char const * const clocknames[] = {
static void test_one_clock_gettime(int clock, const char *name) { - struct timespec start, vdso, end; + struct __kernel_old_timespec vdso; + struct timespec start, end; int vdso_ret, end_ret;
printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock); @@ -376,8 +362,10 @@ static void test_clock_gettime64(void)
static void test_gettimeofday(void) { - struct timeval start, vdso, end; - struct timezone sys_tz, vdso_tz; + struct __kernel_old_timeval vdso; + struct kernel_timezone vdso_tz; + struct timeval start, end; + struct timezone sys_tz; int vdso_ret, end_ret;
if (!vdso_gettimeofday)
Using syscall() is problematic for the reasons outlined in vdso_syscalls.h.
Use the wrappers from the utility header instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- .../testing/selftests/vDSO/vdso_test_correctness.c | 34 ++++++---------------- 1 file changed, 9 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 276446d24f55d6b28910320e3d601bac501c2ca1..ac5fa3e906806c28d3238e6f4e767e370932c5d1 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -20,6 +20,7 @@
#include "vdso_config.h" #include "vdso_call.h" +#include "vdso_syscalls.h" #include "vdso_types.h" #include "../kselftest.h"
@@ -131,21 +132,6 @@ static long sys_getcpu(unsigned * cpu, unsigned * node, return syscall(__NR_getcpu, cpu, node, cache); }
-static inline int sys_clock_gettime(clockid_t id, struct timespec *ts) -{ - return syscall(__NR_clock_gettime, id, ts); -} - -static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts) -{ - return syscall(__NR_clock_gettime64, id, ts); -} - -static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz) -{ - return syscall(__NR_gettimeofday, tv, tz); -} - static void test_getcpu(void) { printf("[RUN]\tTesting getcpu...\n"); @@ -237,8 +223,8 @@ static char const * const clocknames[] = {
static void test_one_clock_gettime(int clock, const char *name) { + struct __kernel_timespec start, end; struct __kernel_old_timespec vdso; - struct timespec start, end; int vdso_ret, end_ret;
printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock); @@ -269,9 +255,9 @@ static void test_one_clock_gettime(int clock, const char *name) }
printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n", - (unsigned long long)start.tv_sec, start.tv_nsec, - (unsigned long long)vdso.tv_sec, vdso.tv_nsec, - (unsigned long long)end.tv_sec, end.tv_nsec); + (unsigned long long)start.tv_sec, (unsigned long)start.tv_nsec, + (unsigned long long)vdso.tv_sec, (unsigned long)vdso.tv_nsec, + (unsigned long long)end.tv_sec, (unsigned long)end.tv_nsec);
if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) { printf("[FAIL]\tTimes are out of sequence\n"); @@ -305,7 +291,7 @@ static void test_one_clock_gettime64(int clock, const char *name)
printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
- if (sys_clock_gettime64(clock, &start) < 0) { + if (sys_clock_gettime(clock, &start) < 0) { if (errno == EINVAL) { vdso_ret = VDSO_CALL(vdso_clock_gettime64, 2, clock, &vdso); if (vdso_ret == -EINVAL) { @@ -321,7 +307,7 @@ static void test_one_clock_gettime64(int clock, const char *name) }
vdso_ret = VDSO_CALL(vdso_clock_gettime64, 2, clock, &vdso); - end_ret = sys_clock_gettime64(clock, &end); + end_ret = sys_clock_gettime(clock, &end);
if (vdso_ret != 0 || end_ret != 0) { printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", @@ -362,10 +348,8 @@ static void test_clock_gettime64(void)
static void test_gettimeofday(void) { - struct __kernel_old_timeval vdso; - struct kernel_timezone vdso_tz; - struct timeval start, end; - struct timezone sys_tz; + struct __kernel_old_timeval start, vdso, end; + struct kernel_timezone vdso_tz, sys_tz; int vdso_ret, end_ret;
if (!vdso_gettimeofday)
On Thu, Nov 13, 2025, at 16:30, Thomas Weißschuh wrote:
static void test_one_clock_gettime(int clock, const char *name) {
- struct __kernel_timespec start, end; struct __kernel_old_timespec vdso;
struct timespec start, end; int vdso_ret, end_ret;
printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name,
This looks confusing to me: I can see that the existing code is wrong because it passes a (libc-defined) timespec into the sys_clock_gettime() that expects __kernel_old_timespec.
It looks like you are changing a sometimes-wrong type into a consistently wrong type, but then you also change the sys_clock_gettime() definition to return __kernel_timespec instead of __kernel_old_timespec, so it ends up working.
Why not always use __kernel_old_timespec for the local variables and the sys_clock_gettime() calls here?
@@ -305,7 +291,7 @@ static void test_one_clock_gettime64(int clock, const char *name)
printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
- if (sys_clock_gettime64(clock, &start) < 0) {
- if (sys_clock_gettime(clock, &start) < 0) { if (errno == EINVAL) { vdso_ret = VDSO_CALL(vdso_clock_gettime64, 2, clock, &vdso); if (vdso_ret == -EINVAL) {
This looks like the correct fix to me. We were already uses the same types for syscall and vdso paths, and now the types match the interface.
Arnd
The soname from the vDSO is not a public API. Furthermore it requires libc to implement dlsym() and friends.
Use the facilities from parse_vdso.c instead which uses the official vDSO ABI to find it, aligned with the other vDSO selftests.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/selftests/vDSO/Makefile | 4 +-- .../testing/selftests/vDSO/vdso_test_correctness.c | 30 ++++++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 74dfc60e636edce91cc1df9643ca8aa008ecfe65..504b30155ae7c6da3065c4472384f54e5547332a 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -26,13 +26,11 @@ CFLAGS_NOLIBC := -nostdlib -nostdinc -ffreestanding -fno-asynchronous-unwind-tab $(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c +$(OUTPUT)/vdso_test_correctness: parse_vdso.c vdso_test_correctness.c
$(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c | headers $(OUTPUT)/vdso_standalone_test_x86: CFLAGS:=$(CFLAGS_NOLIBC) $(CFLAGS)
-$(OUTPUT)/vdso_test_correctness: vdso_test_correctness.c -$(OUTPUT)/vdso_test_correctness: LDFLAGS += -ldl - $(OUTPUT)/vdso_test_getrandom: parse_vdso.c $(OUTPUT)/vdso_test_getrandom: CFLAGS += -isystem $(top_srcdir)/tools/include \ $(KHDR_INCLUDES) \ diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index ac5fa3e906806c28d3238e6f4e767e370932c5d1..310688e1379511e2c564b460c6379cc00b7a5f9a 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -10,20 +10,22 @@ #include <time.h> #include <stdlib.h> #include <unistd.h> +#include <sys/auxv.h> #include <sys/syscall.h> -#include <dlfcn.h> #include <string.h> #include <errno.h> #include <sched.h> #include <stdbool.h> #include <limits.h>
+#include "parse_vdso.h" #include "vdso_config.h" #include "vdso_call.h" #include "vdso_syscalls.h" #include "vdso_types.h" #include "../kselftest.h"
+static const char *version; static const char **name;
#ifndef __NR_clock_gettime64 @@ -88,39 +90,32 @@ static void *vsyscall_getcpu(void)
static void fill_function_pointers(void) { - void *vdso = dlopen("linux-vdso.so.1", - RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); - if (!vdso) - vdso = dlopen("linux-gate.so.1", - RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); - if (!vdso) - vdso = dlopen("linux-vdso32.so.1", - RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); - if (!vdso) - vdso = dlopen("linux-vdso64.so.1", - RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); - if (!vdso) { + unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + + if (!sysinfo_ehdr) { printf("[WARN]\tfailed to find vDSO\n"); return; }
- vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]); + vdso_init_from_sysinfo_ehdr(sysinfo_ehdr); + + vdso_getcpu = (getcpu_t)vdso_sym(version, name[4]); if (!vdso_getcpu) printf("Warning: failed to find getcpu in vDSO\n");
vgetcpu = (getcpu_t) vsyscall_getcpu();
- vdso_clock_gettime = (vdso_clock_gettime_t)dlsym(vdso, name[1]); + vdso_clock_gettime = (vdso_clock_gettime_t)vdso_sym(version, name[1]); if (!vdso_clock_gettime) printf("Warning: failed to find clock_gettime in vDSO\n");
#if defined(VDSO_32BIT) - vdso_clock_gettime64 = (vdso_clock_gettime64_t)dlsym(vdso, name[5]); + vdso_clock_gettime64 = (vdso_clock_gettime64_t)vdso_sym(version, name[5]); if (!vdso_clock_gettime64) printf("Warning: failed to find clock_gettime64 in vDSO\n"); #endif
- vdso_gettimeofday = (vdso_gettimeofday_t)dlsym(vdso, name[0]); + vdso_gettimeofday = (vdso_gettimeofday_t)vdso_sym(version, name[0]); if (!vdso_gettimeofday) printf("Warning: failed to find gettimeofday in vDSO\n");
@@ -398,6 +393,7 @@ static void test_gettimeofday(void)
int main(int argc, char **argv) { + version = versions[VDSO_VERSION]; name = (const char **)&names[VDSO_NAMES];
fill_function_pointers();
Extend the test to also cover the time() function.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- .../testing/selftests/vDSO/vdso_test_correctness.c | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 310688e1379511e2c564b460c6379cc00b7a5f9a..67cb8f11aa3c6977616def286b9a7d35fa6579e7 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -40,6 +40,7 @@ int nerrs = 0; vdso_clock_gettime_t vdso_clock_gettime; vdso_clock_gettime64_t vdso_clock_gettime64; vdso_gettimeofday_t vdso_gettimeofday; +vdso_time_t vdso_time;
typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
@@ -119,6 +120,10 @@ static void fill_function_pointers(void) if (!vdso_gettimeofday) printf("Warning: failed to find gettimeofday in vDSO\n");
+ vdso_time = (vdso_time_t)vdso_sym(version, name[2]); + if (!vdso_time) + printf("Warning: failed to find time in vDSO\n"); + }
static long sys_getcpu(unsigned * cpu, unsigned * node, @@ -391,6 +396,49 @@ static void test_gettimeofday(void) VDSO_CALL(vdso_gettimeofday, 2, &vdso, NULL); }
+static void test_time(void) +{ + __kernel_old_time_t start, end, vdso_ret, vdso_param; + + if (!vdso_time) + return; + + printf("[RUN]\tTesting time...\n"); + + if (sys_time(&start) < 0) { + printf("[FAIL]\tsys_time failed (%d)\n", errno); + nerrs++; + return; + } + + vdso_ret = VDSO_CALL(vdso_time, 1, &vdso_param); + end = sys_time(NULL); + + if (vdso_ret < 0 || end < 0) { + printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", + (int)vdso_ret, errno); + nerrs++; + return; + } + + printf("\t%lld %lld %lld\n", + (long long)start, + (long long)vdso_ret, + (long long)end); + + if (vdso_ret != vdso_param) { + printf("[FAIL]\tinconsistent return values: %lld %lld\n", + (long long)vdso_ret, (long long)vdso_param); + nerrs++; + return; + } + + if (!(start <= vdso_ret) || !(vdso_ret <= end)) { + printf("[FAIL]\tTimes are out of sequence\n"); + nerrs++; + } +} + int main(int argc, char **argv) { version = versions[VDSO_VERSION]; @@ -401,6 +449,7 @@ int main(int argc, char **argv) test_clock_gettime(); test_clock_gettime64(); test_gettimeofday(); + test_time();
/* * Test getcpu() last so that, if something goes wrong setting affinity,
linux-kselftest-mirror@lists.linaro.org