linux/drivers/cpuidle/cpuidle-haltpoll.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   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.  See
   8 * 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 int default_enter_idle(struct cpuidle_device *dev,
  29                              struct cpuidle_driver *drv, int index)
  30{
  31        if (current_clr_polling_and_test()) {
  32                local_irq_enable();
  33                return index;
  34        }
  35        default_idle();
  36        return index;
  37}
  38
  39static struct cpuidle_driver haltpoll_driver = {
  40        .name = "haltpoll",
  41        .governor = "haltpoll",
  42        .states = {
  43                { /* entry 0 is for polling */ },
  44                {
  45                        .enter                  = default_enter_idle,
  46                        .exit_latency           = 1,
  47                        .target_residency       = 1,
  48                        .power_usage            = -1,
  49                        .name                   = "haltpoll idle",
  50                        .desc                   = "default architecture idle",
  51                },
  52        },
  53        .safe_state_index = 0,
  54        .state_count = 2,
  55};
  56
  57static int haltpoll_cpu_online(unsigned int cpu)
  58{
  59        struct cpuidle_device *dev;
  60
  61        dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
  62        if (!dev->registered) {
  63                dev->cpu = cpu;
  64                if (cpuidle_register_device(dev)) {
  65                        pr_notice("cpuidle_register_device %d failed!\n", cpu);
  66                        return -EIO;
  67                }
  68                arch_haltpoll_enable(cpu);
  69        }
  70
  71        return 0;
  72}
  73
  74static int haltpoll_cpu_offline(unsigned int cpu)
  75{
  76        struct cpuidle_device *dev;
  77
  78        dev = per_cpu_ptr(haltpoll_cpuidle_devices, cpu);
  79        if (dev->registered) {
  80                arch_haltpoll_disable(cpu);
  81                cpuidle_unregister_device(dev);
  82        }
  83
  84        return 0;
  85}
  86
  87static void haltpoll_uninit(void)
  88{
  89        if (haltpoll_hp_state)
  90                cpuhp_remove_state(haltpoll_hp_state);
  91        cpuidle_unregister_driver(&haltpoll_driver);
  92
  93        free_percpu(haltpoll_cpuidle_devices);
  94        haltpoll_cpuidle_devices = NULL;
  95}
  96
  97static bool haltpoll_want(void)
  98{
  99        return kvm_para_has_hint(KVM_HINTS_REALTIME) || force;
 100}
 101
 102static int __init haltpoll_init(void)
 103{
 104        int ret;
 105        struct cpuidle_driver *drv = &haltpoll_driver;
 106
 107        /* Do not load haltpoll if idle= is passed */
 108        if (boot_option_idle_override != IDLE_NO_OVERRIDE)
 109                return -ENODEV;
 110
 111        cpuidle_poll_state_init(drv);
 112
 113        if (!kvm_para_available() || !haltpoll_want())
 114                return -ENODEV;
 115
 116        ret = cpuidle_register_driver(drv);
 117        if (ret < 0)
 118                return ret;
 119
 120        haltpoll_cpuidle_devices = alloc_percpu(struct cpuidle_device);
 121        if (haltpoll_cpuidle_devices == NULL) {
 122                cpuidle_unregister_driver(drv);
 123                return -ENOMEM;
 124        }
 125
 126        ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpuidle/haltpoll:online",
 127                                haltpoll_cpu_online, haltpoll_cpu_offline);
 128        if (ret < 0) {
 129                haltpoll_uninit();
 130        } else {
 131                haltpoll_hp_state = ret;
 132                ret = 0;
 133        }
 134
 135        return ret;
 136}
 137
 138static void __exit haltpoll_exit(void)
 139{
 140        haltpoll_uninit();
 141}
 142
 143module_init(haltpoll_init);
 144module_exit(haltpoll_exit);
 145MODULE_LICENSE("GPL");
 146MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
 147