cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpuidle-haltpoll.c (3210B)


      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	if (!kvm_para_available() || !haltpoll_want())
    112		return -ENODEV;
    113
    114	cpuidle_poll_state_init(drv);
    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>");