From: David Woodhouse <dwmw2@xxxxxxxxxxxxx> Sent: Friday, August 2, 2024 6:56 AM > > Leaving the PIT interrupt running can cause noticeable steal time for > virtual guests. The VMM generally has a timer which toggles the IRQ input > to the PIC and I/O APIC, which takes CPU time away from the guest. Even > on real hardware, running the counter may use power needlessly (albeit > not much). > > Make sure it's turned off if it isn't going to be used. > > Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx> > --- > arch/x86/kernel/i8253.c | 11 +++++++++-- > drivers/clocksource/i8253.c | 13 +++++++++---- > include/linux/i8253.h | 1 + > 3 files changed, 19 insertions(+), 6 deletions(-) > > diff --git a/arch/x86/kernel/i8253.c b/arch/x86/kernel/i8253.c > index 2b7999a1a50a..80e262bb627f 100644 > --- a/arch/x86/kernel/i8253.c > +++ b/arch/x86/kernel/i8253.c > @@ -8,6 +8,7 @@ > #include <linux/timex.h> > #include <linux/i8253.h> > > +#include <asm/hypervisor.h> > #include <asm/apic.h> > #include <asm/hpet.h> > #include <asm/time.h> > @@ -39,9 +40,15 @@ static bool __init use_pit(void) > > bool __init pit_timer_init(void) > { > - if (!use_pit()) > + if (!use_pit()) { > + /* > + * Don't just ignore the PIT. Ensure it's stopped, because > + * VMMs otherwise steal CPU time just to pointlessly waggle > + * the (masked) IRQ. > + */ > + clockevent_i8253_disable(); > return false; > - > + } > clockevent_i8253_init(true); > global_clock_event = &i8253_clockevent; > return true; > diff --git a/drivers/clocksource/i8253.c b/drivers/clocksource/i8253.c > index d4350bb10b83..cb215e6f2e83 100644 > --- a/drivers/clocksource/i8253.c > +++ b/drivers/clocksource/i8253.c > @@ -108,11 +108,8 @@ int __init clocksource_i8253_init(void) > #endif > > #ifdef CONFIG_CLKEVT_I8253 > -static int pit_shutdown(struct clock_event_device *evt) > +void clockevent_i8253_disable(void) > { > - if (!clockevent_state_oneshot(evt) && !clockevent_state_periodic(evt)) > - return 0; > - > raw_spin_lock(&i8253_lock); > > outb_p(0x30, PIT_MODE); > @@ -123,6 +120,14 @@ static int pit_shutdown(struct clock_event_device *evt) > } > > raw_spin_unlock(&i8253_lock); > +} > + > +static int pit_shutdown(struct clock_event_device *evt) > +{ > + if (!clockevent_state_oneshot(evt) && !clockevent_state_periodic(evt)) > + return 0; > + > + clockevent_i8253_disable(); > return 0; > } > > diff --git a/include/linux/i8253.h b/include/linux/i8253.h > index 8336b2f6f834..bf169cfef7f1 100644 > --- a/include/linux/i8253.h > +++ b/include/linux/i8253.h > @@ -24,6 +24,7 @@ extern raw_spinlock_t i8253_lock; > extern bool i8253_clear_counter_on_shutdown; > extern struct clock_event_device i8253_clockevent; > extern void clockevent_i8253_init(bool oneshot); > +extern void clockevent_i8253_disable(void); > > extern void setup_pit_timer(void); > > -- > 2.44.0 Did a basic smoke test of this two-patch series on a Hyper-V Gen 1 VM and on a Gen 2 VM. All looks good and behaves as expected. On the Gen 1 VM, the PIT is used briefly at boot (takes ~35 interrupts) before the Hyper-V synthetic timer takes over and the PIT is shutdown. As expected, no further interrupts are received from the PIT. On a Gen 2 VM, apic_needs_pit() returns true because X86_FEATURE_ARAT isn't present. The PIT doesn't exist in a Gen 2 VM, but the code paths handle this situation with no problems, just as before the patch series. For the two-patch series on Hyper-V, Tested-by: Michael Kelley <mhkelley@xxxxxxxxxxx>