0001
0002
0003
0004
0005
0006
0007 #define _GNU_SOURCE
0008
0009 #include <stdio.h>
0010 #include <sys/time.h>
0011 #include <time.h>
0012 #include <stdlib.h>
0013 #include <unistd.h>
0014 #include <sys/syscall.h>
0015 #include <dlfcn.h>
0016 #include <string.h>
0017 #include <errno.h>
0018 #include <sched.h>
0019 #include <stdbool.h>
0020 #include <limits.h>
0021
0022 #include "vdso_config.h"
0023 #include "../kselftest.h"
0024
0025 static const char **name;
0026
0027 #ifndef SYS_getcpu
0028 # ifdef __x86_64__
0029 # define SYS_getcpu 309
0030 # else
0031 # define SYS_getcpu 318
0032 # endif
0033 #endif
0034
0035 #ifndef __NR_clock_gettime64
0036 #define __NR_clock_gettime64 403
0037 #endif
0038
0039 #ifndef __kernel_timespec
0040 struct __kernel_timespec {
0041 long long tv_sec;
0042 long long tv_nsec;
0043 };
0044 #endif
0045
0046
0047 #define MAPS_LINE_LEN 128
0048
0049 int nerrs = 0;
0050
0051 typedef int (*vgettime_t)(clockid_t, struct timespec *);
0052
0053 vgettime_t vdso_clock_gettime;
0054
0055 typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
0056
0057 vgettime64_t vdso_clock_gettime64;
0058
0059 typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
0060
0061 vgtod_t vdso_gettimeofday;
0062
0063 typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
0064
0065 getcpu_t vgetcpu;
0066 getcpu_t vdso_getcpu;
0067
0068 static void *vsyscall_getcpu(void)
0069 {
0070 #ifdef __x86_64__
0071 FILE *maps;
0072 char line[MAPS_LINE_LEN];
0073 bool found = false;
0074
0075 maps = fopen("/proc/self/maps", "r");
0076 if (!maps)
0077 return NULL;
0078
0079 while (fgets(line, MAPS_LINE_LEN, maps)) {
0080 char r, x;
0081 void *start, *end;
0082 char name[MAPS_LINE_LEN];
0083
0084
0085 if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
0086 &start, &end, &r, &x, name) != 5)
0087 continue;
0088
0089 if (strcmp(name, "[vsyscall]"))
0090 continue;
0091
0092
0093 found = true;
0094 break;
0095 }
0096
0097 fclose(maps);
0098
0099 if (!found) {
0100 printf("Warning: failed to find vsyscall getcpu\n");
0101 return NULL;
0102 }
0103 return (void *) (0xffffffffff600800);
0104 #else
0105 return NULL;
0106 #endif
0107 }
0108
0109
0110 static void fill_function_pointers()
0111 {
0112 void *vdso = dlopen("linux-vdso.so.1",
0113 RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
0114 if (!vdso)
0115 vdso = dlopen("linux-gate.so.1",
0116 RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
0117 if (!vdso) {
0118 printf("[WARN]\tfailed to find vDSO\n");
0119 return;
0120 }
0121
0122 vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]);
0123 if (!vdso_getcpu)
0124 printf("Warning: failed to find getcpu in vDSO\n");
0125
0126 vgetcpu = (getcpu_t) vsyscall_getcpu();
0127
0128 vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]);
0129 if (!vdso_clock_gettime)
0130 printf("Warning: failed to find clock_gettime in vDSO\n");
0131
0132 #if defined(VDSO_32BIT)
0133 vdso_clock_gettime64 = (vgettime64_t)dlsym(vdso, name[5]);
0134 if (!vdso_clock_gettime64)
0135 printf("Warning: failed to find clock_gettime64 in vDSO\n");
0136 #endif
0137
0138 vdso_gettimeofday = (vgtod_t)dlsym(vdso, name[0]);
0139 if (!vdso_gettimeofday)
0140 printf("Warning: failed to find gettimeofday in vDSO\n");
0141
0142 }
0143
0144 static long sys_getcpu(unsigned * cpu, unsigned * node,
0145 void* cache)
0146 {
0147 return syscall(__NR_getcpu, cpu, node, cache);
0148 }
0149
0150 static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
0151 {
0152 return syscall(__NR_clock_gettime, id, ts);
0153 }
0154
0155 static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
0156 {
0157 return syscall(__NR_clock_gettime64, id, ts);
0158 }
0159
0160 static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
0161 {
0162 return syscall(__NR_gettimeofday, tv, tz);
0163 }
0164
0165 static void test_getcpu(void)
0166 {
0167 printf("[RUN]\tTesting getcpu...\n");
0168
0169 for (int cpu = 0; ; cpu++) {
0170 cpu_set_t cpuset;
0171 CPU_ZERO(&cpuset);
0172 CPU_SET(cpu, &cpuset);
0173 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
0174 return;
0175
0176 unsigned cpu_sys, cpu_vdso, cpu_vsys,
0177 node_sys, node_vdso, node_vsys;
0178 long ret_sys, ret_vdso = 1, ret_vsys = 1;
0179 unsigned node;
0180
0181 ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
0182 if (vdso_getcpu)
0183 ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
0184 if (vgetcpu)
0185 ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
0186
0187 if (!ret_sys)
0188 node = node_sys;
0189 else if (!ret_vdso)
0190 node = node_vdso;
0191 else if (!ret_vsys)
0192 node = node_vsys;
0193
0194 bool ok = true;
0195 if (!ret_sys && (cpu_sys != cpu || node_sys != node))
0196 ok = false;
0197 if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
0198 ok = false;
0199 if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
0200 ok = false;
0201
0202 printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
0203 if (!ret_sys)
0204 printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
0205 if (!ret_vdso)
0206 printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
0207 if (!ret_vsys)
0208 printf(" vsyscall: cpu %u, node %u", cpu_vsys,
0209 node_vsys);
0210 printf("\n");
0211
0212 if (!ok)
0213 nerrs++;
0214 }
0215 }
0216
0217 static bool ts_leq(const struct timespec *a, const struct timespec *b)
0218 {
0219 if (a->tv_sec != b->tv_sec)
0220 return a->tv_sec < b->tv_sec;
0221 else
0222 return a->tv_nsec <= b->tv_nsec;
0223 }
0224
0225 static bool ts64_leq(const struct __kernel_timespec *a,
0226 const struct __kernel_timespec *b)
0227 {
0228 if (a->tv_sec != b->tv_sec)
0229 return a->tv_sec < b->tv_sec;
0230 else
0231 return a->tv_nsec <= b->tv_nsec;
0232 }
0233
0234 static bool tv_leq(const struct timeval *a, const struct timeval *b)
0235 {
0236 if (a->tv_sec != b->tv_sec)
0237 return a->tv_sec < b->tv_sec;
0238 else
0239 return a->tv_usec <= b->tv_usec;
0240 }
0241
0242 static char const * const clocknames[] = {
0243 [0] = "CLOCK_REALTIME",
0244 [1] = "CLOCK_MONOTONIC",
0245 [2] = "CLOCK_PROCESS_CPUTIME_ID",
0246 [3] = "CLOCK_THREAD_CPUTIME_ID",
0247 [4] = "CLOCK_MONOTONIC_RAW",
0248 [5] = "CLOCK_REALTIME_COARSE",
0249 [6] = "CLOCK_MONOTONIC_COARSE",
0250 [7] = "CLOCK_BOOTTIME",
0251 [8] = "CLOCK_REALTIME_ALARM",
0252 [9] = "CLOCK_BOOTTIME_ALARM",
0253 [10] = "CLOCK_SGI_CYCLE",
0254 [11] = "CLOCK_TAI",
0255 };
0256
0257 static void test_one_clock_gettime(int clock, const char *name)
0258 {
0259 struct timespec start, vdso, end;
0260 int vdso_ret, end_ret;
0261
0262 printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
0263
0264 if (sys_clock_gettime(clock, &start) < 0) {
0265 if (errno == EINVAL) {
0266 vdso_ret = vdso_clock_gettime(clock, &vdso);
0267 if (vdso_ret == -EINVAL) {
0268 printf("[OK]\tNo such clock.\n");
0269 } else {
0270 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
0271 nerrs++;
0272 }
0273 } else {
0274 printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
0275 }
0276 return;
0277 }
0278
0279 vdso_ret = vdso_clock_gettime(clock, &vdso);
0280 end_ret = sys_clock_gettime(clock, &end);
0281
0282 if (vdso_ret != 0 || end_ret != 0) {
0283 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
0284 vdso_ret, errno);
0285 nerrs++;
0286 return;
0287 }
0288
0289 printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
0290 (unsigned long long)start.tv_sec, start.tv_nsec,
0291 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
0292 (unsigned long long)end.tv_sec, end.tv_nsec);
0293
0294 if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
0295 printf("[FAIL]\tTimes are out of sequence\n");
0296 nerrs++;
0297 return;
0298 }
0299
0300 printf("[OK]\tTest Passed.\n");
0301 }
0302
0303 static void test_clock_gettime(void)
0304 {
0305 if (!vdso_clock_gettime) {
0306 printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n");
0307 return;
0308 }
0309
0310 for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
0311 test_one_clock_gettime(clock, clocknames[clock]);
0312
0313
0314 test_one_clock_gettime(-1, "invalid");
0315 test_one_clock_gettime(INT_MIN, "invalid");
0316 test_one_clock_gettime(INT_MAX, "invalid");
0317 }
0318
0319 static void test_one_clock_gettime64(int clock, const char *name)
0320 {
0321 struct __kernel_timespec start, vdso, end;
0322 int vdso_ret, end_ret;
0323
0324 printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
0325
0326 if (sys_clock_gettime64(clock, &start) < 0) {
0327 if (errno == EINVAL) {
0328 vdso_ret = vdso_clock_gettime64(clock, &vdso);
0329 if (vdso_ret == -EINVAL) {
0330 printf("[OK]\tNo such clock.\n");
0331 } else {
0332 printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret);
0333 nerrs++;
0334 }
0335 } else {
0336 printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno);
0337 }
0338 return;
0339 }
0340
0341 vdso_ret = vdso_clock_gettime64(clock, &vdso);
0342 end_ret = sys_clock_gettime64(clock, &end);
0343
0344 if (vdso_ret != 0 || end_ret != 0) {
0345 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
0346 vdso_ret, errno);
0347 nerrs++;
0348 return;
0349 }
0350
0351 printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
0352 (unsigned long long)start.tv_sec, start.tv_nsec,
0353 (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
0354 (unsigned long long)end.tv_sec, end.tv_nsec);
0355
0356 if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) {
0357 printf("[FAIL]\tTimes are out of sequence\n");
0358 nerrs++;
0359 return;
0360 }
0361
0362 printf("[OK]\tTest Passed.\n");
0363 }
0364
0365 static void test_clock_gettime64(void)
0366 {
0367 if (!vdso_clock_gettime64) {
0368 printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n");
0369 return;
0370 }
0371
0372 for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
0373 test_one_clock_gettime64(clock, clocknames[clock]);
0374
0375
0376 test_one_clock_gettime64(-1, "invalid");
0377 test_one_clock_gettime64(INT_MIN, "invalid");
0378 test_one_clock_gettime64(INT_MAX, "invalid");
0379 }
0380
0381 static void test_gettimeofday(void)
0382 {
0383 struct timeval start, vdso, end;
0384 struct timezone sys_tz, vdso_tz;
0385 int vdso_ret, end_ret;
0386
0387 if (!vdso_gettimeofday)
0388 return;
0389
0390 printf("[RUN]\tTesting gettimeofday...\n");
0391
0392 if (sys_gettimeofday(&start, &sys_tz) < 0) {
0393 printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
0394 nerrs++;
0395 return;
0396 }
0397
0398 vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz);
0399 end_ret = sys_gettimeofday(&end, NULL);
0400
0401 if (vdso_ret != 0 || end_ret != 0) {
0402 printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
0403 vdso_ret, errno);
0404 nerrs++;
0405 return;
0406 }
0407
0408 printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n",
0409 (unsigned long long)start.tv_sec, start.tv_usec,
0410 (unsigned long long)vdso.tv_sec, vdso.tv_usec,
0411 (unsigned long long)end.tv_sec, end.tv_usec);
0412
0413 if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
0414 printf("[FAIL]\tTimes are out of sequence\n");
0415 nerrs++;
0416 }
0417
0418 if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
0419 sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
0420 printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
0421 sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
0422 } else {
0423 printf("[FAIL]\ttimezones do not match\n");
0424 nerrs++;
0425 }
0426
0427
0428 vdso_gettimeofday(&vdso, NULL);
0429 }
0430
0431 int main(int argc, char **argv)
0432 {
0433 name = (const char **)&names[VDSO_NAMES];
0434
0435 fill_function_pointers();
0436
0437 test_clock_gettime();
0438 test_clock_gettime64();
0439 test_gettimeofday();
0440
0441
0442
0443
0444
0445 test_getcpu();
0446
0447 return nerrs ? 1 : 0;
0448 }