[PATCH 14/14] selftests/timers/auxclock: Test vDSO functionality

Thomas Weißschuh posted 14 patches 3 months, 1 week ago
[PATCH 14/14] selftests/timers/auxclock: Test vDSO functionality
Posted by Thomas Weißschuh 3 months, 1 week ago
Extend the auxclock test to also cover the vDSO.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
 tools/testing/selftests/timers/auxclock.c | 95 +++++++++++++++++++++++++++++--
 1 file changed, 91 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/timers/auxclock.c b/tools/testing/selftests/timers/auxclock.c
index 0ba2f9996114ade3147f0f3aec49904556a23cd4..314037839c1c7dd32ca32722231c67bc408a2ea3 100644
--- a/tools/testing/selftests/timers/auxclock.c
+++ b/tools/testing/selftests/timers/auxclock.c
@@ -10,11 +10,16 @@
 #include <linux/timex.h>
 #include <sched.h>
 #include <stdio.h>
+#include <sys/auxv.h>
 #include <sys/syscall.h>
 #include <unistd.h>
 
 #include "../kselftest_harness.h"
 
+#include "../vDSO/parse_vdso.c"
+#include "../vDSO/vdso_config.h"
+#include "../vDSO/vdso_call.h"
+
 #ifndef CLOCK_AUX
 #define	CLOCK_AUX	16
 #endif
@@ -133,7 +138,45 @@ static int sys_clock_adjtime64(__kernel_clockid_t clockid, struct __kernel_timex
 #endif
 }
 
