History

cpuidle-haltpoll: vcpu hotplug support

When cpus != maxcpus cpuidle-haltpoll will fail to register all vcpus past the online ones and thus fail to register the idle driver. This is because cpuidle_add_sysfs() will return with -ENODEV as a consequence from get_cpu_device() return no device for a non-existing CPU. Instead switch to cpuidle_register_driver() and manually register each of the present cpus through cpuhp_setup_state() callbacks and future ones that get onlined or offlined. This mimmics similar logic that intel_idle does.

Joao Martins committed 7 years ago

Commit 97d3eb91 parent b7e7fff4 files · +73 −21Browse files

Unified · Split

4 files
  1. arch/x86/include/asm/cpuidle_haltpoll.h +2 −2
  2. arch/x86/kernel/kvm.c +6 −12
  3. drivers/cpuidle/cpuidle-haltpoll.c +63 −5
  4. include/linux/cpuidle_haltpoll.h +2 −2
arch/x86/include/asm/cpuidle_haltpoll.h+2−2
@@ -2,7 +2,7 @@ · 1 unmodified line
22 #ifndef _ARCH_HALTPOLL_H
33 #define _ARCH_HALTPOLL_H
44
5−void arch_haltpoll_enable(void);
6−void arch_haltpoll_disable(void);
5+void arch_haltpoll_enable(unsigned int cpu);
6+void arch_haltpoll_disable(unsigned int cpu);
77
88 #endif
arch/x86/kernel/kvm.c+6−12
@@ -888,32 +888,26 @@ static void kvm_enable_host_haltpoll(void *i) · 887 unmodified lines
888888 wrmsrl(MSR_KVM_POLL_CONTROL, 1);
889889 }
890890
891−void arch_haltpoll_enable(void)
891+void arch_haltpoll_enable(unsigned int cpu)
892892 {
893893 if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) {
894− printk(KERN_ERR "kvm: host does not support poll control\n");
895− printk(KERN_ERR "kvm: host upgrade recommended\n");
894+ pr_err_once("kvm: host does not support poll control\n");
895+ pr_err_once("kvm: host upgrade recommended\n");
896896 return;
897897 }
898898
899− preempt_disable();
900899 /* Enable guest halt poll disables host halt poll */
901− kvm_disable_host_haltpoll(NULL);
902− smp_call_function(kvm_disable_host_haltpoll, NULL, 1);
903− preempt_enable();
900+ smp_call_function_single(cpu, kvm_disable_host_haltpoll, NULL, 1);
904901 }
905902 EXPORT_SYMBOL_GPL(arch_haltpoll_enable);
906903
907−void arch_haltpoll_disable(void)
904+void arch_haltpoll_disable(unsigned int cpu)
908905 {
909906 if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL))
910907 return;
911908
912− preempt_disable();
913909 /* Enable guest halt poll disables host halt poll */
914− kvm_enable_host_haltpoll(NULL);
915− smp_call_function(kvm_enable_host_haltpoll, NULL, 1);
916− preempt_enable();
910+ smp_call_function_single(cpu, kvm_enable_host_haltpoll, NULL, 1);
917911 }
918912 EXPORT_SYMBOL_GPL(arch_haltpoll_disable);
919913 #endif
drivers/cpuidle/cpuidle-haltpoll.c+63−5
@@ -11,12 +11,16 @@ · 10 unmodified lines
1111 */
1212
1313 #include <linux/init.h>
14+#include <linux/cpu.h>
1415 #include <linux/cpuidle.h>
1516 #include <linux/module.h>
1617 #include <linux/sched/idle.h>
1718 #include <linux/kvm_para.h>
1819 #include <linux/cpuidle_haltpoll.h>
1920
21+static struct cpuidle_device __percpu *haltpoll_cpuidle_devices;
22+static enum cpuhp_state haltpoll_hp_state;
23+
2024 static int default_enter_idle(struct cpuidle_device *dev,
2125 struct cpuidle_driver *drv, int index)
2226 {
@@ -46,6 +50,46 @@ static struct cpuidle_driver haltpoll_driver = { · 23 unmodified lines
4650 .state_count = 2,
4751 };
4852
53+static int haltpoll_cpu_online(unsigned int cpu)
54+{
55+ struct cpuidle_device *dev;
56+
57+ dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
58+ if (!dev->registered) {
59+ dev->cpu = cpu;
60+ if (cpuidle_register_device(dev)) {
61+ pr_notice("cpuidle_register_device %d failed!\n", cpu);
62+ return -EIO;
63+ }
64+ arch_haltpoll_enable(cpu);
65+ }
66+
67+ return 0;
68+}
69+
70+static int haltpoll_cpu_offline(unsigned int cpu)
71+{
72+ struct cpuidle_device *dev;
73+
74+ dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
75+ if (dev->registered) {
76+ arch_haltpoll_disable(cpu);
77+ cpuidle_unregister_device(dev);
78+ }
79+
80+ return 0;
81+}
82+
83+static void haltpoll_uninit(void)
84+{
85+ if (haltpoll_hp_state)
86+ cpuhp_remove_state(haltpoll_hp_state);
87+ cpuidle_unregister_driver(&haltpoll_driver);
88+
89+ free_percpu(haltpoll_cpuidle_devices);
90+ haltpoll_cpuidle_devices = NULL;
91+}
92+
4993 static int __init haltpoll_init(void)
5094 {
5195 int ret;
@@ -56,17 +100,31 @@ static int __init haltpoll_init(void) · 4 unmodified lines
56100 if (!kvm_para_available())
57101 return 0;
58102
59− ret = cpuidle_register(&haltpoll_driver, NULL);
60− if (ret == 0)
61− arch_haltpoll_enable();
103+ ret = cpuidle_register_driver(drv);
104+ if (ret < 0)
105+ return ret;
106+
107+ haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device);
108+ if (haltpoll_cpuidle_devices == NULL) {
109+ cpuidle_unregister_driver(drv);
110+ return -ENOMEM;
111+ }
112+
113+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online",
114+ haltpoll_cpu_online, haltpoll_cpu_offline);
115+ if (ret < 0) {
116+ haltpoll_uninit();
117+ } else {
118+ haltpoll_hp_state = ret;
119+ ret = 0;
120+ }
62121
63122 return ret;
64123 }
65124
66125 static void __exit haltpoll_exit(void)
67126 {
68− arch_haltpoll_disable();
69− cpuidle_unregister(&haltpoll_driver);
127+ haltpoll_uninit();
70128 }
71129
72130 module_init(haltpoll_init);
include/linux/cpuidle_haltpoll.h+2−2
@@ -5,11 +5,11 @@ · 4 unmodified lines
55 #ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL
66 #include <asm/cpuidle_haltpoll.h>
77 #else
8−static inline void arch_haltpoll_enable(void)
8+static inline void arch_haltpoll_enable(unsigned int cpu)
99 {
1010 }
1111
12−static inline void arch_haltpoll_disable(void)
12+static inline void arch_haltpoll_disable(unsigned int cpu)
1313 {
1414 }
1515 #endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t