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