[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID:
<SN6PR02MB41576973AC66F8515F6C81F0D4C82@SN6PR02MB4157.namprd02.prod.outlook.com>
Date: Tue, 4 Mar 2025 04:17:34 +0000
From: Michael Kelley <mhklinux@...look.com>
To: Sean Christopherson <seanjc@...gle.com>, Thomas Gleixner
<tglx@...utronix.de>, Ingo Molnar <mingo@...hat.com>, Borislav Petkov
<bp@...en8.de>, Dave Hansen <dave.hansen@...ux.intel.com>, "x86@...nel.org"
<x86@...nel.org>, "Kirill A. Shutemov" <kirill.shutemov@...ux.intel.com>,
Paolo Bonzini <pbonzini@...hat.com>, Juergen Gross <jgross@...e.com>, "K. Y.
Srinivasan" <kys@...rosoft.com>, Haiyang Zhang <haiyangz@...rosoft.com>, Wei
Liu <wei.liu@...nel.org>, Dexuan Cui <decui@...rosoft.com>, Ajay Kaher
<ajay.kaher@...adcom.com>, Jan Kiszka <jan.kiszka@...mens.com>, Andy
Lutomirski <luto@...nel.org>, Peter Zijlstra <peterz@...radead.org>, Daniel
Lezcano <daniel.lezcano@...aro.org>, John Stultz <jstultz@...gle.com>
CC: "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"linux-coco@...ts.linux.dev" <linux-coco@...ts.linux.dev>,
"kvm@...r.kernel.org" <kvm@...r.kernel.org>, "virtualization@...ts.linux.dev"
<virtualization@...ts.linux.dev>, "linux-hyperv@...r.kernel.org"
<linux-hyperv@...r.kernel.org>, "xen-devel@...ts.xenproject.org"
<xen-devel@...ts.xenproject.org>, Tom Lendacky <thomas.lendacky@....com>,
Nikunj A Dadhania <nikunj@....com>
Subject: RE: [PATCH v2 08/38] clocksource: hyper-v: Register sched_clock
save/restore iff it's necessary
From: Sean Christopherson <seanjc@...gle.com> Sent: Wednesday, February 26, 2025 6:18 PM
>
> Register the Hyper-V timer callbacks or saving/restoring its PV sched_clock
s/or/for/
> if and only if the timer is actually being used for sched_clock.
> Currently, Hyper-V overrides the save/restore hooks if the reference TSC
> available, whereas the Hyper-V timer code only overrides sched_clock if
> the reference TSC is available *and* it's not invariant. The flaw is
> effectively papered over by invoking the "old" save/restore callbacks as
> part of save/restore, but that's unnecessary and fragile.
The Hyper-V specific terminology here isn't quite right. There is a
PV "Hyper-V timer", but it is loaded by the guest OS with a specific value
and generates an interrupt when that value is reached. In Linux, it is used
for clockevents, but it's not a clocksource and is not used for sched_clock.
The correct Hyper-V term is "Hyper-V reference counter" (or "refcounter"
for short). The refcounter behaves like the TSC -- it's a monotonically
increasing value that is read-only, and can serve as the sched_clock.
And yes, both the Hyper-V timer and Hyper-V refcounter code is in a
source file with a name containing "timer" but not "refcounter". But
that seems to be the pattern for many of the drivers in
drivers/clocksource. :-)
>
> To avoid introducing more complexity, and to allow for additional cleanups
> of the PV sched_clock code, move the save/restore hooks and logic into
> hyperv_timer.c and simply wire up the hooks when overriding sched_clock
> itself.
>
> Note, while the Hyper-V timer code is intended to be architecture neutral,
> CONFIG_PARAVIRT is firmly x86-only, i.e. adding a small amount of x86
> specific code (which will be reduced in future cleanups) doesn't
> meaningfully pollute generic code.
I'm good with this approach.
>
> Signed-off-by: Sean Christopherson <seanjc@...gle.com>
Modulo the terminology used in the commit message,
Reviewed-by: Michael Kelley <mhklinux@...look.com>
Tested-by: Michael Kelley <mhklinux@...look.com>
> ---
> arch/x86/kernel/cpu/mshyperv.c | 58 ------------------------------
> drivers/clocksource/hyperv_timer.c | 50 ++++++++++++++++++++++++++
> 2 files changed, 50 insertions(+), 58 deletions(-)
>
> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
> index aa60491bf738..174f6a71c899 100644
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -223,63 +223,6 @@ static void hv_machine_crash_shutdown(struct pt_regs *regs)
> hyperv_cleanup();
> }
> #endif /* CONFIG_CRASH_DUMP */
> -
> -static u64 hv_ref_counter_at_suspend;
> -static void (*old_save_sched_clock_state)(void);
> -static void (*old_restore_sched_clock_state)(void);
> -
> -/*
> - * Hyper-V clock counter resets during hibernation. Save and restore clock
> - * offset during suspend/resume, while also considering the time passed
> - * before suspend. This is to make sure that sched_clock using hv tsc page
> - * based clocksource, proceeds from where it left off during suspend and
> - * it shows correct time for the timestamps of kernel messages after resume.
> - */
> -static void save_hv_clock_tsc_state(void)
> -{
> - hv_ref_counter_at_suspend = hv_read_reference_counter();
> -}
> -
> -static void restore_hv_clock_tsc_state(void)
> -{
> - /*
> - * Adjust the offsets used by hv tsc clocksource to
> - * account for the time spent before hibernation.
> - * adjusted value = reference counter (time) at suspend
> - * - reference counter (time) now.
> - */
> - hv_adj_sched_clock_offset(hv_ref_counter_at_suspend -
> hv_read_reference_counter());
> -}
> -
> -/*
> - * Functions to override save_sched_clock_state and restore_sched_clock_state
> - * functions of x86_platform. The Hyper-V clock counter is reset during
> - * suspend-resume and the offset used to measure time needs to be
> - * corrected, post resume.
> - */
> -static void hv_save_sched_clock_state(void)
> -{
> - old_save_sched_clock_state();
> - save_hv_clock_tsc_state();
> -}
> -
> -static void hv_restore_sched_clock_state(void)
> -{
> - restore_hv_clock_tsc_state();
> - old_restore_sched_clock_state();
> -}
> -
> -static void __init x86_setup_ops_for_tsc_pg_clock(void)
> -{
> - if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
> - return;
> -
> - old_save_sched_clock_state = x86_platform.save_sched_clock_state;
> - x86_platform.save_sched_clock_state = hv_save_sched_clock_state;
> -
> - old_restore_sched_clock_state = x86_platform.restore_sched_clock_state;
> - x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state;
> -}
> #endif /* CONFIG_HYPERV */
>
> static uint32_t __init ms_hyperv_platform(void)
> @@ -635,7 +578,6 @@ static void __init ms_hyperv_init_platform(void)
>
> /* Register Hyper-V specific clocksource */
> hv_init_clocksource();
> - x86_setup_ops_for_tsc_pg_clock();
> hv_vtl_init_platform();
> #endif
> /*
> diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c
> index f00019b078a7..86a55167bf5d 100644
> --- a/drivers/clocksource/hyperv_timer.c
> +++ b/drivers/clocksource/hyperv_timer.c
> @@ -534,10 +534,60 @@ static __always_inline void hv_setup_sched_clock(void
> *sched_clock)
> sched_clock_register(sched_clock, 64, NSEC_PER_SEC);
> }
> #elif defined CONFIG_PARAVIRT
> +static u64 hv_ref_counter_at_suspend;
> +static void (*old_save_sched_clock_state)(void);
> +static void (*old_restore_sched_clock_state)(void);
> +
> +/*
> + * Hyper-V clock counter resets during hibernation. Save and restore clock
> + * offset during suspend/resume, while also considering the time passed
> + * before suspend. This is to make sure that sched_clock using hv tsc page
> + * based clocksource, proceeds from where it left off during suspend and
> + * it shows correct time for the timestamps of kernel messages after resume.
> + */
> +static void save_hv_clock_tsc_state(void)
> +{
> + hv_ref_counter_at_suspend = hv_read_reference_counter();
> +}
> +
> +static void restore_hv_clock_tsc_state(void)
> +{
> + /*
> + * Adjust the offsets used by hv tsc clocksource to
> + * account for the time spent before hibernation.
> + * adjusted value = reference counter (time) at suspend
> + * - reference counter (time) now.
> + */
> + hv_adj_sched_clock_offset(hv_ref_counter_at_suspend -
> hv_read_reference_counter());
> +}
> +/*
> + * Functions to override save_sched_clock_state and restore_sched_clock_state
> + * functions of x86_platform. The Hyper-V clock counter is reset during
> + * suspend-resume and the offset used to measure time needs to be
> + * corrected, post resume.
> + */
> +static void hv_save_sched_clock_state(void)
> +{
> + old_save_sched_clock_state();
> + save_hv_clock_tsc_state();
> +}
> +
> +static void hv_restore_sched_clock_state(void)
> +{
> + restore_hv_clock_tsc_state();
> + old_restore_sched_clock_state();
> +}
> +
> static __always_inline void hv_setup_sched_clock(void *sched_clock)
> {
> /* We're on x86/x64 *and* using PV ops */
> paravirt_set_sched_clock(sched_clock);
> +
> + old_save_sched_clock_state = x86_platform.save_sched_clock_state;
> + x86_platform.save_sched_clock_state = hv_save_sched_clock_state;
> +
> + old_restore_sched_clock_state = x86_platform.restore_sched_clock_state;
> + x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state;
> }
> #else /* !CONFIG_GENERIC_SCHED_CLOCK && !CONFIG_PARAVIRT */
> static __always_inline void hv_setup_sched_clock(void *sched_clock) {}
> --
> 2.48.1.711.g2feabab25a-goog
>
Powered by blists - more mailing lists