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 clocksource whose central member is a ->read() callback returning a raw cycle count, plus the arithmetic (mult, shift, mask) to scale that count into nanoseconds (per Documentation/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 live curr_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 a u64. For the x86 Time Stamp Counter this is the RDTSC instruction; for the High Precision Event Timer it is a memory-mapped register read; for the ARM architected timer it reads the CNTVCT_EL0 system 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 has mask = 0x00FFFFFF; a full 64-bit counter has mask = 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) & mask still 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” (per timekeeping.rst).

  • mult and shift — the cycles→nanoseconds conversion factors. See the next section; this is the single most important arithmetic in the file.

  • max_idle_ns and max_cycles — the maximum deferment: the longest interval the kernel may let pass between counter reads before the multiply would overflow u64. 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 into mult without overflowing. The Network Time Protocol steers wall-clock time by nudging mult up or down; maxadj bounds 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 global clocksource_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:

  • cycles is the raw counter delta you got from ->read() (already masked).
  • mult is a pre-computed integer scale factor. Conceptually it encodes nanoseconds-per-cycle × 2^shift. For a 1 GHz counter, one cycle is exactly 1 ns, so the ideal ratio is 1.0; with shift = 24 you would store mult = 1 × 2^24 = 16777216.
  • >> shift divides by 2^shift, undoing the scaling baked into mult. 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 = 16777216 for a 1 GHz / shift = 24 source 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 actual mult/shift a given machine selects depends on clocks_calc_mult_shift()’s search and the chosen sec window. To resolve: read /sys/devices/system/clocksource/clocksource0/ or compare against a real dmesg “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		0x200
  • IS_CONTINUOUS — the counter never stops while the CPU runs; required to drive high-resolution timekeeping.
  • MUST_VERIFYthis 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 .rating initializer in each of those driver files at tag v6.12. The bands (1-99 … 400-499) above are quoted verbatim from clocksource.h and 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: dmesg shows clocksource: timekeeping watchdog on CPU…: Marking clocksource 'tsc' as unstable… followed by Switched 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 printed cs_nsec/wd_nsec skew in the warning. On hardware with an invariant TSC this is often spurious under heavy load and can be suppressed with tsc=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 mask wider than the real hardware width will, at wrap, compute a huge negative-looking delta and corrupt time. The mask must exactly match the counter’s valid bits — a classic clocksource-driver bug.
  • Multiply overflow / time stalls. If mult/shift are chosen for too long a validity window, cycles * mult overflows and time jumps. The clocks_calc_mult_shift() machinery prevents this when you register by frequency; hand-rolled mult/shift is where this bites.

Examples of Real Clocksources

  • Time Stamp Counter (TSC) — x86 on-die cycle counter read with RDTSC. Fastest possible (no bus access), but CLOCK_SOURCE_MUST_VERIFY because 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_EL0 virtual counter, the standard high-rating continuous clocksource on ARM/ARM64; it is also the clockevent source via CNTV_TVAL_EL0, illustrating how one timer block serves both roles.

See Also