123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * cpuidle driver for haltpoll governor.
- *
- * Copyright 2019 Red Hat, Inc. and/or its affiliates.
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- * Authors: Marcelo Tosatti <[email protected]>
- */
- #include <linux/init.h>
- #include <linux/cpu.h>
- #include <linux/cpuidle.h>
- #include <linux/module.h>
- #include <linux/sched/idle.h>
- #include <linux/kvm_para.h>
- #include <linux/cpuidle_haltpoll.h>
- static bool force __read_mostly;
- module_param(force, bool, 0444);
- MODULE_PARM_DESC(force, "Load unconditionally");
- static struct cpuidle_device __percpu *haltpoll_cpuidle_devices;
- static enum cpuhp_state haltpoll_hp_state;
- static int default_enter_idle(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index)
- {
- if (current_clr_polling_and_test()) {
- local_irq_enable();
- return index;
- }
- default_idle();
- return index;
- }
- static struct cpuidle_driver haltpoll_driver = {
- .name = "haltpoll",
- .governor = "haltpoll",
- .states = {
- { /* entry 0 is for polling */ },
- {
- .enter = default_enter_idle,
- .exit_latency = 1,
- .target_residency = 1,
- .power_usage = -1,
- .name = "haltpoll idle",
- .desc = "default architecture idle",
- },
- },
- .safe_state_index = 0,
- .state_count = 2,
- };
- static int haltpoll_cpu_online(unsigned int cpu)
- {
- struct cpuidle_device *dev;
- dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
- if (!dev->registered) {
- dev->cpu = cpu;
- if (cpuidle_register_device(dev)) {
- pr_notice("cpuidle_register_device %d failed!\n", cpu);
- return -EIO;
- }
- arch_haltpoll_enable(cpu);
- }
- return 0;
- }
- static int haltpoll_cpu_offline(unsigned int cpu)
- {
- struct cpuidle_device *dev;
- dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
- if (dev->registered) {
- arch_haltpoll_disable(cpu);
- cpuidle_unregister_device(dev);
- }
- return 0;
- }
- static void haltpoll_uninit(void)
- {
- if (haltpoll_hp_state)
- cpuhp_remove_state(haltpoll_hp_state);
- cpuidle_unregister_driver(&haltpoll_driver);
- free_percpu(haltpoll_cpuidle_devices);
- haltpoll_cpuidle_devices = NULL;
- }
- static bool haltpoll_want(void)
- {
- return kvm_para_has_hint(KVM_HINTS_REALTIME) || force;
- }
- static int __init haltpoll_init(void)
- {
- int ret;
- struct cpuidle_driver *drv = &haltpoll_driver;
- /* Do not load haltpoll if idle= is passed */
- if (boot_option_idle_override != IDLE_NO_OVERRIDE)
- return -ENODEV;
- if (!kvm_para_available() || !haltpoll_want())
- return -ENODEV;
- cpuidle_poll_state_init(drv);
- ret = cpuidle_register_driver(drv);
- if (ret < 0)
- return ret;
- haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device);
- if (haltpoll_cpuidle_devices == NULL) {
- cpuidle_unregister_driver(drv);
- return -ENOMEM;
- }
- ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online",
- haltpoll_cpu_online, haltpoll_cpu_offline);
- if (ret < 0) {
- haltpoll_uninit();
- } else {
- haltpoll_hp_state = ret;
- ret = 0;
- }
- return ret;
- }
- static void __exit haltpoll_exit(void)
- {
- haltpoll_uninit();
- }
- module_init(haltpoll_init);
- module_exit(haltpoll_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Marcelo Tosatti <[email protected]>");
|