0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/init.h>
0014 #include <linux/cpu.h>
0015 #include <linux/cpuidle.h>
0016 #include <linux/module.h>
0017 #include <linux/sched/idle.h>
0018 #include <linux/kvm_para.h>
0019 #include <linux/cpuidle_haltpoll.h>
0020
0021 static bool force __read_mostly;
0022 module_param(force, bool, 0444);
0023 MODULE_PARM_DESC(force, "Load unconditionally");
0024
0025 static struct cpuidle_device __percpu *haltpoll_cpuidle_devices;
0026 static enum cpuhp_state haltpoll_hp_state;
0027
0028 static int default_enter_idle(struct cpuidle_device *dev,
0029 struct cpuidle_driver *drv, int index)
0030 {
0031 if (current_clr_polling_and_test()) {
0032 local_irq_enable();
0033 return index;
0034 }
0035 default_idle();
0036 return index;
0037 }
0038
0039 static struct cpuidle_driver haltpoll_driver = {
0040 .name = "haltpoll",
0041 .governor = "haltpoll",
0042 .states = {
0043 { },
0044 {
0045 .enter = default_enter_idle,
0046 .exit_latency = 1,
0047 .target_residency = 1,
0048 .power_usage = -1,
0049 .name = "haltpoll idle",
0050 .desc = "default architecture idle",
0051 },
0052 },
0053 .safe_state_index = 0,
0054 .state_count = 2,
0055 };
0056
0057 static int haltpoll_cpu_online(unsigned int cpu)
0058 {
0059 struct cpuidle_device *dev;
0060
0061 dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
0062 if (!dev->registered) {
0063 dev->cpu = cpu;
0064 if (cpuidle_register_device(dev)) {
0065 pr_notice("cpuidle_register_device %d failed!\n", cpu);
0066 return -EIO;
0067 }
0068 arch_haltpoll_enable(cpu);
0069 }
0070
0071 return 0;
0072 }
0073
0074 static int haltpoll_cpu_offline(unsigned int cpu)
0075 {
0076 struct cpuidle_device *dev;
0077
0078 dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
0079 if (dev->registered) {
0080 arch_haltpoll_disable(cpu);
0081 cpuidle_unregister_device(dev);
0082 }
0083
0084 return 0;
0085 }
0086
0087 static void haltpoll_uninit(void)
0088 {
0089 if (haltpoll_hp_state)
0090 cpuhp_remove_state(haltpoll_hp_state);
0091 cpuidle_unregister_driver(&haltpoll_driver);
0092
0093 free_percpu(haltpoll_cpuidle_devices);
0094 haltpoll_cpuidle_devices = NULL;
0095 }
0096
0097 static bool haltpoll_want(void)
0098 {
0099 return kvm_para_has_hint(KVM_HINTS_REALTIME) || force;
0100 }
0101
0102 static int __init haltpoll_init(void)
0103 {
0104 int ret;
0105 struct cpuidle_driver *drv = &haltpoll_driver;
0106
0107
0108 if (boot_option_idle_override != IDLE_NO_OVERRIDE)
0109 return -ENODEV;
0110
0111 if (!kvm_para_available() || !haltpoll_want())
0112 return -ENODEV;
0113
0114 cpuidle_poll_state_init(drv);
0115
0116 ret = cpuidle_register_driver(drv);
0117 if (ret < 0)
0118 return ret;
0119
0120 haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device);
0121 if (haltpoll_cpuidle_devices == NULL) {
0122 cpuidle_unregister_driver(drv);
0123 return -ENOMEM;
0124 }
0125
0126 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online",
0127 haltpoll_cpu_online, haltpoll_cpu_offline);
0128 if (ret < 0) {
0129 haltpoll_uninit();
0130 } else {
0131 haltpoll_hp_state = ret;
0132 ret = 0;
0133 }
0134
0135 return ret;
0136 }
0137
0138 static void __exit haltpoll_exit(void)
0139 {
0140 haltpoll_uninit();
0141 }
0142
0143 module_init(haltpoll_init);
0144 module_exit(haltpoll_exit);
0145 MODULE_LICENSE("GPL");
0146 MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");