summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw@amazon.co.uk>2026-06-04 10:35:16 +0100
committerThomas Gleixner <tglx@kernel.org>2026-06-05 14:25:03 +0200
commitc51100f9e26857f2b2376d5cd657a15f52b9e05c (patch)
treeb9630f53aca5f8fb6c3b25c7fb87f3db7380968d
parentca1ec8bfac8c95d0fed9e3611ea21400d1f37262 (diff)
clocksource/hyperv: Implement read_snapshot() for TSC page clocksource
Implement the read_snapshot() callback for the Hyper-V TSC page clock- source. This returns the derived 10MHz reference time (for timekeeping) while also providing the raw TSC value that was used to compute it. When the TSC page is valid, hv_read_tsc_page_tsc() atomically captures both values from a single RDTSC inside the sequence-counter protected read. When the TSC page is invalid (sequence == 0), the hw_csid and hw_cycles are set to zero indicating no value is available. This enables ktime_get_snapshot_id() to provide the raw TSC to consumers like KVM's master clock when running nested guests under Hyper-V. Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Assisted-by: Kiro:claude-opus-4.6-1m Reviewed-by: Michael Kelley <mhklinux@outlook.com> Link: https://patch.msgid.link/20260604095755.64849-2-dwmw2@infradead.org
-rw-r--r--drivers/clocksource/hyperv_timer.c37
1 files changed, 27 insertions, 10 deletions
diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c
index e9f5034a1bc8..df567795d175 100644
--- a/drivers/clocksource/hyperv_timer.c
+++ b/drivers/clocksource/hyperv_timer.c
@@ -444,6 +444,22 @@ static u64 notrace read_hv_clock_tsc_cs(struct clocksource *arg)
return read_hv_clock_tsc();
}
+static u64 notrace read_hv_clock_tsc_cs_snapshot(struct clocksource *arg,
+ struct clocksource_hw_snapshot *chs)
+{
+ u64 time;
+
+ if (hv_read_tsc_page_tsc(tsc_page, &chs->hw_cycles, &time)) {
+ chs->hw_csid = CSID_X86_TSC;
+ } else {
+ chs->hw_cycles = 0;
+ chs->hw_csid = CSID_GENERIC;
+ time = read_hv_clock_msr();
+ }
+
+ return time;
+}
+
static u64 noinstr read_hv_sched_clock_tsc(void)
{
return (read_hv_clock_tsc() - hv_sched_clock_offset) *
@@ -492,18 +508,19 @@ static int hv_cs_enable(struct clocksource *cs)
#endif
static struct clocksource hyperv_cs_tsc = {
- .name = "hyperv_clocksource_tsc_page",
- .rating = 500,
- .read = read_hv_clock_tsc_cs,
- .mask = CLOCKSOURCE_MASK(64),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
- .suspend= suspend_hv_clock_tsc,
- .resume = resume_hv_clock_tsc,
+ .name = "hyperv_clocksource_tsc_page",
+ .rating = 500,
+ .read = read_hv_clock_tsc_cs,
+ .read_snapshot = read_hv_clock_tsc_cs_snapshot,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .suspend = suspend_hv_clock_tsc,
+ .resume = resume_hv_clock_tsc,
#ifdef HAVE_VDSO_CLOCKMODE_HVCLOCK
- .enable = hv_cs_enable,
- .vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK,
+ .enable = hv_cs_enable,
+ .vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK,
#else
- .vdso_clock_mode = VDSO_CLOCKMODE_NONE,
+ .vdso_clock_mode = VDSO_CLOCKMODE_NONE,
#endif
};