From 40723419f4079d0c7de98d0f3149db915557b55a Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 26 Oct 2020 11:49:41 +0000 Subject: kselftest: Enable vDSO test on non x86 platforms Currently the vDSO tests are built only on x86 platforms and cannot be cross compiled. Enable vDSO TARGET for all the platforms. Future patches will extend the tests. Cc: Shuah Khan Signed-off-by: Vincenzo Frascino Acked-by: Thomas Gleixner Signed-off-by: Shuah Khan --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/vDSO/Makefile | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index d9c283503159..2dd60070d775 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -65,6 +65,7 @@ endif TARGETS += tmpfs TARGETS += tpm2 TARGETS += user +TARGETS += vDSO TARGETS += vm TARGETS += x86 TARGETS += zram diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 0069f2f83f86..47031f3c5f3a 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -9,7 +9,6 @@ ifeq ($(ARCH),x86) TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 endif -ifndef CROSS_COMPILE CFLAGS := -std=gnu99 CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector ifeq ($(CONFIG_X86_32),y) @@ -24,4 +23,3 @@ $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c vdso_standalone_test_x86.c parse_vdso.c \ -o $@ -endif -- cgit v1.2.3 From 693f5ca08ca0767b407b7ca634dbf1b783676ec3 Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 26 Oct 2020 11:49:42 +0000 Subject: kselftest: Extend vDSO selftest The current version of the multiarch vDSO selftest verifies only gettimeofday. Extend the vDSO selftest to the other library functions: - time - clock_getres - clock_gettime The extension has been used to verify the unified vdso library on the supported architectures. Cc: Shuah Khan Signed-off-by: Vincenzo Frascino Acked-by: Thomas Gleixner Signed-off-by: Shuah Khan --- tools/testing/selftests/vDSO/Makefile | 2 + tools/testing/selftests/vDSO/vdso_config.h | 90 ++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 244 +++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 tools/testing/selftests/vDSO/vdso_config.h create mode 100644 tools/testing/selftests/vDSO/vdso_test_abi.c (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 47031f3c5f3a..2ac7448bd414 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -5,6 +5,7 @@ uname_M := $(shell uname -m 2>/dev/null || echo not) ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu +TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi ifeq ($(ARCH),x86) TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 endif @@ -18,6 +19,7 @@ endif all: $(TEST_GEN_PROGS) $(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_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ vdso_standalone_test_x86.c parse_vdso.c \ diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h new file mode 100644 index 000000000000..eeb725df6045 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_config.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * vdso_config.h: Configuration options for vDSO tests. + * Copyright (c) 2019 Arm Ltd. + */ +#ifndef __VDSO_CONFIG_H__ +#define __VDSO_CONFIG_H__ + +/* + * Each architecture exports its vDSO implementation with different names + * and a different version from the others, so we need to handle it as a + * special case. + */ +#if defined(__arm__) +#define VDSO_VERSION 0 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#elif defined(__aarch64__) +#define VDSO_VERSION 3 +#define VDSO_NAMES 0 +#elif defined(__powerpc__) +#define VDSO_VERSION 1 +#define VDSO_NAMES 0 +#define VDSO_32BIT 1 +#elif defined(__powerpc64__) +#define VDSO_VERSION 1 +#define VDSO_NAMES 0 +#elif defined (__s390__) +#define VDSO_VERSION 2 +#define VDSO_NAMES 0 +#define VDSO_32BIT 1 +#elif defined (__s390X__) +#define VDSO_VERSION 2 +#define VDSO_NAMES 0 +#elif defined(__mips__) +#define VDSO_VERSION 0 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#elif defined(__sparc__) +#define VDSO_VERSION 0 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#elif defined(__i386__) +#define VDSO_VERSION 0 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#elif defined(__x86_64__) +#define VDSO_VERSION 0 +#define VDSO_NAMES 1 +#elif defined(__riscv__) +#define VDSO_VERSION 5 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#else /* nds32 */ +#define VDSO_VERSION 4 +#define VDSO_NAMES 1 +#define VDSO_32BIT 1 +#endif + +static const char *versions[6] = { + "LINUX_2.6", + "LINUX_2.6.15", + "LINUX_2.6.29", + "LINUX_2.6.39", + "LINUX_4", + "LINUX_4.15", +}; + +static const char *names[2][5] = { + { + "__kernel_gettimeofday", + "__kernel_clock_gettime", + "__kernel_time", + "__kernel_clock_getres", +#if defined(VDSO_32BIT) + "__kernel_clock_gettime64", +#endif + }, + { + "__vdso_gettimeofday", + "__vdso_clock_gettime", + "__vdso_time", + "__vdso_clock_getres", +#if defined(VDSO_32BIT) + "__vdso_clock_gettime64", +#endif + }, +}; + +#endif /* __VDSO_CONFIG_H__ */ diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c new file mode 100644 index 000000000000..3d603f1394af --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vdso_full_test.c: Sample code to test all the timers. + * Copyright (c) 2019 Arm Ltd. + * + * Compile with: + * gcc -std=gnu99 vdso_full_test.c parse_vdso.c + * + */ + +#include +#include +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include + +#include "../kselftest.h" +#include "vdso_config.h" + +extern void *vdso_sym(const char *version, const char *name); +extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); +extern void vdso_init_from_auxv(void *auxv); + +static const char *version; +static const char **name; + +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_getres_t)(clockid_t clk_id, struct timespec *ts); +typedef time_t (*vdso_time_t)(time_t *t); + +static int vdso_test_gettimeofday(void) +{ + /* Find gettimeofday. */ + vdso_gettimeofday_t vdso_gettimeofday = + (vdso_gettimeofday_t)vdso_sym(version, name[0]); + + if (!vdso_gettimeofday) { + printf("Could not find %s\n", name[0]); + return KSFT_SKIP; + } + + struct timeval tv; + long ret = vdso_gettimeofday(&tv, 0); + + if (ret == 0) { + printf("The time is %lld.%06lld\n", + (long long)tv.tv_sec, (long long)tv.tv_usec); + } else { + printf("%s failed\n", name[0]); + return KSFT_FAIL; + } + + return KSFT_PASS; +} + +static int vdso_test_clock_gettime(clockid_t clk_id) +{ + /* Find clock_gettime. */ + vdso_clock_gettime_t vdso_clock_gettime = + (vdso_clock_gettime_t)vdso_sym(version, name[1]); + + if (!vdso_clock_gettime) { + printf("Could not find %s\n", name[1]); + return KSFT_SKIP; + } + + struct timespec ts; + long ret = vdso_clock_gettime(clk_id, &ts); + + if (ret == 0) { + printf("The time is %lld.%06lld\n", + (long long)ts.tv_sec, (long long)ts.tv_nsec); + } else { + printf("%s failed\n", name[1]); + return KSFT_FAIL; + } + + return KSFT_PASS; +} + +static int vdso_test_time(void) +{ + /* Find time. */ + vdso_time_t vdso_time = + (vdso_time_t)vdso_sym(version, name[2]); + + if (!vdso_time) { + printf("Could not find %s\n", name[2]); + return KSFT_SKIP; + } + + long ret = vdso_time(NULL); + + if (ret > 0) { + printf("The time in hours since January 1, 1970 is %lld\n", + (long long)(ret / 3600)); + } else { + printf("%s failed\n", name[2]); + return KSFT_FAIL; + } + + return KSFT_PASS; +} + +static int vdso_test_clock_getres(clockid_t clk_id) +{ + /* Find clock_getres. */ + vdso_clock_getres_t vdso_clock_getres = + (vdso_clock_getres_t)vdso_sym(version, name[3]); + + if (!vdso_clock_getres) { + printf("Could not find %s\n", name[3]); + return KSFT_SKIP; + } + + struct timespec ts, sys_ts; + long ret = vdso_clock_getres(clk_id, &ts); + + if (ret == 0) { + printf("The resolution is %lld %lld\n", + (long long)ts.tv_sec, (long long)ts.tv_nsec); + } else { + printf("%s failed\n", name[3]); + return KSFT_FAIL; + } + + ret = syscall(SYS_clock_getres, clk_id, &sys_ts); + + if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) { + printf("%s failed\n", name[3]); + return KSFT_FAIL; + } + + return KSFT_PASS; +} + +const char *vdso_clock_name[12] = { + "CLOCK_REALTIME", + "CLOCK_MONOTONIC", + "CLOCK_PROCESS_CPUTIME_ID", + "CLOCK_THREAD_CPUTIME_ID", + "CLOCK_MONOTONIC_RAW", + "CLOCK_REALTIME_COARSE", + "CLOCK_MONOTONIC_COARSE", + "CLOCK_BOOTTIME", + "CLOCK_REALTIME_ALARM", + "CLOCK_BOOTTIME_ALARM", + "CLOCK_SGI_CYCLE", + "CLOCK_TAI", +}; + +/* + * This function calls vdso_test_clock_gettime and vdso_test_clock_getres + * with different values for clock_id. + */ +static inline int vdso_test_clock(clockid_t clock_id) +{ + int ret0, ret1; + + ret0 = vdso_test_clock_gettime(clock_id); + /* A skipped test is considered passed */ + if (ret0 == KSFT_SKIP) + ret0 = KSFT_PASS; + + ret1 = vdso_test_clock_getres(clock_id); + /* A skipped test is considered passed */ + if (ret1 == KSFT_SKIP) + ret1 = KSFT_PASS; + + ret0 += ret1; + + printf("clock_id: %s", vdso_clock_name[clock_id]); + + if (ret0 > 0) + printf(" [FAIL]\n"); + else + printf(" [PASS]\n"); + + return ret0; +} + +int main(int argc, char **argv) +{ + unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + int ret; + + if (!sysinfo_ehdr) { + printf("AT_SYSINFO_EHDR is not present!\n"); + return KSFT_SKIP; + } + + version = versions[VDSO_VERSION]; + name = (const char **)&names[VDSO_NAMES]; + + printf("[vDSO kselftest] VDSO_VERSION: %s\n", version); + + vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); + + ret = vdso_test_gettimeofday(); + +#if _POSIX_TIMERS > 0 + +#ifdef CLOCK_REALTIME + ret += vdso_test_clock(CLOCK_REALTIME); +#endif + +#ifdef CLOCK_BOOTTIME + ret += vdso_test_clock(CLOCK_BOOTTIME); +#endif + +#ifdef CLOCK_TAI + ret += vdso_test_clock(CLOCK_TAI); +#endif + +#ifdef CLOCK_REALTIME_COARSE + ret += vdso_test_clock(CLOCK_REALTIME_COARSE); +#endif + +#ifdef CLOCK_MONOTONIC + ret += vdso_test_clock(CLOCK_MONOTONIC); +#endif + +#ifdef CLOCK_MONOTONIC_RAW + ret += vdso_test_clock(CLOCK_MONOTONIC_RAW); +#endif + +#ifdef CLOCK_MONOTONIC_COARSE + ret += vdso_test_clock(CLOCK_MONOTONIC_COARSE); +#endif + +#endif + + ret += vdso_test_time(); + + if (ret > 0) + return KSFT_FAIL; + + return KSFT_PASS; +} -- cgit v1.2.3 From 03f55c7952c92d8577d6e9bc695f3fd20032cfd9 Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 26 Oct 2020 11:49:43 +0000 Subject: kselftest: Extend vDSO selftest to clock_getres The current version of the multiarch vDSO selftest verifies only gettimeofday. Extend the vDSO selftest to clock_getres, to verify that the syscall and the vDSO library function return the same information. The extension has been used to verify the hrtimer_resoltion fix. Cc: Shuah Khan Signed-off-by: Vincenzo Frascino Acked-by: Thomas Gleixner Signed-off-by: Shuah Khan --- tools/testing/selftests/vDSO/Makefile | 2 + .../selftests/vDSO/vdso_test_clock_getres.c | 124 +++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 tools/testing/selftests/vDSO/vdso_test_clock_getres.c (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 2ac7448bd414..375d80c2bff5 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -6,6 +6,7 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi +TEST_GEN_PROGS += $(OUTPUT)/vdso_test_clock_getres ifeq ($(ARCH),x86) TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 endif @@ -20,6 +21,7 @@ all: $(TEST_GEN_PROGS) $(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_clock_getres: vdso_test_clock_getres.c $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ vdso_standalone_test_x86.c parse_vdso.c \ diff --git a/tools/testing/selftests/vDSO/vdso_test_clock_getres.c b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c new file mode 100644 index 000000000000..15dcee16ff72 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_test_clock_getres.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +/* + * vdso_clock_getres.c: Sample code to test clock_getres. + * Copyright (c) 2019 Arm Ltd. + * + * Compile with: + * gcc -std=gnu99 vdso_clock_getres.c + * + * Tested on ARM, ARM64, MIPS32, x86 (32-bit and 64-bit), + * Power (32-bit and 64-bit), S390x (32-bit and 64-bit). + * Might work on other architectures. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" + +static long syscall_clock_getres(clockid_t _clkid, struct timespec *_ts) +{ + long ret; + + ret = syscall(SYS_clock_getres, _clkid, _ts); + + return ret; +} + +const char *vdso_clock_name[12] = { + "CLOCK_REALTIME", + "CLOCK_MONOTONIC", + "CLOCK_PROCESS_CPUTIME_ID", + "CLOCK_THREAD_CPUTIME_ID", + "CLOCK_MONOTONIC_RAW", + "CLOCK_REALTIME_COARSE", + "CLOCK_MONOTONIC_COARSE", + "CLOCK_BOOTTIME", + "CLOCK_REALTIME_ALARM", + "CLOCK_BOOTTIME_ALARM", + "CLOCK_SGI_CYCLE", + "CLOCK_TAI", +}; + +/* + * This function calls clock_getres in vdso and by system call + * with different values for clock_id. + * + * Example of output: + * + * clock_id: CLOCK_REALTIME [PASS] + * clock_id: CLOCK_BOOTTIME [PASS] + * clock_id: CLOCK_TAI [PASS] + * clock_id: CLOCK_REALTIME_COARSE [PASS] + * clock_id: CLOCK_MONOTONIC [PASS] + * clock_id: CLOCK_MONOTONIC_RAW [PASS] + * clock_id: CLOCK_MONOTONIC_COARSE [PASS] + */ +static inline int vdso_test_clock(unsigned int clock_id) +{ + struct timespec x, y; + + printf("clock_id: %s", vdso_clock_name[clock_id]); + clock_getres(clock_id, &x); + syscall_clock_getres(clock_id, &y); + + if ((x.tv_sec != y.tv_sec) || (x.tv_nsec != y.tv_nsec)) { + printf(" [FAIL]\n"); + return KSFT_FAIL; + } + + printf(" [PASS]\n"); + return KSFT_PASS; +} + +int main(int argc, char **argv) +{ + int ret; + +#if _POSIX_TIMERS > 0 + +#ifdef CLOCK_REALTIME + ret = vdso_test_clock(CLOCK_REALTIME); +#endif + +#ifdef CLOCK_BOOTTIME + ret += vdso_test_clock(CLOCK_BOOTTIME); +#endif + +#ifdef CLOCK_TAI + ret += vdso_test_clock(CLOCK_TAI); +#endif + +#ifdef CLOCK_REALTIME_COARSE + ret += vdso_test_clock(CLOCK_REALTIME_COARSE); +#endif + +#ifdef CLOCK_MONOTONIC + ret += vdso_test_clock(CLOCK_MONOTONIC); +#endif + +#ifdef CLOCK_MONOTONIC_RAW + ret += vdso_test_clock(CLOCK_MONOTONIC_RAW); +#endif + +#ifdef CLOCK_MONOTONIC_COARSE + ret += vdso_test_clock(CLOCK_MONOTONIC_COARSE); +#endif + +#endif + if (ret > 0) + return KSFT_FAIL; + + return KSFT_PASS; +} -- cgit v1.2.3 From c7e5789b24d36dd5dddd36ea2b99280a606cac42 Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 26 Oct 2020 11:49:44 +0000 Subject: kselftest: Move test_vdso to the vDSO test suite Move test_vdso from x86 to the vDSO test suite. Suggested-by: Andy Lutomirski Cc: Shuah Khan Signed-off-by: Vincenzo Frascino Acked-by: Thomas Gleixner Signed-off-by: Shuah Khan --- tools/testing/selftests/vDSO/Makefile | 10 +- .../testing/selftests/vDSO/vdso_test_correctness.c | 342 +++++++++++++++++++++ tools/testing/selftests/x86/Makefile | 2 +- tools/testing/selftests/x86/test_vdso.c | 342 --------------------- 4 files changed, 351 insertions(+), 345 deletions(-) create mode 100644 tools/testing/selftests/vDSO/vdso_test_correctness.c delete mode 100644 tools/testing/selftests/x86/test_vdso.c (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 375d80c2bff5..d53a4d8008f9 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -7,12 +7,14 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi TEST_GEN_PROGS += $(OUTPUT)/vdso_test_clock_getres -ifeq ($(ARCH),x86) +ifeq ($(ARCH),$(filter $(ARCH),x86 x86_64)) TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 endif +TEST_GEN_PROGS += $(OUTPUT)/vdso_test_correctness CFLAGS := -std=gnu99 CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector +LDFLAGS_vdso_test_correctness := -ldl ifeq ($(CONFIG_X86_32),y) LDLIBS += -lgcc_s endif @@ -26,4 +28,8 @@ $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ vdso_standalone_test_x86.c parse_vdso.c \ -o $@ - +$(OUTPUT)/vdso_test_correctness: vdso_test_correctness.c + $(CC) $(CFLAGS) \ + vdso_test_correctness.c \ + -o $@ \ + $(LDFLAGS_vdso_test_correctness) diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c new file mode 100644 index 000000000000..42052db0f870 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ldt_gdt.c - Test cases for LDT and GDT access + * Copyright (c) 2011-2015 Andrew Lutomirski + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SYS_getcpu +# ifdef __x86_64__ +# define SYS_getcpu 309 +# else +# define SYS_getcpu 318 +# endif +#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 long (*vgtod_t)(struct timeval *tv, struct timezone *tz); + +vgtod_t vdso_gettimeofday; + +typedef long (*getcpu_t)(unsigned *, unsigned *, void *); + +getcpu_t vgetcpu; +getcpu_t vdso_getcpu; + +static void *vsyscall_getcpu(void) +{ +#ifdef __x86_64__ + FILE *maps; + char line[MAPS_LINE_LEN]; + bool found = false; + + maps = fopen("/proc/self/maps", "r"); + if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */ + return NULL; + + while (fgets(line, MAPS_LINE_LEN, maps)) { + char r, x; + void *start, *end; + char name[MAPS_LINE_LEN]; + + /* sscanf() is safe here as strlen(name) >= strlen(line) */ + if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s", + &start, &end, &r, &x, name) != 5) + continue; + + if (strcmp(name, "[vsyscall]")) + continue; + + /* assume entries are OK, as we test vDSO here not vsyscall */ + found = true; + break; + } + + fclose(maps); + + if (!found) { + printf("Warning: failed to find vsyscall getcpu\n"); + return NULL; + } + return (void *) (0xffffffffff600800); +#else + return NULL; +#endif +} + + +static void fill_function_pointers() +{ + 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) { + printf("[WARN]\tfailed to find vDSO\n"); + return; + } + + vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu"); + if (!vdso_getcpu) + printf("Warning: failed to find getcpu in vDSO\n"); + + vgetcpu = (getcpu_t) vsyscall_getcpu(); + + vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime"); + if (!vdso_clock_gettime) + printf("Warning: failed to find clock_gettime in vDSO\n"); + + vdso_gettimeofday = (vgtod_t)dlsym(vdso, "__vdso_gettimeofday"); + if (!vdso_gettimeofday) + printf("Warning: failed to find gettimeofday in vDSO\n"); + +} + +static long sys_getcpu(unsigned * cpu, unsigned * node, + void* cache) +{ + 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_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + return syscall(__NR_gettimeofday, tv, tz); +} + +static void test_getcpu(void) +{ + printf("[RUN]\tTesting getcpu...\n"); + + for (int cpu = 0; ; cpu++) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) + return; + + unsigned cpu_sys, cpu_vdso, cpu_vsys, + node_sys, node_vdso, node_vsys; + long ret_sys, ret_vdso = 1, ret_vsys = 1; + unsigned node; + + ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0); + if (vdso_getcpu) + ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0); + if (vgetcpu) + ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0); + + if (!ret_sys) + node = node_sys; + else if (!ret_vdso) + node = node_vdso; + else if (!ret_vsys) + node = node_vsys; + + bool ok = true; + if (!ret_sys && (cpu_sys != cpu || node_sys != node)) + ok = false; + if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node)) + ok = false; + if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node)) + ok = false; + + printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu); + if (!ret_sys) + printf(" syscall: cpu %u, node %u", cpu_sys, node_sys); + if (!ret_vdso) + printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso); + if (!ret_vsys) + printf(" vsyscall: cpu %u, node %u", cpu_vsys, + node_vsys); + printf("\n"); + + if (!ok) + nerrs++; + } +} + +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 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; +} + +static char const * const clocknames[] = { + [0] = "CLOCK_REALTIME", + [1] = "CLOCK_MONOTONIC", + [2] = "CLOCK_PROCESS_CPUTIME_ID", + [3] = "CLOCK_THREAD_CPUTIME_ID", + [4] = "CLOCK_MONOTONIC_RAW", + [5] = "CLOCK_REALTIME_COARSE", + [6] = "CLOCK_MONOTONIC_COARSE", + [7] = "CLOCK_BOOTTIME", + [8] = "CLOCK_REALTIME_ALARM", + [9] = "CLOCK_BOOTTIME_ALARM", + [10] = "CLOCK_SGI_CYCLE", + [11] = "CLOCK_TAI", +}; + +static void test_one_clock_gettime(int clock, const char *name) +{ + struct timespec start, vdso, end; + int vdso_ret, end_ret; + + printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock); + + if (sys_clock_gettime(clock, &start) < 0) { + if (errno == EINVAL) { + vdso_ret = vdso_clock_gettime(clock, &vdso); + if (vdso_ret == -EINVAL) { + printf("[OK]\tNo such clock.\n"); + } else { + printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret); + nerrs++; + } + } else { + printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno); + } + return; + } + + vdso_ret = vdso_clock_gettime(clock, &vdso); + end_ret = sys_clock_gettime(clock, &end); + + if (vdso_ret != 0 || end_ret != 0) { + printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", + vdso_ret, errno); + nerrs++; + return; + } + + 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); + + if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) { + printf("[FAIL]\tTimes are out of sequence\n"); + nerrs++; + } +} + +static void test_clock_gettime(void) +{ + if (!vdso_clock_gettime) { + printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n"); + return; + } + + for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]); + clock++) { + test_one_clock_gettime(clock, clocknames[clock]); + } + + /* Also test some invalid clock ids */ + test_one_clock_gettime(-1, "invalid"); + test_one_clock_gettime(INT_MIN, "invalid"); + test_one_clock_gettime(INT_MAX, "invalid"); +} + +static void test_gettimeofday(void) +{ + struct timeval start, vdso, end; + struct timezone sys_tz, vdso_tz; + int vdso_ret, end_ret; + + if (!vdso_gettimeofday) + return; + + printf("[RUN]\tTesting gettimeofday...\n"); + + if (sys_gettimeofday(&start, &sys_tz) < 0) { + printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno); + nerrs++; + return; + } + + vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz); + end_ret = sys_gettimeofday(&end, NULL); + + if (vdso_ret != 0 || end_ret != 0) { + printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", + vdso_ret, errno); + nerrs++; + return; + } + + printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n", + (unsigned long long)start.tv_sec, start.tv_usec, + (unsigned long long)vdso.tv_sec, vdso.tv_usec, + (unsigned long long)end.tv_sec, end.tv_usec); + + if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) { + printf("[FAIL]\tTimes are out of sequence\n"); + nerrs++; + } + + if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest && + sys_tz.tz_dsttime == vdso_tz.tz_dsttime) { + printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n", + sys_tz.tz_minuteswest, sys_tz.tz_dsttime); + } else { + printf("[FAIL]\ttimezones do not match\n"); + nerrs++; + } + + /* And make sure that passing NULL for tz doesn't crash. */ + vdso_gettimeofday(&vdso, NULL); +} + +int main(int argc, char **argv) +{ + fill_function_pointers(); + + test_clock_gettime(); + test_gettimeofday(); + + /* + * Test getcpu() last so that, if something goes wrong setting affinity, + * we still run the other tests. + */ + test_getcpu(); + + return nerrs ? 1 : 0; +} diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 6703c7906b71..333980375bc7 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -12,7 +12,7 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie) TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \ check_initial_reg_state sigreturn iopl ioperm \ - test_vdso test_vsyscall mov_ss_trap \ + test_vsyscall mov_ss_trap \ syscall_arg_fault fsgsbase_restore TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ diff --git a/tools/testing/selftests/x86/test_vdso.c b/tools/testing/selftests/x86/test_vdso.c deleted file mode 100644 index 42052db0f870..000000000000 --- a/tools/testing/selftests/x86/test_vdso.c +++ /dev/null @@ -1,342 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ldt_gdt.c - Test cases for LDT and GDT access - * Copyright (c) 2011-2015 Andrew Lutomirski - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SYS_getcpu -# ifdef __x86_64__ -# define SYS_getcpu 309 -# else -# define SYS_getcpu 318 -# endif -#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 long (*vgtod_t)(struct timeval *tv, struct timezone *tz); - -vgtod_t vdso_gettimeofday; - -typedef long (*getcpu_t)(unsigned *, unsigned *, void *); - -getcpu_t vgetcpu; -getcpu_t vdso_getcpu; - -static void *vsyscall_getcpu(void) -{ -#ifdef __x86_64__ - FILE *maps; - char line[MAPS_LINE_LEN]; - bool found = false; - - maps = fopen("/proc/self/maps", "r"); - if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */ - return NULL; - - while (fgets(line, MAPS_LINE_LEN, maps)) { - char r, x; - void *start, *end; - char name[MAPS_LINE_LEN]; - - /* sscanf() is safe here as strlen(name) >= strlen(line) */ - if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s", - &start, &end, &r, &x, name) != 5) - continue; - - if (strcmp(name, "[vsyscall]")) - continue; - - /* assume entries are OK, as we test vDSO here not vsyscall */ - found = true; - break; - } - - fclose(maps); - - if (!found) { - printf("Warning: failed to find vsyscall getcpu\n"); - return NULL; - } - return (void *) (0xffffffffff600800); -#else - return NULL; -#endif -} - - -static void fill_function_pointers() -{ - 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) { - printf("[WARN]\tfailed to find vDSO\n"); - return; - } - - vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu"); - if (!vdso_getcpu) - printf("Warning: failed to find getcpu in vDSO\n"); - - vgetcpu = (getcpu_t) vsyscall_getcpu(); - - vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime"); - if (!vdso_clock_gettime) - printf("Warning: failed to find clock_gettime in vDSO\n"); - - vdso_gettimeofday = (vgtod_t)dlsym(vdso, "__vdso_gettimeofday"); - if (!vdso_gettimeofday) - printf("Warning: failed to find gettimeofday in vDSO\n"); - -} - -static long sys_getcpu(unsigned * cpu, unsigned * node, - void* cache) -{ - 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_gettimeofday(struct timeval *tv, struct timezone *tz) -{ - return syscall(__NR_gettimeofday, tv, tz); -} - -static void test_getcpu(void) -{ - printf("[RUN]\tTesting getcpu...\n"); - - for (int cpu = 0; ; cpu++) { - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); - if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) - return; - - unsigned cpu_sys, cpu_vdso, cpu_vsys, - node_sys, node_vdso, node_vsys; - long ret_sys, ret_vdso = 1, ret_vsys = 1; - unsigned node; - - ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0); - if (vdso_getcpu) - ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0); - if (vgetcpu) - ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0); - - if (!ret_sys) - node = node_sys; - else if (!ret_vdso) - node = node_vdso; - else if (!ret_vsys) - node = node_vsys; - - bool ok = true; - if (!ret_sys && (cpu_sys != cpu || node_sys != node)) - ok = false; - if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node)) - ok = false; - if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node)) - ok = false; - - printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu); - if (!ret_sys) - printf(" syscall: cpu %u, node %u", cpu_sys, node_sys); - if (!ret_vdso) - printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso); - if (!ret_vsys) - printf(" vsyscall: cpu %u, node %u", cpu_vsys, - node_vsys); - printf("\n"); - - if (!ok) - nerrs++; - } -} - -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 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; -} - -static char const * const clocknames[] = { - [0] = "CLOCK_REALTIME", - [1] = "CLOCK_MONOTONIC", - [2] = "CLOCK_PROCESS_CPUTIME_ID", - [3] = "CLOCK_THREAD_CPUTIME_ID", - [4] = "CLOCK_MONOTONIC_RAW", - [5] = "CLOCK_REALTIME_COARSE", - [6] = "CLOCK_MONOTONIC_COARSE", - [7] = "CLOCK_BOOTTIME", - [8] = "CLOCK_REALTIME_ALARM", - [9] = "CLOCK_BOOTTIME_ALARM", - [10] = "CLOCK_SGI_CYCLE", - [11] = "CLOCK_TAI", -}; - -static void test_one_clock_gettime(int clock, const char *name) -{ - struct timespec start, vdso, end; - int vdso_ret, end_ret; - - printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock); - - if (sys_clock_gettime(clock, &start) < 0) { - if (errno == EINVAL) { - vdso_ret = vdso_clock_gettime(clock, &vdso); - if (vdso_ret == -EINVAL) { - printf("[OK]\tNo such clock.\n"); - } else { - printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret); - nerrs++; - } - } else { - printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno); - } - return; - } - - vdso_ret = vdso_clock_gettime(clock, &vdso); - end_ret = sys_clock_gettime(clock, &end); - - if (vdso_ret != 0 || end_ret != 0) { - printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", - vdso_ret, errno); - nerrs++; - return; - } - - 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); - - if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) { - printf("[FAIL]\tTimes are out of sequence\n"); - nerrs++; - } -} - -static void test_clock_gettime(void) -{ - if (!vdso_clock_gettime) { - printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n"); - return; - } - - for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]); - clock++) { - test_one_clock_gettime(clock, clocknames[clock]); - } - - /* Also test some invalid clock ids */ - test_one_clock_gettime(-1, "invalid"); - test_one_clock_gettime(INT_MIN, "invalid"); - test_one_clock_gettime(INT_MAX, "invalid"); -} - -static void test_gettimeofday(void) -{ - struct timeval start, vdso, end; - struct timezone sys_tz, vdso_tz; - int vdso_ret, end_ret; - - if (!vdso_gettimeofday) - return; - - printf("[RUN]\tTesting gettimeofday...\n"); - - if (sys_gettimeofday(&start, &sys_tz) < 0) { - printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno); - nerrs++; - return; - } - - vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz); - end_ret = sys_gettimeofday(&end, NULL); - - if (vdso_ret != 0 || end_ret != 0) { - printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", - vdso_ret, errno); - nerrs++; - return; - } - - printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n", - (unsigned long long)start.tv_sec, start.tv_usec, - (unsigned long long)vdso.tv_sec, vdso.tv_usec, - (unsigned long long)end.tv_sec, end.tv_usec); - - if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) { - printf("[FAIL]\tTimes are out of sequence\n"); - nerrs++; - } - - if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest && - sys_tz.tz_dsttime == vdso_tz.tz_dsttime) { - printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n", - sys_tz.tz_minuteswest, sys_tz.tz_dsttime); - } else { - printf("[FAIL]\ttimezones do not match\n"); - nerrs++; - } - - /* And make sure that passing NULL for tz doesn't crash. */ - vdso_gettimeofday(&vdso, NULL); -} - -int main(int argc, char **argv) -{ - fill_function_pointers(); - - test_clock_gettime(); - test_gettimeofday(); - - /* - * Test getcpu() last so that, if something goes wrong setting affinity, - * we still run the other tests. - */ - test_getcpu(); - - return nerrs ? 1 : 0; -} -- cgit v1.2.3 From b2f1c3db28870d88d1a19aa86a8374e7725d62c5 Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 26 Oct 2020 11:49:45 +0000 Subject: kselftest: Extend vdso correctness test to clock_gettime64 With the release of Linux 5.1 has been added a new syscall, clock_gettime64, that provided a 64 bit time value for a specified clock_ID to make the kernel Y2038 safe on 32 bit architectures. Extend the vdso correctness test to cover the newly exposed vdso function. Cc: Shuah Khan Signed-off-by: Vincenzo Frascino Acked-by: Thomas Gleixner Signed-off-by: Shuah Khan --- tools/testing/selftests/vDSO/vdso_config.h | 4 +- .../testing/selftests/vDSO/vdso_test_correctness.c | 115 ++++++++++++++++++++- 2 files changed, 115 insertions(+), 4 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index eeb725df6045..6a6fe8d4ff55 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h @@ -66,12 +66,13 @@ static const char *versions[6] = { "LINUX_4.15", }; -static const char *names[2][5] = { +static const char *names[2][6] = { { "__kernel_gettimeofday", "__kernel_clock_gettime", "__kernel_time", "__kernel_clock_getres", + "__kernel_getcpu", #if defined(VDSO_32BIT) "__kernel_clock_gettime64", #endif @@ -81,6 +82,7 @@ static const char *names[2][5] = { "__vdso_clock_gettime", "__vdso_time", "__vdso_clock_getres", + "__vdso_getcpu", #if defined(VDSO_32BIT) "__vdso_clock_gettime64", #endif diff --git a/tools/testing/selftests/vDSO/vdso_test_correctness.c b/tools/testing/selftests/vDSO/vdso_test_correctness.c index 42052db0f870..5029ef9b228c 100644 --- a/tools/testing/selftests/vDSO/vdso_test_correctness.c +++ b/tools/testing/selftests/vDSO/vdso_test_correctness.c @@ -19,6 +19,10 @@ #include #include +#include "vdso_config.h" + +static const char **name; + #ifndef SYS_getcpu # ifdef __x86_64__ # define SYS_getcpu 309 @@ -27,6 +31,17 @@ # endif #endif +#ifndef __NR_clock_gettime64 +#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 @@ -36,6 +51,10 @@ 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; @@ -99,17 +118,23 @@ static void fill_function_pointers() return; } - vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu"); + vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]); if (!vdso_getcpu) printf("Warning: failed to find getcpu in vDSO\n"); vgetcpu = (getcpu_t) vsyscall_getcpu(); - vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime"); + vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]); if (!vdso_clock_gettime) printf("Warning: failed to find clock_gettime in vDSO\n"); - vdso_gettimeofday = (vgtod_t)dlsym(vdso, "__vdso_gettimeofday"); +#if defined(VDSO_32BIT) + vdso_clock_gettime64 = (vgettime64_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]); if (!vdso_gettimeofday) printf("Warning: failed to find gettimeofday in vDSO\n"); @@ -126,6 +151,11 @@ 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); @@ -191,6 +221,15 @@ static bool ts_leq(const struct timespec *a, const struct timespec *b) 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) @@ -254,7 +293,10 @@ static void test_one_clock_gettime(int clock, const char *name) if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) { printf("[FAIL]\tTimes are out of sequence\n"); nerrs++; + return; } + + printf("[OK]\tTest Passed.\n"); } static void test_clock_gettime(void) @@ -275,6 +317,70 @@ static void test_clock_gettime(void) test_one_clock_gettime(INT_MAX, "invalid"); } +static void test_one_clock_gettime64(int clock, const char *name) +{ + struct __kernel_timespec start, vdso, end; + int vdso_ret, end_ret; + + printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock); + + if (sys_clock_gettime64(clock, &start) < 0) { + if (errno == EINVAL) { + vdso_ret = vdso_clock_gettime64(clock, &vdso); + if (vdso_ret == -EINVAL) { + printf("[OK]\tNo such clock.\n"); + } else { + printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret); + nerrs++; + } + } else { + printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno); + } + return; + } + + vdso_ret = vdso_clock_gettime64(clock, &vdso); + end_ret = sys_clock_gettime64(clock, &end); + + if (vdso_ret != 0 || end_ret != 0) { + printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n", + vdso_ret, errno); + nerrs++; + return; + } + + 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); + + if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) { + printf("[FAIL]\tTimes are out of sequence\n"); + nerrs++; + return; + } + + printf("[OK]\tTest Passed.\n"); +} + +static void test_clock_gettime64(void) +{ + if (!vdso_clock_gettime64) { + printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n"); + return; + } + + for (int clock = 0; clock < sizeof(clocknames) / sizeof(clocknames[0]); + clock++) { + test_one_clock_gettime64(clock, clocknames[clock]); + } + + /* Also test some invalid clock ids */ + test_one_clock_gettime64(-1, "invalid"); + test_one_clock_gettime64(INT_MIN, "invalid"); + test_one_clock_gettime64(INT_MAX, "invalid"); +} + static void test_gettimeofday(void) { struct timeval start, vdso, end; @@ -327,9 +433,12 @@ static void test_gettimeofday(void) int main(int argc, char **argv) { + name = (const char **)&names[VDSO_NAMES]; + fill_function_pointers(); test_clock_gettime(); + test_clock_gettime64(); test_gettimeofday(); /* -- cgit v1.2.3 From ff2c395b9257f0e617f9cd212893f3c72c80ee6c Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 4 Nov 2020 21:08:40 +1100 Subject: selftests/gpio: Use TEST_GEN_PROGS_EXTENDED Use TEST_GEN_PROGS_EXTENDED rather than TEST_PROGS_EXTENDED. That tells the lib.mk logic that the files it references are to be generated by the Makefile. Having done that we don't need to override the all rule. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/gpio/Makefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 32bdc978a711..c85fb5acf5f4 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -11,22 +11,20 @@ LDLIBS += $(VAR_LDLIBS) TEST_PROGS := gpio-mockup.sh TEST_FILES := gpio-mockup-sysfs.sh -TEST_PROGS_EXTENDED := gpio-mockup-chardev +TEST_GEN_PROGS_EXTENDED := gpio-mockup-chardev GPIODIR := $(realpath ../../../gpio) GPIOOBJ := gpio-utils.o -all: $(TEST_PROGS_EXTENDED) - override define CLEAN - $(RM) $(TEST_PROGS_EXTENDED) + $(RM) $(TEST_GEN_PROGS_EXTENDED) $(MAKE) -C $(GPIODIR) OUTPUT=$(GPIODIR)/ clean endef KSFT_KHDR_INSTALL := 1 include ../lib.mk -$(TEST_PROGS_EXTENDED): $(GPIODIR)/$(GPIOOBJ) +$(TEST_GEN_PROGS_EXTENDED): $(GPIODIR)/$(GPIOOBJ) $(GPIODIR)/$(GPIOOBJ): $(MAKE) OUTPUT=$(GPIODIR)/ -C $(GPIODIR) -- cgit v1.2.3 From 449539da2e237336bc750b41f1736a77f9aca25c Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 4 Nov 2020 21:08:41 +1100 Subject: selftests/gpio: Move include of lib.mk up Move the include of lib.mk up so that in a subsequent patch we can use OUTPUT, which is initialised by lib.mk, in the definition of the GPIO variables. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/gpio/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index c85fb5acf5f4..615c8a953ade 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -13,6 +13,9 @@ TEST_PROGS := gpio-mockup.sh TEST_FILES := gpio-mockup-sysfs.sh TEST_GEN_PROGS_EXTENDED := gpio-mockup-chardev +KSFT_KHDR_INSTALL := 1 +include ../lib.mk + GPIODIR := $(realpath ../../../gpio) GPIOOBJ := gpio-utils.o @@ -21,9 +24,6 @@ override define CLEAN $(MAKE) -C $(GPIODIR) OUTPUT=$(GPIODIR)/ clean endef -KSFT_KHDR_INSTALL := 1 -include ../lib.mk - $(TEST_GEN_PROGS_EXTENDED): $(GPIODIR)/$(GPIOOBJ) $(GPIODIR)/$(GPIOOBJ): -- cgit v1.2.3 From b68c1c65dec5fb5186ebd33ce52059b4c6db8500 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 4 Nov 2020 21:08:42 +1100 Subject: selftests/gpio: Fix build when source tree is read only Currently the gpio selftests fail to build if the source tree is read only: make -j 160 -C tools/testing/selftests TARGETS=gpio make[1]: Entering directory '/linux/tools/testing/selftests/gpio' make OUTPUT=/linux/tools/gpio/ -C /linux/tools/gpio make[2]: Entering directory '/linux/tools/gpio' mkdir -p /linux/tools/gpio/include/linux 2>&1 || true ln -sf /linux/tools/gpio/../../include/uapi/linux/gpio.h /linux/tools/gpio/include/linux/gpio.h ln: failed to create symbolic link '/linux/tools/gpio/include/linux/gpio.h': Read-only file system This happens because we ask make to build ../../../gpio (tools/gpio) without pointing OUTPUT away from the source directory. To fix it we create a subdirectory of the existing OUTPUT directory, called tools-gpio, and tell tools/gpio to build in there. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/gpio/Makefile | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index 615c8a953ade..acf4088a9891 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -17,14 +17,18 @@ KSFT_KHDR_INSTALL := 1 include ../lib.mk GPIODIR := $(realpath ../../../gpio) -GPIOOBJ := gpio-utils.o +GPIOOUT := $(OUTPUT)/tools-gpio/ +GPIOOBJ := $(GPIOOUT)/gpio-utils.o override define CLEAN $(RM) $(TEST_GEN_PROGS_EXTENDED) - $(MAKE) -C $(GPIODIR) OUTPUT=$(GPIODIR)/ clean + $(RM) -rf $(GPIOOUT) endef -$(TEST_GEN_PROGS_EXTENDED): $(GPIODIR)/$(GPIOOBJ) +$(TEST_GEN_PROGS_EXTENDED): $(GPIOOBJ) -$(GPIODIR)/$(GPIOOBJ): - $(MAKE) OUTPUT=$(GPIODIR)/ -C $(GPIODIR) +$(GPIOOUT): + mkdir -p $@ + +$(GPIOOBJ): $(GPIOOUT) + $(MAKE) OUTPUT=$(GPIOOUT) -C $(GPIODIR) -- cgit v1.2.3 From 85128c5bcdf9bd9b574d7cbafa49170a39fed2e1 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 4 Nov 2020 21:08:43 +1100 Subject: selftests/gpio: Add to CLEAN rule rather than overriding Rather than overriding the CLEAN rule we can just append to it. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/gpio/Makefile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile index acf4088a9891..41582fe485ee 100644 --- a/tools/testing/selftests/gpio/Makefile +++ b/tools/testing/selftests/gpio/Makefile @@ -20,10 +20,7 @@ GPIODIR := $(realpath ../../../gpio) GPIOOUT := $(OUTPUT)/tools-gpio/ GPIOOBJ := $(GPIOOUT)/gpio-utils.o -override define CLEAN - $(RM) $(TEST_GEN_PROGS_EXTENDED) - $(RM) -rf $(GPIOOUT) -endef +CLEAN += ; $(RM) -rf $(GPIOOUT) $(TEST_GEN_PROGS_EXTENDED): $(GPIOOBJ) -- cgit v1.2.3 From fc4a3a1bf9ad799181e4d4ec9c2598c0405bc27d Mon Sep 17 00:00:00 2001 From: Tommi Rantala Date: Mon, 2 Nov 2020 09:39:18 +0200 Subject: selftests: intel_pstate: ftime() is deprecated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use clock_gettime() instead of deprecated ftime(). aperf.c: In function ‘main’: aperf.c:58:2: warning: ‘ftime’ is deprecated [-Wdeprecated-declarations] 58 | ftime(&before); | ^~~~~ In file included from aperf.c:9: /usr/include/sys/timeb.h:39:12: note: declared here 39 | extern int ftime (struct timeb *__timebuf) | ^~~~~ Signed-off-by: Tommi Rantala Signed-off-by: Shuah Khan --- tools/testing/selftests/intel_pstate/aperf.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/intel_pstate/aperf.c b/tools/testing/selftests/intel_pstate/aperf.c index f6cd03a87493..a8acf3996973 100644 --- a/tools/testing/selftests/intel_pstate/aperf.c +++ b/tools/testing/selftests/intel_pstate/aperf.c @@ -10,8 +10,12 @@ #include #include #include +#include #include "../kselftest.h" +#define MSEC_PER_SEC 1000L +#define NSEC_PER_MSEC 1000000L + void usage(char *name) { printf ("Usage: %s cpunum\n", name); } @@ -22,7 +26,7 @@ int main(int argc, char **argv) { long long tsc, old_tsc, new_tsc; long long aperf, old_aperf, new_aperf; long long mperf, old_mperf, new_mperf; - struct timeb before, after; + struct timespec before, after; long long int start, finish, total; cpu_set_t cpuset; @@ -55,7 +59,10 @@ int main(int argc, char **argv) { return 1; } - ftime(&before); + if (clock_gettime(CLOCK_MONOTONIC, &before) < 0) { + perror("clock_gettime"); + return 1; + } pread(fd, &old_tsc, sizeof(old_tsc), 0x10); pread(fd, &old_aperf, sizeof(old_mperf), 0xe7); pread(fd, &old_mperf, sizeof(old_aperf), 0xe8); @@ -64,7 +71,10 @@ int main(int argc, char **argv) { sqrt(i); } - ftime(&after); + if (clock_gettime(CLOCK_MONOTONIC, &after) < 0) { + perror("clock_gettime"); + return 1; + } pread(fd, &new_tsc, sizeof(new_tsc), 0x10); pread(fd, &new_aperf, sizeof(new_mperf), 0xe7); pread(fd, &new_mperf, sizeof(new_aperf), 0xe8); @@ -73,11 +83,11 @@ int main(int argc, char **argv) { aperf = new_aperf-old_aperf; mperf = new_mperf-old_mperf; - start = before.time*1000 + before.millitm; - finish = after.time*1000 + after.millitm; + start = before.tv_sec*MSEC_PER_SEC + before.tv_nsec/NSEC_PER_MSEC; + finish = after.tv_sec*MSEC_PER_SEC + after.tv_nsec/NSEC_PER_MSEC; total = finish - start; - printf("runTime: %4.2f\n", 1.0*total/1000); + printf("runTime: %4.2f\n", 1.0*total/MSEC_PER_SEC); printf("freq: %7.0f\n", tsc / (1.0*aperf / (1.0 * mperf)) / total); return 0; } -- cgit v1.2.3 From 1c49e3783f8899555190a49024ac86d3d76633cd Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 4 Nov 2020 21:03:05 +1100 Subject: selftests/memfd: Fix implicit declaration warnings The memfd tests emit several warnings: fuse_test.c:261:7: warning: implicit declaration of function 'open' fuse_test.c:67:6: warning: implicit declaration of function 'fcntl' memfd_test.c:397:6: warning: implicit declaration of function 'fallocate' memfd_test.c:64:7: warning: implicit declaration of function 'open' memfd_test.c:90:6: warning: implicit declaration of function 'fcntl' These are all caused by the test not including fcntl.h. Instead of including linux/fcntl.h, include fcntl.h, which should eventually cause the former to be included as well. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/memfd/fuse_test.c | 2 +- tools/testing/selftests/memfd/memfd_test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c index b018e835737d..be675002f918 100644 --- a/tools/testing/selftests/memfd/fuse_test.c +++ b/tools/testing/selftests/memfd/fuse_test.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 334a7eea2004..74baab83fec3 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 82f147944c650a07831c796c398f5c973dbdde79 Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Sat, 7 Nov 2020 17:19:35 +0800 Subject: tool: selftests: fix spelling typo of 'writting' writting -> writing Signed-off-by: Wang Qing Reviewed-by: Mike Rapoport Signed-off-by: Shuah Khan --- tools/testing/selftests/vm/userfaultfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index 9b0912a01777..9132fae7ad65 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -894,7 +894,7 @@ static int faulting_process(int signal_test) count_verify[nr]); } /* - * Trigger write protection if there is by writting + * Trigger write protection if there is by writing * the same value back. */ *area_count(area_dst, nr) = count; @@ -922,7 +922,7 @@ static int faulting_process(int signal_test) count_verify[nr]); exit(1); } /* - * Trigger write protection if there is by writting + * Trigger write protection if there is by writing * the same value back. */ *area_count(area_dst, nr) = count; -- cgit v1.2.3 From 93f20eff0cca972d74cb554a2e8b47730228be16 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Wed, 28 Oct 2020 16:31:14 +0800 Subject: selftests/run_kselftest.sh: fix dry-run typo Should be -d instead of -n for dry-run. Fixes: 5da1918446a1 ("selftests/run_kselftest.sh: Make each test individually selectable") Signed-off-by: Hangbin Liu Signed-off-by: Shuah Khan --- tools/testing/selftests/run_kselftest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/run_kselftest.sh b/tools/testing/selftests/run_kselftest.sh index 609a4ef9300e..97165a83df63 100755 --- a/tools/testing/selftests/run_kselftest.sh +++ b/tools/testing/selftests/run_kselftest.sh @@ -48,7 +48,7 @@ while true; do -l | --list) echo "$available" exit 0 ;; - -n | --dry-run) + -d | --dry-run) dryrun="echo" shift ;; -h | --help) -- cgit v1.2.3 From c2e46f6b3e3551558d44c4dc518b9667cb0d5f8b Mon Sep 17 00:00:00 2001 From: Sachin Sant Date: Fri, 6 Nov 2020 13:10:06 +0530 Subject: selftests/cgroup: Fix build on older distros On older distros struct clone_args does not have a cgroup member, leading to build errors: cgroup_util.c: In function 'clone_into_cgroup': cgroup_util.c:343:4: error: 'struct clone_args' has no member named 'cgroup' cgroup_util.c:346:33: error: invalid application of 'sizeof' to incomplete type 'struct clone_args' But the selftests already have a locally defined version of the structure which is up to date, called __clone_args. So use __clone_args which fixes the error. Signed-off-by: Michael Ellerman Signed-off-by: Sachin Sant > Acked-by: Christian Brauner Signed-off-by: Shuah Khan --- tools/testing/selftests/cgroup/cgroup_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c index 05853b0b8831..027014662fb2 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.c +++ b/tools/testing/selftests/cgroup/cgroup_util.c @@ -337,13 +337,13 @@ pid_t clone_into_cgroup(int cgroup_fd) #ifdef CLONE_ARGS_SIZE_VER2 pid_t pid; - struct clone_args args = { + struct __clone_args args = { .flags = CLONE_INTO_CGROUP, .exit_signal = SIGCHLD, .cgroup = cgroup_fd, }; - pid = sys_clone3(&args, sizeof(struct clone_args)); + pid = sys_clone3(&args, sizeof(struct __clone_args)); /* * Verify that this is a genuine test failure: * ENOSYS -> clone3() not available -- cgit v1.2.3