Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * cpuidle driver for haltpoll governor.
0004  *
0005  * Copyright 2019 Red Hat, Inc. and/or its affiliates.
0006  *
0007  * This work is licensed under the terms of the GNU GPL, version 2.  See
0008  * the COPYING file in the top-level directory.
0009  *
0010  * Authors: Marcelo Tosatti <mtosatti@redhat.com>
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         { /* entry 0 is for polling */ },
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     /* Do not load haltpoll if idle= is passed */
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>");