Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.

Fixes: fa86ee90eb11 ("add cpuidle-haltpoll driver")
Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Joao Martins and committed by
Rafael J. Wysocki
97d3eb9d b7e7fffd

+73 -21
+2 -2
arch/x86/include/asm/cpuidle_haltpoll.h
··· 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
+6 -12
arch/x86/kernel/kvm.c
··· 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
+63 -5
drivers/cpuidle/cpuidle-haltpoll.c
··· 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> 20 + 21 + static struct cpuidle_device __percpu *haltpoll_cpuidle_devices; 22 + static enum cpuhp_state haltpoll_hp_state; 19 23 20 24 static int default_enter_idle(struct cpuidle_device *dev, 21 25 struct cpuidle_driver *drv, int index) ··· 50 46 .state_count = 2, 51 47 }; 52 48 49 + static int haltpoll_cpu_online(unsigned int cpu) 50 + { 51 + struct cpuidle_device *dev; 52 + 53 + dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); 54 + if (!dev->registered) { 55 + dev->cpu = cpu; 56 + if (cpuidle_register_device(dev)) { 57 + pr_notice("cpuidle_register_device %d failed!\n", cpu); 58 + return -EIO; 59 + } 60 + arch_haltpoll_enable(cpu); 61 + } 62 + 63 + return 0; 64 + } 65 + 66 + static int haltpoll_cpu_offline(unsigned int cpu) 67 + { 68 + struct cpuidle_device *dev; 69 + 70 + dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu); 71 + if (dev->registered) { 72 + arch_haltpoll_disable(cpu); 73 + cpuidle_unregister_device(dev); 74 + } 75 + 76 + return 0; 77 + } 78 + 79 + static void haltpoll_uninit(void) 80 + { 81 + if (haltpoll_hp_state) 82 + cpuhp_remove_state(haltpoll_hp_state); 83 + cpuidle_unregister_driver(&haltpoll_driver); 84 + 85 + free_percpu(haltpoll_cpuidle_devices); 86 + haltpoll_cpuidle_devices = NULL; 87 + } 88 + 53 89 static int __init haltpoll_init(void) 54 90 { 55 91 int ret; ··· 100 56 if (!kvm_para_available()) 101 57 return 0; 102 58 103 - ret = cpuidle_register(&haltpoll_driver, NULL); 104 - if (ret == 0) 105 - arch_haltpoll_enable(); 59 + ret = cpuidle_register_driver(drv); 60 + if (ret < 0) 61 + return ret; 62 + 63 + haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device); 64 + if (haltpoll_cpuidle_devices == NULL) { 65 + cpuidle_unregister_driver(drv); 66 + return -ENOMEM; 67 + } 68 + 69 + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online", 70 + haltpoll_cpu_online, haltpoll_cpu_offline); 71 + if (ret < 0) { 72 + haltpoll_uninit(); 73 + } else { 74 + haltpoll_hp_state = ret; 75 + ret = 0; 76 + } 106 77 107 78 return ret; 108 79 } 109 80 110 81 static void __exit haltpoll_exit(void) 111 82 { 112 - arch_haltpoll_disable(); 113 - cpuidle_unregister(&haltpoll_driver); 83 + haltpoll_uninit(); 114 84 } 115 85 116 86 module_init(haltpoll_init);
+2 -2
include/linux/cpuidle_haltpoll.h
··· 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