147 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * cpuidle driver for haltpoll governor.4 *5 * Copyright 2019 Red Hat, Inc. and/or its affiliates.6 *7 * This work is licensed under the terms of the GNU GPL, version 2. See8 * the COPYING file in the top-level directory.9 *10 * Authors: Marcelo Tosatti <mtosatti@redhat.com>11 */12 13#include <linux/init.h>14#include <linux/cpu.h>15#include <linux/cpuidle.h>16#include <linux/module.h>17#include <linux/sched/idle.h>18#include <linux/kvm_para.h>19#include <linux/cpuidle_haltpoll.h>20 21static bool force __read_mostly;22module_param(force, bool, 0444);23MODULE_PARM_DESC(force, "Load unconditionally");24 25static struct cpuidle_device __percpu *haltpoll_cpuidle_devices;26static enum cpuhp_state haltpoll_hp_state;27 28static __cpuidle int default_enter_idle(struct cpuidle_device *dev,29 struct cpuidle_driver *drv, int index)30{31 if (current_clr_polling_and_test())32 return index;33 34 arch_cpu_idle();35 return index;36}37 38static struct cpuidle_driver haltpoll_driver = {39 .name = "haltpoll",40 .governor = "haltpoll",41 .states = {42 { /* entry 0 is for polling */ },43 {44 .enter = default_enter_idle,45 .exit_latency = 1,46 .target_residency = 1,47 .power_usage = -1,48 .name = "haltpoll idle",49 .desc = "default architecture idle",50 },51 },52 .safe_state_index = 0,53 .state_count = 2,54};55 56static int haltpoll_cpu_online(unsigned int cpu)57{58 struct cpuidle_device *dev;59 60 dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);61 if (!dev->registered) {62 dev->cpu = cpu;63 if (cpuidle_register_device(dev)) {64 pr_notice("cpuidle_register_device %d failed!\n", cpu);65 return -EIO;66 }67 arch_haltpoll_enable(cpu);68 }69 70 return 0;71}72 73static int haltpoll_cpu_offline(unsigned int cpu)74{75 struct cpuidle_device *dev;76 77 dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);78 if (dev->registered) {79 arch_haltpoll_disable(cpu);80 cpuidle_unregister_device(dev);81 }82 83 return 0;84}85 86static void haltpoll_uninit(void)87{88 if (haltpoll_hp_state)89 cpuhp_remove_state(haltpoll_hp_state);90 cpuidle_unregister_driver(&haltpoll_driver);91 92 free_percpu(haltpoll_cpuidle_devices);93 haltpoll_cpuidle_devices = NULL;94}95 96static bool haltpoll_want(void)97{98 return kvm_para_has_hint(KVM_HINTS_REALTIME) || force;99}100 101static int __init haltpoll_init(void)102{103 int ret;104 struct cpuidle_driver *drv = &haltpoll_driver;105 106 /* Do not load haltpoll if idle= is passed */107 if (boot_option_idle_override != IDLE_NO_OVERRIDE)108 return -ENODEV;109 110 if (!kvm_para_available() || !haltpoll_want())111 return -ENODEV;112 113 cpuidle_poll_state_init(drv);114 115 ret = cpuidle_register_driver(drv);116 if (ret < 0)117 return ret;118 119 haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device);120 if (haltpoll_cpuidle_devices == NULL) {121 cpuidle_unregister_driver(drv);122 return -ENOMEM;123 }124 125 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online",126 haltpoll_cpu_online, haltpoll_cpu_offline);127 if (ret < 0) {128 haltpoll_uninit();129 } else {130 haltpoll_hp_state = ret;131 ret = 0;132 }133 134 return ret;135}136 137static void __exit haltpoll_exit(void)138{139 haltpoll_uninit();140}141 142module_init(haltpoll_init);143module_exit(haltpoll_exit);144MODULE_DESCRIPTION("cpuidle driver for haltpoll governor");145MODULE_LICENSE("GPL");146MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");147