]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
LoongArch: Add cpuhotplug hooks to fix high cpu usage of vCPU threads
authorXianglai Li <lixianglai@loongson.cn>
Wed, 20 Aug 2025 14:23:44 +0000 (22:23 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 9 Sep 2025 17:02:15 +0000 (19:02 +0200)
[ Upstream commit 8ef7f3132e4005a103b382e71abea7ad01fbeb86 ]

When the CPU is offline, the timer of LoongArch is not correctly closed.
This is harmless for real machines, but resulting in an excessively high
cpu usage rate of the offline vCPU thread in the virtual machines.

To correctly close the timer, we have made the following modifications:

Register the cpu hotplug event (CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING)
for LoongArch. This event's hooks will be called to close the timer when
the CPU is offline.

Clear the timer interrupt when the timer is turned off. Since before the
timer is turned off, there may be a timer interrupt that has already been
in the pending state due to the interruption of the disabled, which also
affects the halt state of the offline vCPU.

Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Sasha Levin <sashal@kernel.org>
arch/loongarch/kernel/time.c
include/linux/cpuhotplug.h

index 367906b10f810a11952cb8b3d7604fab31a872fb..f3092f2de8b501bb48661f3aa7c2e00d8c82a604 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
  */
 #include <linux/clockchips.h>
+#include <linux/cpuhotplug.h>
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/init.h>
@@ -102,6 +103,23 @@ static int constant_timer_next_event(unsigned long delta, struct clock_event_dev
        return 0;
 }
 
+static int arch_timer_starting(unsigned int cpu)
+{
+       set_csr_ecfg(ECFGF_TIMER);
+
+       return 0;
+}
+
+static int arch_timer_dying(unsigned int cpu)
+{
+       constant_set_state_shutdown(this_cpu_ptr(&constant_clockevent_device));
+
+       /* Clear Timer Interrupt */
+       write_csr_tintclear(CSR_TINTCLR_TI);
+
+       return 0;
+}
+
 static unsigned long get_loops_per_jiffy(void)
 {
        unsigned long lpj = (unsigned long)const_clock_freq;
@@ -172,6 +190,10 @@ int constant_clockevent_init(void)
        lpj_fine = get_loops_per_jiffy();
        pr_info("Constant clock event device register\n");
 
+       cpuhp_setup_state(CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING,
+                         "clockevents/loongarch/timer:starting",
+                         arch_timer_starting, arch_timer_dying);
+
        return 0;
 }
 
index df366ee15456bba56c581d482f98a5b9d26211d5..e62064cb9e08a47e0f8e7b1a2ef00ca8f75ada87 100644 (file)
@@ -169,6 +169,7 @@ enum cpuhp_state {
        CPUHP_AP_QCOM_TIMER_STARTING,
        CPUHP_AP_TEGRA_TIMER_STARTING,
        CPUHP_AP_ARMADA_TIMER_STARTING,
+       CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING,
        CPUHP_AP_MIPS_GIC_TIMER_STARTING,
        CPUHP_AP_ARC_TIMER_STARTING,
        CPUHP_AP_REALTEK_TIMER_STARTING,