The Virtual Dynamic Shared Object's Fallback Path: Why gettimeofday() Sometimes Enters the Kernel Anyway

2026-09-04

You already know the vDSO turns clock_gettime() into a user-space memory read of a kernel-maintained page. What's less advertised is that the vDSO code is a conditional — it reads a clocksource identifier, and if the identifier isn't one it knows how to handle, it falls through to a real syscall instruction. Your "zero-syscall" clock call can silently become a 300ns syscall depending on what your kernel picked as the clocksource at boot.

The mechanism: the kernel exposes a vdso_data page containing the current time, a sequence counter (for lockless reads), a multiplier/shift for TSC-to-nanoseconds conversion, and a vclock_mode field. The vDSO's __vdso_clock_gettime switches on vclock_mode: VCLOCK_TSC reads RDTSC and does the math inline; VCLOCK_PVCLOCK (KVM paravirt) and VCLOCK_HVCLOCK (Hyper-V) have their own inline paths; VCLOCK_NONE means "I can't do this in user space" and jumps to the syscall fallback.

When does the kernel pick VCLOCK_NONE? Any time the current clocksource isn't safe to read from user space. Common triggers:

Real-world example: A trading firm benchmarks clock_gettime(CLOCK_MONOTONIC) at 20ns on a dev box and deploys to a four-socket production server. Latency jumps to ~350ns per call — 17× slower. Cause: the four-socket box failed the TSC sync check at boot and fell back to HPET. Fix: echo tsc > /sys/devices/system/clocksource/*/current_clocksource after confirming synchronization, or boot with tsc=reliable.

Rule of thumb: before trusting vDSO performance, check cat /sys/devices/system/clocksource/clocksource0/current_clocksource. If it says tsc (or kvm-clock/hyperv_clocksource in a VM), you get the fast path. Anything else — hpet, acpi_pm, jiffies — and every call costs a syscall.

You can confirm the fallback empirically: strace -c ./your_program. If clock_gettime shows up in the syscall count at all, your vDSO isn't doing what you think it is.

Key Takeaway: The vDSO is a fast path with a syscall fallback, and which one you get depends on a boot-time clocksource decision you probably didn't make — always verify the clocksource before trusting your microbenchmarks.

All newsletters