-FIXTURE(auxclock) {};
+FIXTURE(auxclock) {
+	int (*vdso_clock_gettime)(__kernel_clockid_t clockid, struct timespec *ts);
+	int (*vdso_clock_gettime64)(__kernel_clockid_t clockid, struct __kernel_timespec *ts);
+	int (*vdso_clock_getres)(__kernel_clockid_t clockid, struct timespec *ts);
+};
+
+static int vdso_clock_gettime64(FIXTURE_DATA(auxclock) *self, __kernel_clockid_t clockid,
+				struct __kernel_timespec *ts)
+{
+	struct timespec _ts;
+	int ret;
+
+	if (self->vdso_clock_gettime64) {
+		return VDSO_CALL(self->vdso_clock_gettime64, 2, clockid, ts);
+	} else if (self->vdso_clock_gettime) {
+		ret = VDSO_CALL(self->vdso_clock_gettime, 2, clockid, &_ts);
+		if (!ret)
+			timespec_to_kernel_timespec(&_ts, ts);
+		return ret;
+	} else {
+		return -ENOSYS;
+	}
+}
+
+static int vdso_clock_getres_time64(FIXTURE_DATA(auxclock) *self, __kernel_clockid_t clockid,
+				    struct __kernel_timespec *ts)
+{
+	struct timespec _ts;
+	int ret;
+
+	if (self->vdso_clock_getres) {
+		ret = VDSO_CALL(self->vdso_clock_getres, 2, clockid, &_ts);
+		if (!ret)
+			timespec_to_kernel_timespec(&_ts, ts);
+		return ret;
+	} else {
+		return -ENOSYS;
+	}
+}
 
 FIXTURE_VARIANT(auxclock) {
 	__kernel_clockid_t clock;
@@ -193,6 +236,18 @@ static void enter_timens(struct __test_metadata *_metadata)
 FIXTURE_SETUP(auxclock) {
 	int ret;
 
+#ifdef AT_SYSINFO_EHDR
+	unsigned long sysinfo_ehdr;
+
+	sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
+	if (sysinfo_ehdr)
+		vdso_init_from_sysinfo_ehdr(sysinfo_ehdr);
+
+	self->vdso_clock_gettime = vdso_sym(versions[VDSO_VERSION], names[VDSO_NAMES][1]);
+	self->vdso_clock_gettime64 = vdso_sym(versions[VDSO_VERSION], names[VDSO_NAMES][5]);
+	self->vdso_clock_getres = vdso_sym(versions[VDSO_VERSION], names[VDSO_NAMES][3]);
+#endif /* !AT_SYSINFO_EHDR */
+
 	ret = configure_auxclock(variant->clock, variant->clock_enabled);
 	if (ret == -ENOENT)
 		SKIP(return, "auxclocks not enabled");
@@ -220,6 +275,20 @@ TEST_F(auxclock, sys_clock_getres) {
 	ASSERT_EQ(1, ts.tv_nsec);
 }
 
+TEST_F(auxclock, vdso_clock_getres) {
+	struct __kernel_timespec ts;
+	int ret;
+
+	ret = vdso_clock_getres_time64(self, variant->clock, &ts);
+	if (ret == -ENOSYS) {
+		SKIP(return, "no clock_getres() in vDSO");
+	} else {
+		ASSERT_EQ(0, ret);
+		ASSERT_EQ(0, ts.tv_sec);
+		ASSERT_EQ(1, ts.tv_nsec);
+	}
+}
+
 TEST_F(auxclock, sys_clock_gettime) {
 	struct __kernel_timespec ts;
 	int ret;
@@ -233,6 +302,20 @@ TEST_F(auxclock, sys_clock_gettime) {
 	}
 }
 
+TEST_F(auxclock, vdso_clock_gettime) {
+	struct __kernel_timespec ts;
+	int ret;
+
+	ret = vdso_clock_gettime64(self, variant->clock, &ts);
+	if (ret == -ENOSYS) {
+		SKIP(return, "no clock_gettime() in vDSO");
+	} else if (variant->clock_enabled) {
+		ASSERT_EQ(0, ret);
+	} else {
+		ASSERT_EQ(-ENODEV, ret);
+	}
+}
+
 static void auxclock_validate_progression(struct __test_metadata *_metadata,
 					  const struct __kernel_timespec *a,
 					  const struct __kernel_timespec *b)
@@ -310,9 +393,13 @@ TEST_F(auxclock, progression) {
 		auxclock_validate_progression(_metadata, &a, &b);
 
 		memset(&a, 0, sizeof(a));
-		ret = sys_clock_gettime64(variant->clock, &a);
-		ASSERT_EQ(0, ret);
-		auxclock_validate_progression(_metadata, &b, &a);
+		ret = vdso_clock_gettime64(self, variant->clock, &a);
+		if (ret == -ENOSYS) {
+			a = b;
+		} else {
+			ASSERT_EQ(0, ret);
+			auxclock_validate_progression(_metadata, &b, &a);
+		}
 	}
 }
 

-- 
2.50.0

Re: [PATCH 14/14] selftests/timers/auxclock: Test vDSO functionality
Posted by Thomas Gleixner 3 months ago
On Tue, Jul 01 2025 at 10:58, Thomas Weißschuh wrote:

> Extend the auxclock test to also cover the vDSO.

I'm not really convinved, that this is the right thing to do. Why can't
this just extend selftests/vDSO instead of creating these

> +#include "../vDSO/parse_vdso.c"
> +#include "../vDSO/vdso_config.h"
> +#include "../vDSO/vdso_call.h"

cross directory oddities? Confused.

Thanks,

        tglx
Re: [PATCH 14/14] selftests/timers/auxclock: Test vDSO functionality
Posted by Thomas Weißschuh 3 months ago
On Sun, Jul 06, 2025 at 10:26:31PM +0200, Thomas Gleixner wrote:
> On Tue, Jul 01 2025 at 10:58, Thomas Weißschuh wrote:
> 
> > Extend the auxclock test to also cover the vDSO.
> 
> I'm not really convinved, that this is the right thing to do. Why can't
> this just extend selftests/vDSO instead of creating these
> 
> > +#include "../vDSO/parse_vdso.c"
> > +#include "../vDSO/vdso_config.h"
> > +#include "../vDSO/vdso_call.h"
> 
> cross directory oddities? Confused.

Then we'd have to duplicate the auxclock management into the vDSO selftests.
And the knowledge about the expected clock_getres() return values.

In general I think we need to introduce some reuse of the vDSO parsing code
from other selftests in any case. For example the timens selftests call the
vDSO by just invoking the libc wrappers. But the libc may not support a vDSO,
have it disabled for some reason or the vDSO itself falls back to a syscall.
I intend to introduce some vDSO accessors which force the usage of the vDSO
fastpath by using parse_vdso.c to directly call into the vDSO and seccomp to
inhibit the fallback syscalls.

Or, to avoid a dependency we could move the vDSO parsing one step up to
tools/testing/selftests/vdso_support.h, and clean up the interfaces.


Thomas
Re: [PATCH 14/14] selftests/timers/auxclock: Test vDSO functionality
Posted by Thomas Gleixner 3 months ago
On Mon, Jul 07 2025 at 09:17, Thomas Weißschuh wrote:
> On Sun, Jul 06, 2025 at 10:26:31PM +0200, Thomas Gleixner wrote:
>> On Tue, Jul 01 2025 at 10:58, Thomas Weißschuh wrote:
>> 
>> > Extend the auxclock test to also cover the vDSO.
>> 
>> I'm not really convinved, that this is the right thing to do. Why can't
>> this just extend selftests/vDSO instead of creating these
>> 
>> > +#include "../vDSO/parse_vdso.c"
>> > +#include "../vDSO/vdso_config.h"
>> > +#include "../vDSO/vdso_call.h"
>> 
>> cross directory oddities? Confused.
>
> Then we'd have to duplicate the auxclock management into the vDSO selftests.

Fair enough.

> I intend to introduce some vDSO accessors which force the usage of the vDSO
> fastpath by using parse_vdso.c to directly call into the vDSO and seccomp to
> inhibit the fallback syscalls.
>
> Or, to avoid a dependency we could move the vDSO parsing one step up to
> tools/testing/selftests/vdso_support.h, and clean up the interfaces.

Maybe that's not the worst thing as there are VDSO tests in several
places by now.

Thanks,

        tglx