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.
Unified · Split
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | #ifndef _ARCH_HALTPOLL_H |
| 3 | 3 | #define _ARCH_HALTPOLL_H |
| 4 | 4 | |
| 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); | |
| 7 | 7 | |
| 8 | 8 | #endif |
| @@ -888,32 +888,26 @@ static void kvm_enable_host_haltpoll(void *i) · 887 unmodified lines | ||
| 888 | 888 | wrmsrl(MSR_KVM_POLL_CONTROL, 1); |
| 889 | 889 | } |
| 890 | 890 | |
| 891 | −void arch_haltpoll_enable(void) | |
| 891 | +void arch_haltpoll_enable(unsigned int cpu) | |
| 892 | 892 | { |
| 893 | 893 | 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"); | |
| 896 | 896 | return; |
| 897 | 897 | } |
| 898 | 898 | |
| 899 | − preempt_disable(); | |
| 900 | 899 | /* 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); | |
| 904 | 901 | } |
| 905 | 902 | EXPORT_SYMBOL_GPL(arch_haltpoll_enable); |
| 906 | 903 | |
| 907 | −void arch_haltpoll_disable(void) | |
| 904 | +void arch_haltpoll_disable(unsigned int cpu) | |
| 908 | 905 | { |
| 909 | 906 | if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) |
| 910 | 907 | return; |
| 911 | 908 | |
| 912 | − preempt_disable(); | |
| 913 | 909 | /* 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); | |
| 917 | 911 | } |
| 918 | 912 | EXPORT_SYMBOL_GPL(arch_haltpoll_disable); |
| 919 | 913 | #endif |
| @@ -11,12 +11,16 @@ · 10 unmodified lines | ||
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | #include <linux/init.h> |
| 14 | +#include <linux/cpu.h> | |
| 14 | 15 | #include <linux/cpuidle.h> |
| 15 | 16 | #include <linux/module.h> |
| 16 | 17 | #include <linux/sched/idle.h> |
| 17 | 18 | #include <linux/kvm_para.h> |
| 18 | 19 | #include <linux/cpuidle_haltpoll.h> |
| 19 | 20 | |
| 21 | +static struct cpuidle_device __percpu *haltpoll_cpuidle_devices; | |
| 22 | +static enum cpuhp_state haltpoll_hp_state; | |
| 23 | + | |
| 20 | 24 | static int default_enter_idle(struct cpuidle_device *dev, |
| 21 | 25 | struct cpuidle_driver *drv, int index) |
| 22 | 26 | { |
| @@ -46,6 +50,46 @@ static struct cpuidle_driver haltpoll_driver = { · 23 unmodified lines | ||
| 46 | 50 | .state_count = 2, |
| 47 | 51 | }; |
| 48 | 52 | |
| 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 | + | |
| 49 | 93 | static int __init haltpoll_init(void) |
| 50 | 94 | { |
| 51 | 95 | int ret; |
| @@ -56,17 +100,31 @@ static int __init haltpoll_init(void) · 4 unmodified lines | ||
| 56 | 100 | if (!kvm_para_available()) |
| 57 | 101 | return 0; |
| 58 | 102 | |
| 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 | + } | |
| 62 | 121 | |
| 63 | 122 | return ret; |
| 64 | 123 | } |
| 65 | 124 | |
| 66 | 125 | static void __exit haltpoll_exit(void) |
| 67 | 126 | { |
| 68 | − arch_haltpoll_disable(); | |
| 69 | − cpuidle_unregister(&haltpoll_driver); | |
| 127 | + haltpoll_uninit(); | |
| 70 | 128 | } |
| 71 | 129 | |
| 72 | 130 | module_init(haltpoll_init); |
| @@ -5,11 +5,11 @@ · 4 unmodified lines | ||
| 5 | 5 | #ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL |
| 6 | 6 | #include <asm/cpuidle_haltpoll.h> |
| 7 | 7 | #else |
| 8 | −static inline void arch_haltpoll_enable(void) | |
| 8 | +static inline void arch_haltpoll_enable(unsigned int cpu) | |
| 9 | 9 | { |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | −static inline void arch_haltpoll_disable(void) | |
| 12 | +static inline void arch_haltpoll_disable(unsigned int cpu) | |
| 13 | 13 | { |
| 14 | 14 | } |
| 15 | 15 | #endif |