Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <shuah@kernel.org>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Vincenzo Frascino and committed by
Shuah Khan
693f5ca0 40723419

+336
+2
tools/testing/selftests/vDSO/Makefile
··· 5 5 ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) 6 6 7 7 TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu 8 + TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi 8 9 ifeq ($(ARCH),x86) 9 10 TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 10 11 endif ··· 19 18 all: $(TEST_GEN_PROGS) 20 19 $(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c 21 20 $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c 21 + $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c 22 22 $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c 23 23 $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ 24 24 vdso_standalone_test_x86.c parse_vdso.c \
+90
tools/testing/selftests/vDSO/vdso_config.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * vdso_config.h: Configuration options for vDSO tests. 4 + * Copyright (c) 2019 Arm Ltd. 5 + */ 6 + #ifndef __VDSO_CONFIG_H__ 7 + #define __VDSO_CONFIG_H__ 8 + 9 + /* 10 + * Each architecture exports its vDSO implementation with different names 11 + * and a different version from the others, so we need to handle it as a 12 + * special case. 13 + */ 14 + #if defined(__arm__) 15 + #define VDSO_VERSION 0 16 + #define VDSO_NAMES 1 17 + #define VDSO_32BIT 1 18 + #elif defined(__aarch64__) 19 + #define VDSO_VERSION 3 20 + #define VDSO_NAMES 0 21 + #elif defined(__powerpc__) 22 + #define VDSO_VERSION 1 23 + #define VDSO_NAMES 0 24 + #define VDSO_32BIT 1 25 + #elif defined(__powerpc64__) 26 + #define VDSO_VERSION 1 27 + #define VDSO_NAMES 0 28 + #elif defined (__s390__) 29 + #define VDSO_VERSION 2 30 + #define VDSO_NAMES 0 31 + #define VDSO_32BIT 1 32 + #elif defined (__s390X__) 33 + #define VDSO_VERSION 2 34 + #define VDSO_NAMES 0 35 + #elif defined(__mips__) 36 + #define VDSO_VERSION 0 37 + #define VDSO_NAMES 1 38 + #define VDSO_32BIT 1 39 + #elif defined(__sparc__) 40 + #define VDSO_VERSION 0 41 + #define VDSO_NAMES 1 42 + #define VDSO_32BIT 1 43 + #elif defined(__i386__) 44 + #define VDSO_VERSION 0 45 + #define VDSO_NAMES 1 46 + #define VDSO_32BIT 1 47 + #elif defined(__x86_64__) 48 + #define VDSO_VERSION 0 49 + #define VDSO_NAMES 1 50 + #elif defined(__riscv__) 51 + #define VDSO_VERSION 5 52 + #define VDSO_NAMES 1 53 + #define VDSO_32BIT 1 54 + #else /* nds32 */ 55 + #define VDSO_VERSION 4 56 + #define VDSO_NAMES 1 57 + #define VDSO_32BIT 1 58 + #endif 59 + 60 + static const char *versions[6] = { 61 + "LINUX_2.6", 62 + "LINUX_2.6.15", 63 + "LINUX_2.6.29", 64 + "LINUX_2.6.39", 65 + "LINUX_4", 66 + "LINUX_4.15", 67 + }; 68 + 69 + static const char *names[2][5] = { 70 + { 71 + "__kernel_gettimeofday", 72 + "__kernel_clock_gettime", 73 + "__kernel_time", 74 + "__kernel_clock_getres", 75 + #if defined(VDSO_32BIT) 76 + "__kernel_clock_gettime64", 77 + #endif 78 + }, 79 + { 80 + "__vdso_gettimeofday", 81 + "__vdso_clock_gettime", 82 + "__vdso_time", 83 + "__vdso_clock_getres", 84 + #if defined(VDSO_32BIT) 85 + "__vdso_clock_gettime64", 86 + #endif 87 + }, 88 + }; 89 + 90 + #endif /* __VDSO_CONFIG_H__ */
+244
tools/testing/selftests/vDSO/vdso_test_abi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * vdso_full_test.c: Sample code to test all the timers. 4 + * Copyright (c) 2019 Arm Ltd. 5 + * 6 + * Compile with: 7 + * gcc -std=gnu99 vdso_full_test.c parse_vdso.c 8 + * 9 + */ 10 + 11 + #include <stdint.h> 12 + #include <elf.h> 13 + #include <stdio.h> 14 + #include <time.h> 15 + #include <sys/auxv.h> 16 + #include <sys/time.h> 17 + #define _GNU_SOURCE 18 + #include <unistd.h> 19 + #include <sys/syscall.h> 20 + 21 + #include "../kselftest.h" 22 + #include "vdso_config.h" 23 + 24 + extern void *vdso_sym(const char *version, const char *name); 25 + extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); 26 + extern void vdso_init_from_auxv(void *auxv); 27 + 28 + static const char *version; 29 + static const char **name; 30 + 31 + typedef long (*vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz); 32 + typedef long (*vdso_clock_gettime_t)(clockid_t clk_id, struct timespec *ts); 33 + typedef long (*vdso_clock_getres_t)(clockid_t clk_id, struct timespec *ts); 34 + typedef time_t (*vdso_time_t)(time_t *t); 35 + 36 + static int vdso_test_gettimeofday(void) 37 + { 38 + /* Find gettimeofday. */ 39 + vdso_gettimeofday_t vdso_gettimeofday = 40 + (vdso_gettimeofday_t)vdso_sym(version, name[0]); 41 + 42 + if (!vdso_gettimeofday) { 43 + printf("Could not find %s\n", name[0]); 44 + return KSFT_SKIP; 45 + } 46 + 47 + struct timeval tv; 48 + long ret = vdso_gettimeofday(&tv, 0); 49 + 50 + if (ret == 0) { 51 + printf("The time is %lld.%06lld\n", 52 + (long long)tv.tv_sec, (long long)tv.tv_usec); 53 + } else { 54 + printf("%s failed\n", name[0]); 55 + return KSFT_FAIL; 56 + } 57 + 58 + return KSFT_PASS; 59 + } 60 + 61 + static int vdso_test_clock_gettime(clockid_t clk_id) 62 + { 63 + /* Find clock_gettime. */ 64 + vdso_clock_gettime_t vdso_clock_gettime = 65 + (vdso_clock_gettime_t)vdso_sym(version, name[1]); 66 + 67 + if (!vdso_clock_gettime) { 68 + printf("Could not find %s\n", name[1]); 69 + return KSFT_SKIP; 70 + } 71 + 72 + struct timespec ts; 73 + long ret = vdso_clock_gettime(clk_id, &ts); 74 + 75 + if (ret == 0) { 76 + printf("The time is %lld.%06lld\n", 77 + (long long)ts.tv_sec, (long long)ts.tv_nsec); 78 + } else { 79 + printf("%s failed\n", name[1]); 80 + return KSFT_FAIL; 81 + } 82 + 83 + return KSFT_PASS; 84 + } 85 + 86 + static int vdso_test_time(void) 87 + { 88 + /* Find time. */ 89 + vdso_time_t vdso_time = 90 + (vdso_time_t)vdso_sym(version, name[2]); 91 + 92 + if (!vdso_time) { 93 + printf("Could not find %s\n", name[2]); 94 + return KSFT_SKIP; 95 + } 96 + 97 + long ret = vdso_time(NULL); 98 + 99 + if (ret > 0) { 100 + printf("The time in hours since January 1, 1970 is %lld\n", 101 + (long long)(ret / 3600)); 102 + } else { 103 + printf("%s failed\n", name[2]); 104 + return KSFT_FAIL; 105 + } 106 + 107 + return KSFT_PASS; 108 + } 109 + 110 + static int vdso_test_clock_getres(clockid_t clk_id) 111 + { 112 + /* Find clock_getres. */ 113 + vdso_clock_getres_t vdso_clock_getres = 114 + (vdso_clock_getres_t)vdso_sym(version, name[3]); 115 + 116 + if (!vdso_clock_getres) { 117 + printf("Could not find %s\n", name[3]); 118 + return KSFT_SKIP; 119 + } 120 + 121 + struct timespec ts, sys_ts; 122 + long ret = vdso_clock_getres(clk_id, &ts); 123 + 124 + if (ret == 0) { 125 + printf("The resolution is %lld %lld\n", 126 + (long long)ts.tv_sec, (long long)ts.tv_nsec); 127 + } else { 128 + printf("%s failed\n", name[3]); 129 + return KSFT_FAIL; 130 + } 131 + 132 + ret = syscall(SYS_clock_getres, clk_id, &sys_ts); 133 + 134 + if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) { 135 + printf("%s failed\n", name[3]); 136 + return KSFT_FAIL; 137 + } 138 + 139 + return KSFT_PASS; 140 + } 141 + 142 + const char *vdso_clock_name[12] = { 143 + "CLOCK_REALTIME", 144 + "CLOCK_MONOTONIC", 145 + "CLOCK_PROCESS_CPUTIME_ID", 146 + "CLOCK_THREAD_CPUTIME_ID", 147 + "CLOCK_MONOTONIC_RAW", 148 + "CLOCK_REALTIME_COARSE", 149 + "CLOCK_MONOTONIC_COARSE", 150 + "CLOCK_BOOTTIME", 151 + "CLOCK_REALTIME_ALARM", 152 + "CLOCK_BOOTTIME_ALARM", 153 + "CLOCK_SGI_CYCLE", 154 + "CLOCK_TAI", 155 + }; 156 + 157 + /* 158 + * This function calls vdso_test_clock_gettime and vdso_test_clock_getres 159 + * with different values for clock_id. 160 + */ 161 + static inline int vdso_test_clock(clockid_t clock_id) 162 + { 163 + int ret0, ret1; 164 + 165 + ret0 = vdso_test_clock_gettime(clock_id); 166 + /* A skipped test is considered passed */ 167 + if (ret0 == KSFT_SKIP) 168 + ret0 = KSFT_PASS; 169 + 170 + ret1 = vdso_test_clock_getres(clock_id); 171 + /* A skipped test is considered passed */ 172 + if (ret1 == KSFT_SKIP) 173 + ret1 = KSFT_PASS; 174 + 175 + ret0 += ret1; 176 + 177 + printf("clock_id: %s", vdso_clock_name[clock_id]); 178 + 179 + if (ret0 > 0) 180 + printf(" [FAIL]\n"); 181 + else 182 + printf(" [PASS]\n"); 183 + 184 + return ret0; 185 + } 186 + 187 + int main(int argc, char **argv) 188 + { 189 + unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); 190 + int ret; 191 + 192 + if (!sysinfo_ehdr) { 193 + printf("AT_SYSINFO_EHDR is not present!\n"); 194 + return KSFT_SKIP; 195 + } 196 + 197 + version = versions[VDSO_VERSION]; 198 + name = (const char **)&names[VDSO_NAMES]; 199 + 200 + printf("[vDSO kselftest] VDSO_VERSION: %s\n", version); 201 + 202 + vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); 203 + 204 + ret = vdso_test_gettimeofday(); 205 + 206 + #if _POSIX_TIMERS > 0 207 + 208 + #ifdef CLOCK_REALTIME 209 + ret += vdso_test_clock(CLOCK_REALTIME); 210 + #endif 211 + 212 + #ifdef CLOCK_BOOTTIME 213 + ret += vdso_test_clock(CLOCK_BOOTTIME); 214 + #endif 215 + 216 + #ifdef CLOCK_TAI 217 + ret += vdso_test_clock(CLOCK_TAI); 218 + #endif 219 + 220 + #ifdef CLOCK_REALTIME_COARSE 221 + ret += vdso_test_clock(CLOCK_REALTIME_COARSE); 222 + #endif 223 + 224 + #ifdef CLOCK_MONOTONIC 225 + ret += vdso_test_clock(CLOCK_MONOTONIC); 226 + #endif 227 + 228 + #ifdef CLOCK_MONOTONIC_RAW 229 + ret += vdso_test_clock(CLOCK_MONOTONIC_RAW); 230 + #endif 231 + 232 + #ifdef CLOCK_MONOTONIC_COARSE 233 + ret += vdso_test_clock(CLOCK_MONOTONIC_COARSE); 234 + #endif 235 + 236 + #endif 237 + 238 + ret += vdso_test_time(); 239 + 240 + if (ret > 0) 241 + return KSFT_FAIL; 242 + 243 + return KSFT_PASS; 244 + }