12cffe9f6SMarcelo Tosatti // SPDX-License-Identifier: GPL-2.0
22cffe9f6SMarcelo Tosatti /*
32cffe9f6SMarcelo Tosatti  * haltpoll.c - haltpoll idle governor
42cffe9f6SMarcelo Tosatti  *
52cffe9f6SMarcelo Tosatti  * Copyright 2019 Red Hat, Inc. and/or its affiliates.
62cffe9f6SMarcelo Tosatti  *
72cffe9f6SMarcelo Tosatti  * This work is licensed under the terms of the GNU GPL, version 2.  See
82cffe9f6SMarcelo Tosatti  * the COPYING file in the top-level directory.
92cffe9f6SMarcelo Tosatti  *
102cffe9f6SMarcelo Tosatti  * Authors: Marcelo Tosatti <mtosatti@redhat.com>
112cffe9f6SMarcelo Tosatti  */
122cffe9f6SMarcelo Tosatti 
132cffe9f6SMarcelo Tosatti #include <linux/kernel.h>
142cffe9f6SMarcelo Tosatti #include <linux/cpuidle.h>
152cffe9f6SMarcelo Tosatti #include <linux/time.h>
162cffe9f6SMarcelo Tosatti #include <linux/ktime.h>
172cffe9f6SMarcelo Tosatti #include <linux/hrtimer.h>
182cffe9f6SMarcelo Tosatti #include <linux/tick.h>
192cffe9f6SMarcelo Tosatti #include <linux/sched.h>
202cffe9f6SMarcelo Tosatti #include <linux/module.h>
212cffe9f6SMarcelo Tosatti #include <linux/kvm_para.h>
22*0da11bf0SEiichi Tsukata #include <trace/events/power.h>
232cffe9f6SMarcelo Tosatti 
242cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_ns __read_mostly = 200000;
252cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_ns, uint, 0644);
262cffe9f6SMarcelo Tosatti 
272cffe9f6SMarcelo Tosatti /* division factor to shrink halt_poll_ns */
282cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_shrink __read_mostly = 2;
292cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_shrink, uint, 0644);
302cffe9f6SMarcelo Tosatti 
312cffe9f6SMarcelo Tosatti /* multiplication factor to grow per-cpu poll_limit_ns */
322cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_grow __read_mostly = 2;
332cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_grow, uint, 0644);
342cffe9f6SMarcelo Tosatti 
352cffe9f6SMarcelo Tosatti /* value in us to start growing per-cpu halt_poll_ns */
362cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_grow_start __read_mostly = 50000;
372cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_grow_start, uint, 0644);
382cffe9f6SMarcelo Tosatti 
392cffe9f6SMarcelo Tosatti /* allow shrinking guest halt poll */
402cffe9f6SMarcelo Tosatti static bool guest_halt_poll_allow_shrink __read_mostly = true;
412cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_allow_shrink, bool, 0644);
422cffe9f6SMarcelo Tosatti 
432cffe9f6SMarcelo Tosatti /**
442cffe9f6SMarcelo Tosatti  * haltpoll_select - selects the next idle state to enter
452cffe9f6SMarcelo Tosatti  * @drv: cpuidle driver containing state data
462cffe9f6SMarcelo Tosatti  * @dev: the CPU
472cffe9f6SMarcelo Tosatti  * @stop_tick: indication on whether or not to stop the tick
482cffe9f6SMarcelo Tosatti  */
haltpoll_select(struct cpuidle_driver * drv,struct cpuidle_device * dev,bool * stop_tick)492cffe9f6SMarcelo Tosatti static int haltpoll_select(struct cpuidle_driver *drv,
502cffe9f6SMarcelo Tosatti 			   struct cpuidle_device *dev,
512cffe9f6SMarcelo Tosatti 			   bool *stop_tick)
522cffe9f6SMarcelo Tosatti {
53c1d51f68SRafael J. Wysocki 	s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
542cffe9f6SMarcelo Tosatti 
552cffe9f6SMarcelo Tosatti 	if (!drv->state_count || latency_req == 0) {
562cffe9f6SMarcelo Tosatti 		*stop_tick = false;
572cffe9f6SMarcelo Tosatti 		return 0;
582cffe9f6SMarcelo Tosatti 	}
592cffe9f6SMarcelo Tosatti 
602cffe9f6SMarcelo Tosatti 	if (dev->poll_limit_ns == 0)
612cffe9f6SMarcelo Tosatti 		return 1;
622cffe9f6SMarcelo Tosatti 
632cffe9f6SMarcelo Tosatti 	/* Last state was poll? */
642cffe9f6SMarcelo Tosatti 	if (dev->last_state_idx == 0) {
652cffe9f6SMarcelo Tosatti 		/* Halt if no event occurred on poll window */
662cffe9f6SMarcelo Tosatti 		if (dev->poll_time_limit == true)
672cffe9f6SMarcelo Tosatti 			return 1;
682cffe9f6SMarcelo Tosatti 
692cffe9f6SMarcelo Tosatti 		*stop_tick = false;
702cffe9f6SMarcelo Tosatti 		/* Otherwise, poll again */
712cffe9f6SMarcelo Tosatti 		return 0;
722cffe9f6SMarcelo Tosatti 	}
732cffe9f6SMarcelo Tosatti 
742cffe9f6SMarcelo Tosatti 	*stop_tick = false;
752cffe9f6SMarcelo Tosatti 	/* Last state was halt: poll */
762cffe9f6SMarcelo Tosatti 	return 0;
772cffe9f6SMarcelo Tosatti }
782cffe9f6SMarcelo Tosatti 
adjust_poll_limit(struct cpuidle_device * dev,u64 block_ns)79c1d51f68SRafael J. Wysocki static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns)
802cffe9f6SMarcelo Tosatti {
812cffe9f6SMarcelo Tosatti 	unsigned int val;
822cffe9f6SMarcelo Tosatti 
832cffe9f6SMarcelo Tosatti 	/* Grow cpu_halt_poll_us if
842cffe9f6SMarcelo Tosatti 	 * cpu_halt_poll_us < block_ns < guest_halt_poll_us
852cffe9f6SMarcelo Tosatti 	 */
862cffe9f6SMarcelo Tosatti 	if (block_ns > dev->poll_limit_ns && block_ns <= guest_halt_poll_ns) {
872cffe9f6SMarcelo Tosatti 		val = dev->poll_limit_ns * guest_halt_poll_grow;
882cffe9f6SMarcelo Tosatti 
892cffe9f6SMarcelo Tosatti 		if (val < guest_halt_poll_grow_start)
902cffe9f6SMarcelo Tosatti 			val = guest_halt_poll_grow_start;
912cffe9f6SMarcelo Tosatti 		if (val > guest_halt_poll_ns)
922cffe9f6SMarcelo Tosatti 			val = guest_halt_poll_ns;
932cffe9f6SMarcelo Tosatti 
94*0da11bf0SEiichi Tsukata 		trace_guest_halt_poll_ns_grow(val, dev->poll_limit_ns);
952cffe9f6SMarcelo Tosatti 		dev->poll_limit_ns = val;
962cffe9f6SMarcelo Tosatti 	} else if (block_ns > guest_halt_poll_ns &&
972cffe9f6SMarcelo Tosatti 		   guest_halt_poll_allow_shrink) {
982cffe9f6SMarcelo Tosatti 		unsigned int shrink = guest_halt_poll_shrink;
992cffe9f6SMarcelo Tosatti 
1002cffe9f6SMarcelo Tosatti 		val = dev->poll_limit_ns;
1012cffe9f6SMarcelo Tosatti 		if (shrink == 0)
1022cffe9f6SMarcelo Tosatti 			val = 0;
1032cffe9f6SMarcelo Tosatti 		else
1042cffe9f6SMarcelo Tosatti 			val /= shrink;
105*0da11bf0SEiichi Tsukata 		trace_guest_halt_poll_ns_shrink(val, dev->poll_limit_ns);
1062cffe9f6SMarcelo Tosatti 		dev->poll_limit_ns = val;
1072cffe9f6SMarcelo Tosatti 	}
1082cffe9f6SMarcelo Tosatti }
1092cffe9f6SMarcelo Tosatti 
1102cffe9f6SMarcelo Tosatti /**
1112cffe9f6SMarcelo Tosatti  * haltpoll_reflect - update variables and update poll time
1122cffe9f6SMarcelo Tosatti  * @dev: the CPU
1132cffe9f6SMarcelo Tosatti  * @index: the index of actual entered state
1142cffe9f6SMarcelo Tosatti  */
haltpoll_reflect(struct cpuidle_device * dev,int index)1152cffe9f6SMarcelo Tosatti static void haltpoll_reflect(struct cpuidle_device *dev, int index)
1162cffe9f6SMarcelo Tosatti {
1172cffe9f6SMarcelo Tosatti 	dev->last_state_idx = index;
1182cffe9f6SMarcelo Tosatti 
1192cffe9f6SMarcelo Tosatti 	if (index != 0)
120c1d51f68SRafael J. Wysocki 		adjust_poll_limit(dev, dev->last_residency_ns);
1212cffe9f6SMarcelo Tosatti }
1222cffe9f6SMarcelo Tosatti 
1232cffe9f6SMarcelo Tosatti /**
1242cffe9f6SMarcelo Tosatti  * haltpoll_enable_device - scans a CPU's states and does setup
1252cffe9f6SMarcelo Tosatti  * @drv: cpuidle driver
1262cffe9f6SMarcelo Tosatti  * @dev: the CPU
1272cffe9f6SMarcelo Tosatti  */
haltpoll_enable_device(struct cpuidle_driver * drv,struct cpuidle_device * dev)1282cffe9f6SMarcelo Tosatti static int haltpoll_enable_device(struct cpuidle_driver *drv,
1292cffe9f6SMarcelo Tosatti 				  struct cpuidle_device *dev)
1302cffe9f6SMarcelo Tosatti {
1312cffe9f6SMarcelo Tosatti 	dev->poll_limit_ns = 0;
1322cffe9f6SMarcelo Tosatti 
1332cffe9f6SMarcelo Tosatti 	return 0;
1342cffe9f6SMarcelo Tosatti }
1352cffe9f6SMarcelo Tosatti 
1362cffe9f6SMarcelo Tosatti static struct cpuidle_governor haltpoll_governor = {
1372cffe9f6SMarcelo Tosatti 	.name =			"haltpoll",
13873214408SJoao Martins 	.rating =		9,
1392cffe9f6SMarcelo Tosatti 	.enable =		haltpoll_enable_device,
1402cffe9f6SMarcelo Tosatti 	.select =		haltpoll_select,
1412cffe9f6SMarcelo Tosatti 	.reflect =		haltpoll_reflect,
1422cffe9f6SMarcelo Tosatti };
1432cffe9f6SMarcelo Tosatti 
init_haltpoll(void)1442cffe9f6SMarcelo Tosatti static int __init init_haltpoll(void)
1452cffe9f6SMarcelo Tosatti {
1462cffe9f6SMarcelo Tosatti 	if (kvm_para_available())
1472cffe9f6SMarcelo Tosatti 		return cpuidle_register_governor(&haltpoll_governor);
1482cffe9f6SMarcelo Tosatti 
1492cffe9f6SMarcelo Tosatti 	return 0;
1502cffe9f6SMarcelo Tosatti }
1512cffe9f6SMarcelo Tosatti 
1522cffe9f6SMarcelo Tosatti postcore_initcall(init_haltpoll);
153