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)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 20 Aug 2025 14:23:44 +0000 (22:23 +0800)
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>
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 edfa61d807022207684f996000bc26c4689d4d85..62cd7b35a29c94f214fec3b55f9462eceffad694 100644 (file)
@@ -168,6 +168,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,