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 | #ifndef _ARCH_HALTPOLL_H | 2 | #ifndef _ARCH_HALTPOLL_H |
| 3 | #define _ARCH_HALTPOLL_H | 3 | #define _ARCH_HALTPOLL_H |
| 4 | 4 | ||
| 5 | void arch_haltpoll_enable(void); | 5 | void arch_haltpoll_enable(unsigned int cpu); |
| 6 | void arch_haltpoll_disable(void); | 6 | void arch_haltpoll_disable(unsigned int cpu); |
| 7 | 7 | ||
| 8 | #endif | 8 | #endif |
| @@ -888,32 +888,26 @@ static void kvm_enable_host_haltpoll(void *i) · 887 unmodified lines | |||
| 888 | wrmsrl(MSR_KVM_POLL_CONTROL, 1); | 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 | if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) { | 893 | if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) { |
| 894 | printk(KERN_ERR "kvm: host does not support poll control\n"); | 894 | pr_err_once("kvm: host does not support poll control\n"); |
| 895 | printk(KERN_ERR "kvm: host upgrade recommended\n"); | 895 | pr_err_once("kvm: host upgrade recommended\n"); |
| 896 | return; | 896 | return; |
| 897 | } | 897 | } |
| 898 | 898 | ||
| 899 | preempt_disable(); | ||
| 900 | /* Enable guest halt poll disables host halt poll */ | 899 | /* Enable guest halt poll disables host halt poll */ |
| 901 | kvm_disable_host_haltpoll(NULL); | 900 | smp_call_function_single(cpu, kvm_disable_host_haltpoll, NULL, 1); |
| 902 | smp_call_function(kvm_disable_host_haltpoll, NULL, 1); | ||
| 903 | preempt_enable(); | ||
| 904 | } | 901 | } |
| 905 | EXPORT_SYMBOL_GPL(arch_haltpoll_enable); | 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 | if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) | 906 | if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) |
| 910 | return; | 907 | return; |
| 911 | 908 | ||
| 912 | preempt_disable(); | ||
| 913 | /* Enable guest halt poll disables host halt poll */ | 909 | /* Enable guest halt poll disables host halt poll */ |
| 914 | kvm_enable_host_haltpoll(NULL); | 910 | smp_call_function_single(cpu, kvm_enable_host_haltpoll, NULL, 1); |
| 915 | smp_call_function(kvm_enable_host_haltpoll, NULL, 1); | ||
| 916 | preempt_enable(); | ||
| 917 | } | 911 | } |
| 918 | EXPORT_SYMBOL_GPL(arch_haltpoll_disable); | 912 | EXPORT_SYMBOL_GPL(arch_haltpoll_disable); |
| 919 | #endif | 913 | #endif |
| @@ -11,12 +11,16 @@ · 10 unmodified lines | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
| 14 | #include <linux/cpu.h> | ||
| 14 | #include <linux/cpuidle.h> | 15 | #include <linux/cpuidle.h> |
| 15 | #include <linux/module.h> | 16 | #include <linux/module.h> |
| 16 | #include <linux/sched/idle.h> | 17 | #include <linux/sched/idle.h> |
| 17 | #include <linux/kvm_para.h> | 18 | #include <linux/kvm_para.h> |
| 18 | #include <linux/cpuidle_haltpoll.h> | 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 | static int default_enter_idle(struct cpuidle_device *dev, | 24 | static int default_enter_idle(struct cpuidle_device *dev, |
| 21 | struct cpuidle_driver *drv, int index) | 25 | struct cpuidle_driver *drv, int index) |
| 22 | { | 26 | { |
| @@ -46,6 +50,46 @@ static struct cpuidle_driver haltpoll_driver = { · 23 unmodified lines | |||
| 46 | .state_count = 2, | 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 | static int __init haltpoll_init(void) | 93 | static int __init haltpoll_init(void) |
| 50 | { | 94 | { |
| 51 | int ret; | 95 | int ret; |
| @@ -56,17 +100,31 @@ static int __init haltpoll_init(void) · 4 unmodified lines | |||
| 56 | if (!kvm_para_available()) | 100 | if (!kvm_para_available()) |
| 57 | return 0; | 101 | return 0; |
| 58 | 102 | ||
| 59 | ret = cpuidle_register(&haltpoll_driver, NULL); | 103 | ret = cpuidle_register_driver(drv); |
| 60 | if (ret == 0) | 104 | if (ret < 0) |
| 61 | arch_haltpoll_enable(); | 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 | return ret; | 122 | return ret; |
| 64 | } | 123 | } |
| 65 | 124 | ||
| 66 | static void __exit haltpoll_exit(void) | 125 | static void __exit haltpoll_exit(void) |
| 67 | { | 126 | { |
| 68 | arch_haltpoll_disable(); | 127 | haltpoll_uninit(); |
| 69 | cpuidle_unregister(&haltpoll_driver); | ||
| 70 | } | 128 | } |
| 71 | 129 | ||
| 72 | module_init(haltpoll_init); | 130 | module_init(haltpoll_init); |
| @@ -5,11 +5,11 @@ · 4 unmodified lines | |||
| 5 | #ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL | 5 | #ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL |
| 6 | #include <asm/cpuidle_haltpoll.h> | 6 | #include <asm/cpuidle_haltpoll.h> |
| 7 | #else | 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 | #endif | 15 | #endif |