Clocksources
A clocksource is the kernel’s abstraction of a free-running, monotonically increasing hardware counter — the thing you read to answer “what time is it?” Concretely it is a
struct clocksourcewhose central member is a->read()callback returning a raw cycle count, plus the arithmetic (mult,shift,mask) to scale that count into nanoseconds (perDocumentation/timers/timekeeping.rst: a clocksource “provides a timeline for the system that tells you where you are in time”). The kernel registers several clocksources (the x86 Time Stamp Counter, the High Precision Event Timer, the ACPI Power-Management timer, the ARM architected timer), rates each one, and runs the best-rated as the livecurr_clocksource. A watchdog continuously cross-checks the chosen counter against a known-good reference and demotes it if it drifts — the mechanism that catches a misbehaving Time Stamp Counter and forces a fallback. This note covers the v6.12 long-term-support kernel; the structure has been stable for many releases.
A clocksource is exactly half of the kernel’s hardware-time story. It answers “what time is it now?” Its dual — a programmable device that fires an interrupt at a future instant — is the clockevent device. The same silicon (a High Precision Event Timer, an ARM architected timer) often provides both roles, but the two roles are distinct interfaces with distinct data structures. Keep that split in mind throughout.
Mental Model
Think of a clocksource as an odometer that only ever counts up, ticking at some fixed hardware frequency, and that wraps back to zero when it overflows its bit-width. The kernel never sets it and never resets it; it only reads it. Each read yields a raw cycle count. To turn cycles into nanoseconds the kernel does one multiply and one shift — deliberately cheap, because the timekeeping core does this on every clock update and the fast clock_gettime() path does it on every call.
flowchart TB HW["Hardware counter<br/>(TSC / HPET / arch timer / PM-timer)"] HW -->|"->read() returns raw cycles"| RAW["raw cycle count<br/>& cs->mask"] RAW -->|"ns = (cycles * mult) >> shift"| NS["nanoseconds delta"] NS --> TK["timekeeping core<br/>accumulates into wall/monotonic time"] REG["clocksource_list<br/>(sorted by rating)"] -->|"highest rating wins"| CURR["curr_clocksource<br/>(the live one)"] WD["watchdog timer<br/>every 0.5s"] -->|"compare current vs reference"| CHECK{"skew ><br/>uncertainty margin?"} CHECK -->|yes| DEMOTE["__clocksource_unstable():<br/>clear VALID_FOR_HRES,<br/>rating := 0, reselect"] CHECK -->|no| OK["keep using it"]
How a clocksource feeds the kernel and how it is policed. What it shows: the live counter is read, scaled by mult/shift into nanoseconds, and fed to the timekeeping core; meanwhile the registered list is kept sorted by rating so the best one is selected, and a periodic watchdog compares the live counter against a reference and demotes it on excessive skew. The insight to take: a clocksource is trusted only as long as it agrees with an independent reference — the kernel does not assume the hardware is honest, it verifies it twice a second.
The Structure — struct clocksource
The whole abstraction is one structure. Here it is verbatim from include/linux/clocksource.h (v6.12), with the load-bearing fields:
struct clocksource {
u64 (*read)(struct clocksource *cs);
u64 mask;
u32 mult;
u32 shift;
u64 max_idle_ns;
u32 maxadj;
u32 uncertainty_margin;
/* ... arch data ... */
u64 max_cycles;
const char *name;
struct list_head list;
u32 freq_khz;
int rating;
enum clocksource_ids id;
enum vdso_clock_mode vdso_clock_mode;
unsigned long flags;
struct clocksource_base *base;
int (*enable)(struct clocksource *cs);
void (*disable)(struct clocksource *cs);
void (*suspend)(struct clocksource *cs);
void (*resume)(struct clocksource *cs);
void (*mark_unstable)(struct clocksource *cs);
void (*tick_stable)(struct clocksource *cs);
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
struct list_head wd_list;
u64 cs_last;
u64 wd_last;
#endif
struct module *owner;
};Walking the fields that matter:
-
->read(cs)— the heart. It returns the current raw counter value as au64. For the x86 Time Stamp Counter this is theRDTSCinstruction; for the High Precision Event Timer it is a memory-mapped register read; for the ARM architected timer it reads theCNTVCT_EL0system register. The contract is that successive reads are non-decreasing (modulo wraparound) and cheap. -
mask— the bit-width of the counter, expressed as a mask of the valid bits. A 24-bit counter hasmask = 0x00FFFFFF; a full 64-bit counter hasmask = CLOCKSOURCE_MASK(64). The kernel ANDs every read delta with this so that when the hardware counter wraps from its maximum back to zero, the subtraction(now - last) & maskstill yields the correct positive elapsed-cycle count. The documentation makes the stakes explicit: a 32-bit counter at 100 MHz wraps after about 43 seconds, and “the timekeeping code knows when the counter will wrap around and can insert the necessary compensation code on both sides of the wrap point so that the system timeline remains monotonic” (pertimekeeping.rst). -
multandshift— the cycles→nanoseconds conversion factors. See the next section; this is the single most important arithmetic in the file. -
max_idle_nsandmax_cycles— the maximum deferment: the longest interval the kernel may let pass between counter reads before the multiply would overflowu64. This is why a low-bit-width counter forces the kernel to read it more often, and why it limits how long a CPU can stay tickless. -
maxadj— the maximum NTP adjustment that may be folded intomultwithout overflowing. The Network Time Protocol steers wall-clock time by nudgingmultup or down;maxadjbounds that nudge. -
uncertainty_margin— how far this clocksource may legitimately disagree with the watchdog before being declared unstable (in nanoseconds). It is initialized from the hardware’s known precision; a coarse source gets a larger margin. -
rating— an integer 1–499 expressing desirability; higher wins. The comment block defines the bands precisely. -
flags— bitfield of properties (CLOCK_SOURCE_IS_CONTINUOUS,CLOCK_SOURCE_VALID_FOR_HRES,CLOCK_SOURCE_MUST_VERIFY,CLOCK_SOURCE_UNSTABLE, …) discussed below. -
list— links the clocksource into the globalclocksource_list, kept sorted by rating. -
wd_list,cs_last,wd_last— private watchdog bookkeeping: the last-seen value of this clocksource and the corresponding value of the watchdog reference, used to compute drift.
The mult/shift Conversion, Symbol by Symbol
Floating point is forbidden in most kernel context, and a 64-bit integer divide is expensive. So the kernel converts cycles to nanoseconds with one multiply and one right-shift — fixed-point arithmetic. The canonical inline from clocksource.h:
static inline s64 clocksource_cyc2ns(u64 cycles, u32 mult, u32 shift)
{
return ((u64) cycles * mult) >> shift;
}Read the formula ns = (cycles * mult) >> shift symbol by symbol:
cyclesis the raw counter delta you got from->read()(already masked).multis a pre-computed integer scale factor. Conceptually it encodesnanoseconds-per-cycle × 2^shift. For a 1 GHz counter, one cycle is exactly 1 ns, so the ideal ratio is 1.0; withshift = 24you would storemult = 1 × 2^24 = 16777216.>> shiftdivides by2^shift, undoing the scaling baked intomult. The shift is a power-of-two divide, which is a single CPU instruction.
Why store the factor scaled up by 2^shift instead of just dividing? Because most cycle-to-ns ratios are not integers. A 3.4 GHz Time Stamp Counter ticks every ~0.294 ns; you cannot represent 0.294 as an integer. By multiplying the desired ratio by 2^shift you push that fraction into the high bits of mult, do an exact integer multiply, then shift the fraction back out. A larger shift keeps more fractional precision — but cycles * mult must not overflow u64. That trade-off is exactly what clocks_calc_mult_shift() resolves.
When you register a clocksource by frequency, the kernel computes mult/shift for you in __clocksource_update_freq_scale() (clocksource.c). It picks the conversion validity window sec (how many seconds of accumulation the conversion must stay accurate over), then:
sec = cs->mask;
do_div(sec, freq);
do_div(sec, scale);
if (!sec)
sec = 1;
else if (sec > 600 && cs->mask > UINT_MAX)
sec = 600;
clocks_calc_mult_shift(&cs->mult, &cs->shift, freq,
NSEC_PER_SEC / scale, sec * scale);The first lines compute how long the counter can run before it wraps (mask / freq seconds), capping the window at 600 seconds for wide (>32-bit) counters because there is no benefit to a longer validity horizon. Then clocks_calc_mult_shift() searches shift values from 32 downward, choosing the largest shift (most precision) for which freq × mult over sec seconds still fits in 64 bits. The driver-facing helpers clocksource_register_hz() and clocksource_register_khz() wrap this so a driver typically writes nothing more than clocksource_register_hz(&my_cs, 24000000).
Uncertain
Verify: the worked numeric example “3.4 GHz Time Stamp Counter ticks every ~0.294 ns” and the specific
mult = 16777216for a 1 GHz /shift = 24source are illustrative arithmetic I computed, not values read from a kernel boot log. The arithmetic is straightforward (1e9/3.4e9 ≈ 0.294;2^24 = 16777216) but the actualmult/shifta given machine selects depends onclocks_calc_mult_shift()’s search and the chosensecwindow. To resolve: read/sys/devices/system/clocksource/clocksource0/or compare against a realdmesg“Switched to clocksource tsc” line plus the per-clocksource mult/shift, which are not exported by name in 6.12 sysfs.
Max deferment — why mask bounds tickless idle
Because cycles * mult must fit in u64, there is a hard ceiling on how large cycles may grow before a read — i.e. how long the kernel may go without reading the counter. clocks_calc_max_nsecs() computes it (clocksource.c):
max_cycles = ULLONG_MAX;
do_div(max_cycles, mult + maxadj); /* largest count that won't overflow */
max_cycles = min(max_cycles, mask); /* but also can't exceed the bit-width */
max_nsecs = clocksource_cyc2ns(max_cycles, mult - maxadj, shift);
max_nsecs >>= 1; /* halve for safety margin */max_cycles is the smaller of (a) the count at which count * (mult+maxadj) would overflow and (b) the counter’s own bit-width mask. The result, halved for headroom, lands in cs->max_idle_ns. This is the direct link from clocksource bit-width to power management: a narrow counter has a small max_idle_ns, so an idle CPU running tickless (NOHZ) must wake to re-read it sooner, capping how deep and how long it can sleep.
Flags and Rating
The flags field carries the kernel’s beliefs about a clocksource. From clocksource.h:
#define CLOCK_SOURCE_IS_CONTINUOUS 0x01
#define CLOCK_SOURCE_MUST_VERIFY 0x02
#define CLOCK_SOURCE_WATCHDOG 0x10
#define CLOCK_SOURCE_VALID_FOR_HRES 0x20
#define CLOCK_SOURCE_UNSTABLE 0x40
#define CLOCK_SOURCE_SUSPEND_NONSTOP 0x80
#define CLOCK_SOURCE_RESELECT 0x100
#define CLOCK_SOURCE_VERIFY_PERCPU 0x200IS_CONTINUOUS— the counter never stops while the CPU runs; required to drive high-resolution timekeeping.MUST_VERIFY— this clocksource is not trusted on its own and must be policed by the watchdog. The Time Stamp Counter carries this on many systems precisely because it is known to misbehave.WATCHDOG— internal: the watchdog has taken a first sample and is now actively monitoring this source.VALID_FOR_HRES— eligible to back high-resolution timers and tickless operation. The watchdog clears this the instant it demotes a source.UNSTABLE— set when the watchdog has caught the source drifting; it will be re-rated to 0 and dropped.SUSPEND_NONSTOP— keeps counting across system suspend (lets the kernel measure how long it was asleep).VERIFY_PERCPU— for the Time Stamp Counter: also cross-check the counter across CPUs, because a per-core TSC can be individually skewed even when each core looks fine against the watchdog.
The rating decides selection. The comment block defines the bands verbatim:
* 1-99: Unfit for real use — only available for bootup and testing.
* 100-199: Base level usability — functional, but not desired.
* 200-299: Good — a correct and usable clocksource.
* 300-399: Desired — a reasonably fast and accurate clocksource.
* 400-499: Perfect — the ideal clocksource; a must-use where available.
Concrete examples (ratings as set by the respective drivers): the ACPI Power-Management timer rates 200 (correct but slow — it is a memory-mapped I/O read at ~3.58 MHz); the High Precision Event Timer rates 250; the x86 Time Stamp Counter rates 300 when deemed reliable (fast, on-die, no bus access). The detailed rating mechanics and per-architecture values live in the sibling note Clocksource Rating and Selection.
Uncertain
Verify: the specific numeric ratings “PM-timer 200, HPET 250, TSC 300”. Reason: these are set in per-driver registration code (
arch/x86/kernel/tsc.c,drivers/clocksource/acpi_pm.c,arch/x86/kernel/hpet.c), which I did not fetch in this task — they are stated from memory and are exactly the kind of value the vault’s conventions say to verify against the source. To resolve: read the.ratinginitializer in each of those driver files at tagv6.12. The bands (1-99 … 400-499) above are quoted verbatim fromclocksource.hand are reliable.
Registration and Selection
A driver fills in a struct clocksource and calls clocksource_register_hz(). Registration inserts it into the global list sorted by rating, so the head of the list is always the best candidate. The insertion, verbatim from clocksource.c:
static void clocksource_enqueue(struct clocksource *cs)
{
struct list_head *entry = &clocksource_list;
struct clocksource *tmp;
list_for_each_entry(tmp, &clocksource_list, list) {
if (tmp->rating < cs->rating)
break;
entry = &tmp->list;
}
list_add(&cs->list, entry);
}Selection then walks the list and takes the first suitable entry (clocksource_find_best()): “We pick the clocksource with the highest rating. If oneshot mode is active, we pick the highres valid clocksource with the best rating.” In other words, when the system is in high-resolution / tickless mode, a source lacking CLOCK_SOURCE_VALID_FOR_HRES is skipped no matter how high its rating. The chosen source is committed via timekeeping_notify() and recorded in the global curr_clocksource, with a dmesg line like Switched to clocksource tsc. A user can force a choice by writing a name to /sys/devices/system/clocksource/clocksource0/current_clocksource, which sets override_name; the override is honored only if it is HRES-valid while in oneshot mode.
The Watchdog — Catching a Drifting Counter
This is the most consequential part of the subsystem. A fast clocksource like the Time Stamp Counter is attractive but historically unreliable — it can change frequency with CPU power states, stop in deep sleep, or differ between cores. So when a source carries CLOCK_SOURCE_MUST_VERIFY, the kernel runs a watchdog: a periodic timer that reads both the suspect clocksource and a known-good reference (typically the High Precision Event Timer or the ACPI PM-timer, which are slow but trustworthy) and compares how much time each thinks elapsed.
The constants, verbatim from clocksource.c:
#define WATCHDOG_INTERVAL (HZ >> 1) /* check every 0.5 second */
#define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 5) /* ~31.25 ms */
#define MAX_SKEW_USEC (125 * WATCHDOG_INTERVAL / HZ)
#define WATCHDOG_MAX_SKEW (MAX_SKEW_USEC * NSEC_PER_USEC) /* 500 ppm floor */The check fires twice a second. The default skew tolerance bottoms out at 500 parts-per-million (MAX_SKEW_USEC works out to ~62.5 µs over the 0.5 s interval, i.e. 500 ppm), overridable via the CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US Kconfig. The core comparison, verbatim:
wd_nsec = cycles_to_nsec_safe(watchdog, cs->wd_last, wdnow);
cs_nsec = cycles_to_nsec_safe(cs, cs->cs_last, csnow);
/* ... */
md = cs->uncertainty_margin + watchdog->uncertainty_margin;
if (abs(cs_nsec - wd_nsec) > md) {
pr_warn("timekeeping watchdog on CPU%d: Marking clocksource '%s' as unstable because the skew is too large:\n",
smp_processor_id(), cs->name);
/* ... detailed skew report ... */
__clocksource_unstable(cs);
continue;
}Step by step: the watchdog converts the elapsed cycles of both the candidate (cs_nsec) and the reference (wd_nsec) to nanoseconds over the same wall interval. The tolerance md is the sum of both sources’ uncertainty margins — neither is assumed perfect. If the absolute difference exceeds md, the candidate is drifting and gets marked unstable.
Demotion, verbatim:
static void __clocksource_unstable(struct clocksource *cs)
{
cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
cs->flags |= CLOCK_SOURCE_UNSTABLE;
if (list_empty(&cs->list)) {
cs->rating = 0;
return;
}
if (cs->mark_unstable)
cs->mark_unstable(cs);
/* kick clocksource_watchdog_kthread() */
if (finished_booting)
schedule_work(&watchdog_work);
}It clears VALID_FOR_HRES (so the source can no longer back high-res timers), sets UNSTABLE, optionally calls the driver’s mark_unstable() hook, and schedules a kernel worker. That worker, clocksource_watchdog_kthread(), re-rates the source to 0 (via __clocksource_change_rating(cs, 0)) and triggers a fresh selection — so the kernel automatically falls back to the next-best source (commonly the HPET). This is exactly the famous “Clocksource tsc unstable” / “Switched to clocksource hpet” sequence seen in dmesg on machines with a flaky Time Stamp Counter. The watchdog also handles overload gracefully: if reading the counters itself takes too long (WD_READ_SKIP, e.g. under extreme load), it suspends checks for 5 minutes rather than falsely accusing a healthy source.
Failure Modes and Diagnosis
- TSC marked unstable, fell back to HPET. Symptom:
dmesgshowsclocksource: timekeeping watchdog on CPU…: Marking clocksource 'tsc' as unstable…followed bySwitched to clocksource hpet. Cause: the Time Stamp Counter genuinely drifted (frequency change with power states, multi-socket skew) or virtualization/SMI interference inflated a single watchdog read. Diagnose by reading the printedcs_nsec/wd_nsecskew in the warning. On hardware with an invariant TSC this is often spurious under heavy load and can be suppressed withtsc=reliable(which trusts the TSC and skips verification) — at the cost of losing the safety net. - No high-resolution timers. If the only available continuous source lacks
CLOCK_SOURCE_VALID_FOR_HRES, the kernel cannot enter high-resolution / tickless mode;clock_gettime()falls back to coarser jiffies-based time. Check/sys/devices/system/clocksource/clocksource0/available_clocksource. - Counter wrap mishandled. A driver that sets
maskwider than the real hardware width will, at wrap, compute a huge negative-looking delta and corrupt time. Themaskmust exactly match the counter’s valid bits — a classic clocksource-driver bug. - Multiply overflow / time stalls. If
mult/shiftare chosen for too long a validity window,cycles * multoverflows and time jumps. Theclocks_calc_mult_shift()machinery prevents this when you register by frequency; hand-rolledmult/shiftis where this bites.
Examples of Real Clocksources
- Time Stamp Counter (TSC) — x86 on-die cycle counter read with
RDTSC. Fastest possible (no bus access), butCLOCK_SOURCE_MUST_VERIFYbecause it can drift; see The Time Stamp Counter and TSC Reliability for the full reliability story. - High Precision Event Timer (HPET) — memory-mapped, typically 14.318 MHz, very stable; the usual watchdog reference and the usual TSC fallback. Detailed in High Precision Event Timer.
- ACPI Power-Management Timer (acpi_pm) — a ~3.579545 MHz memory-mapped counter present on essentially all PCs; slow to read (an I/O access) but reliable, hence rating ~200 and frequent use as the watchdog of last resort.
- ARM architected timer — the
CNTVCT_EL0virtual counter, the standard high-rating continuous clocksource on ARM/ARM64; it is also the clockevent source viaCNTV_TVAL_EL0, illustrating how one timer block serves both roles.
See Also
- Clockevent Devices — the dual abstraction: a programmable device that fires an interrupt at a future instant (this note’s counterpart)
- Clocksource Rating and Selection — deeper on the rating bands, per-driver values, and the selection algorithm
- The Time Stamp Counter and TSC Reliability — why the TSC is the canonical
MUST_VERIFYclocksource and what makes it trustworthy - High Precision Event Timer — the usual watchdog reference and TSC fallback
- The Scheduling-Clock Tick — consumes clockevent interrupts; reads the clocksource to account time
- High-Resolution Timers and hrtimers — require a
VALID_FOR_HRESclocksource and a oneshot clockevent - Linux Time and Timers MOC — parent map (section A: the hardware layer)