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> 222cffe9f6SMarcelo Tosatti 232cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_ns __read_mostly = 200000; 242cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_ns, uint, 0644); 252cffe9f6SMarcelo Tosatti 262cffe9f6SMarcelo Tosatti /* division factor to shrink halt_poll_ns */ 272cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_shrink __read_mostly = 2; 282cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_shrink, uint, 0644); 292cffe9f6SMarcelo Tosatti 302cffe9f6SMarcelo Tosatti /* multiplication factor to grow per-cpu poll_limit_ns */ 312cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_grow __read_mostly = 2; 322cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_grow, uint, 0644); 332cffe9f6SMarcelo Tosatti 342cffe9f6SMarcelo Tosatti /* value in us to start growing per-cpu halt_poll_ns */ 352cffe9f6SMarcelo Tosatti static unsigned int guest_halt_poll_grow_start __read_mostly = 50000; 362cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_grow_start, uint, 0644); 372cffe9f6SMarcelo Tosatti 382cffe9f6SMarcelo Tosatti /* allow shrinking guest halt poll */ 392cffe9f6SMarcelo Tosatti static bool guest_halt_poll_allow_shrink __read_mostly = true; 402cffe9f6SMarcelo Tosatti module_param(guest_halt_poll_allow_shrink, bool, 0644); 412cffe9f6SMarcelo Tosatti 422cffe9f6SMarcelo Tosatti /** 432cffe9f6SMarcelo Tosatti * haltpoll_select - selects the next idle state to enter 442cffe9f6SMarcelo Tosatti * @drv: cpuidle driver containing state data 452cffe9f6SMarcelo Tosatti * @dev: the CPU 462cffe9f6SMarcelo Tosatti * @stop_tick: indication on whether or not to stop the tick 472cffe9f6SMarcelo Tosatti */ 482cffe9f6SMarcelo Tosatti static int haltpoll_select(struct cpuidle_driver *drv, 492cffe9f6SMarcelo Tosatti struct cpuidle_device *dev, 502cffe9f6SMarcelo Tosatti bool *stop_tick) 512cffe9f6SMarcelo Tosatti { 522cffe9f6SMarcelo Tosatti int latency_req = cpuidle_governor_latency_req(dev->cpu); 532cffe9f6SMarcelo Tosatti 542cffe9f6SMarcelo Tosatti if (!drv->state_count || latency_req == 0) { 552cffe9f6SMarcelo Tosatti *stop_tick = false; 562cffe9f6SMarcelo Tosatti return 0; 572cffe9f6SMarcelo Tosatti } 582cffe9f6SMarcelo Tosatti 592cffe9f6SMarcelo Tosatti if (dev->poll_limit_ns == 0) 602cffe9f6SMarcelo Tosatti return 1; 612cffe9f6SMarcelo Tosatti 622cffe9f6SMarcelo Tosatti /* Last state was poll? */ 632cffe9f6SMarcelo Tosatti if (dev->last_state_idx == 0) { 642cffe9f6SMarcelo Tosatti /* Halt if no event occurred on poll window */ 652cffe9f6SMarcelo Tosatti if (dev->poll_time_limit == true) 662cffe9f6SMarcelo Tosatti return 1; 672cffe9f6SMarcelo Tosatti 682cffe9f6SMarcelo Tosatti *stop_tick = false; 692cffe9f6SMarcelo Tosatti /* Otherwise, poll again */ 702cffe9f6SMarcelo Tosatti return 0; 712cffe9f6SMarcelo Tosatti } 722cffe9f6SMarcelo Tosatti 732cffe9f6SMarcelo Tosatti *stop_tick = false; 742cffe9f6SMarcelo Tosatti /* Last state was halt: poll */ 752cffe9f6SMarcelo Tosatti return 0; 762cffe9f6SMarcelo Tosatti } 772cffe9f6SMarcelo Tosatti 782cffe9f6SMarcelo Tosatti static void adjust_poll_limit(struct cpuidle_device *dev, unsigned int block_us) 792cffe9f6SMarcelo Tosatti { 802cffe9f6SMarcelo Tosatti unsigned int val; 812cffe9f6SMarcelo Tosatti u64 block_ns = block_us*NSEC_PER_USEC; 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 942cffe9f6SMarcelo Tosatti dev->poll_limit_ns = val; 952cffe9f6SMarcelo Tosatti } else if (block_ns > guest_halt_poll_ns && 962cffe9f6SMarcelo Tosatti guest_halt_poll_allow_shrink) { 972cffe9f6SMarcelo Tosatti unsigned int shrink = guest_halt_poll_shrink; 982cffe9f6SMarcelo Tosatti 992cffe9f6SMarcelo Tosatti val = dev->poll_limit_ns; 1002cffe9f6SMarcelo Tosatti if (shrink == 0) 1012cffe9f6SMarcelo Tosatti val = 0; 1022cffe9f6SMarcelo Tosatti else 1032cffe9f6SMarcelo Tosatti val /= shrink; 1042cffe9f6SMarcelo Tosatti dev->poll_limit_ns = val; 1052cffe9f6SMarcelo Tosatti } 1062cffe9f6SMarcelo Tosatti } 1072cffe9f6SMarcelo Tosatti 1082cffe9f6SMarcelo Tosatti /** 1092cffe9f6SMarcelo Tosatti * haltpoll_reflect - update variables and update poll time 1102cffe9f6SMarcelo Tosatti * @dev: the CPU 1112cffe9f6SMarcelo Tosatti * @index: the index of actual entered state 1122cffe9f6SMarcelo Tosatti */ 1132cffe9f6SMarcelo Tosatti static void haltpoll_reflect(struct cpuidle_device *dev, int index) 1142cffe9f6SMarcelo Tosatti { 1152cffe9f6SMarcelo Tosatti dev->last_state_idx = index; 1162cffe9f6SMarcelo Tosatti 1172cffe9f6SMarcelo Tosatti if (index != 0) 1182cffe9f6SMarcelo Tosatti adjust_poll_limit(dev, dev->last_residency); 1192cffe9f6SMarcelo Tosatti } 1202cffe9f6SMarcelo Tosatti 1212cffe9f6SMarcelo Tosatti /** 1222cffe9f6SMarcelo Tosatti * haltpoll_enable_device - scans a CPU's states and does setup 1232cffe9f6SMarcelo Tosatti * @drv: cpuidle driver 1242cffe9f6SMarcelo Tosatti * @dev: the CPU 1252cffe9f6SMarcelo Tosatti */ 1262cffe9f6SMarcelo Tosatti static int haltpoll_enable_device(struct cpuidle_driver *drv, 1272cffe9f6SMarcelo Tosatti struct cpuidle_device *dev) 1282cffe9f6SMarcelo Tosatti { 1292cffe9f6SMarcelo Tosatti dev->poll_limit_ns = 0; 1302cffe9f6SMarcelo Tosatti 1312cffe9f6SMarcelo Tosatti return 0; 1322cffe9f6SMarcelo Tosatti } 1332cffe9f6SMarcelo Tosatti 1342cffe9f6SMarcelo Tosatti static struct cpuidle_governor haltpoll_governor = { 1352cffe9f6SMarcelo Tosatti .name = "haltpoll", 13673214408SJoao Martins .rating = 9, 1372cffe9f6SMarcelo Tosatti .enable = haltpoll_enable_device, 1382cffe9f6SMarcelo Tosatti .select = haltpoll_select, 1392cffe9f6SMarcelo Tosatti .reflect = haltpoll_reflect, 1402cffe9f6SMarcelo Tosatti }; 1412cffe9f6SMarcelo Tosatti 1422cffe9f6SMarcelo Tosatti static int __init init_haltpoll(void) 1432cffe9f6SMarcelo Tosatti { 1442cffe9f6SMarcelo Tosatti if (kvm_para_available()) 1452cffe9f6SMarcelo Tosatti return cpuidle_register_governor(&haltpoll_governor); 1462cffe9f6SMarcelo Tosatti 1472cffe9f6SMarcelo Tosatti return 0; 1482cffe9f6SMarcelo Tosatti } 1492cffe9f6SMarcelo Tosatti 1502cffe9f6SMarcelo Tosatti postcore_initcall(init_haltpoll); 151