2019-07-03 20:51:28 -03:00
// SPDX-License-Identifier: GPL-2.0
/*
* haltpoll . c - haltpoll idle 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 < mtosatti @ redhat . com >
*/
# include <linux/kernel.h>
# include <linux/cpuidle.h>
# include <linux/time.h>
# include <linux/ktime.h>
# include <linux/hrtimer.h>
# include <linux/tick.h>
# include <linux/sched.h>
# include <linux/module.h>
# include <linux/kvm_para.h>
static unsigned int guest_halt_poll_ns __read_mostly = 200000 ;
module_param ( guest_halt_poll_ns , uint , 0644 ) ;
/* division factor to shrink halt_poll_ns */
static unsigned int guest_halt_poll_shrink __read_mostly = 2 ;
module_param ( guest_halt_poll_shrink , uint , 0644 ) ;
/* multiplication factor to grow per-cpu poll_limit_ns */
static unsigned int guest_halt_poll_grow __read_mostly = 2 ;
module_param ( guest_halt_poll_grow , uint , 0644 ) ;
/* value in us to start growing per-cpu halt_poll_ns */
static unsigned int guest_halt_poll_grow_start __read_mostly = 50000 ;
module_param ( guest_halt_poll_grow_start , uint , 0644 ) ;
/* allow shrinking guest halt poll */
static bool guest_halt_poll_allow_shrink __read_mostly = true ;
module_param ( guest_halt_poll_allow_shrink , bool , 0644 ) ;
/**
* haltpoll_select - selects the next idle state to enter
* @ drv : cpuidle driver containing state data
* @ dev : the CPU
* @ stop_tick : indication on whether or not to stop the tick
*/
static int haltpoll_select ( struct cpuidle_driver * drv ,
struct cpuidle_device * dev ,
bool * stop_tick )
{
2019-11-07 15:25:12 +01:00
s64 latency_req = cpuidle_governor_latency_req ( dev - > cpu ) ;
2019-07-03 20:51:28 -03:00
if ( ! drv - > state_count | | latency_req = = 0 ) {
* stop_tick = false ;
return 0 ;
}
if ( dev - > poll_limit_ns = = 0 )
return 1 ;
/* Last state was poll? */
if ( dev - > last_state_idx = = 0 ) {
/* Halt if no event occurred on poll window */
if ( dev - > poll_time_limit = = true )
return 1 ;
* stop_tick = false ;
/* Otherwise, poll again */
return 0 ;
}
* stop_tick = false ;
/* Last state was halt: poll */
return 0 ;
}
2019-11-07 15:25:12 +01:00
static void adjust_poll_limit ( struct cpuidle_device * dev , u64 block_ns )
2019-07-03 20:51:28 -03:00
{
unsigned int val ;
/* Grow cpu_halt_poll_us if
* cpu_halt_poll_us < block_ns < guest_halt_poll_us
*/
if ( block_ns > dev - > poll_limit_ns & & block_ns < = guest_halt_poll_ns ) {
val = dev - > poll_limit_ns * guest_halt_poll_grow ;
if ( val < guest_halt_poll_grow_start )
val = guest_halt_poll_grow_start ;
if ( val > guest_halt_poll_ns )
val = guest_halt_poll_ns ;
dev - > poll_limit_ns = val ;
} else if ( block_ns > guest_halt_poll_ns & &
guest_halt_poll_allow_shrink ) {
unsigned int shrink = guest_halt_poll_shrink ;
val = dev - > poll_limit_ns ;
if ( shrink = = 0 )
val = 0 ;
else
val / = shrink ;
dev - > poll_limit_ns = val ;
}
}
/**
* haltpoll_reflect - update variables and update poll time
* @ dev : the CPU
* @ index : the index of actual entered state
*/
static void haltpoll_reflect ( struct cpuidle_device * dev , int index )
{
dev - > last_state_idx = index ;
if ( index ! = 0 )
2019-11-07 15:25:12 +01:00
adjust_poll_limit ( dev , dev - > last_residency_ns ) ;
2019-07-03 20:51:28 -03:00
}
/**
* haltpoll_enable_device - scans a CPU ' s states and does setup
* @ drv : cpuidle driver
* @ dev : the CPU
*/
static int haltpoll_enable_device ( struct cpuidle_driver * drv ,
struct cpuidle_device * dev )
{
dev - > poll_limit_ns = 0 ;
return 0 ;
}
static struct cpuidle_governor haltpoll_governor = {
. name = " haltpoll " ,
2019-09-08 00:45:22 +01:00
. rating = 9 ,
2019-07-03 20:51:28 -03:00
. enable = haltpoll_enable_device ,
. select = haltpoll_select ,
. reflect = haltpoll_reflect ,
} ;
static int __init init_haltpoll ( void )
{
if ( kvm_para_available ( ) )
return cpuidle_register_governor ( & haltpoll_governor ) ;
return 0 ;
}
postcore_initcall ( init_haltpoll ) ;