1// SPDX-License-Identifier: GPL-2.0 2/* 3 * haltpoll.c - haltpoll idle 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/kernel.h> 14#include <linux/cpuidle.h> 15#include <linux/time.h> 16#include <linux/ktime.h> 17#include <linux/hrtimer.h> 18#include <linux/tick.h> 19#include <linux/sched.h> 20#include <linux/module.h> 21#include <linux/kvm_para.h> 22#include <trace/events/power.h> 23 24static unsigned int guest_halt_poll_ns __read_mostly = 200000; 25module_param(guest_halt_poll_ns, uint, 0644); 26 27/* division factor to shrink halt_poll_ns */ 28static unsigned int guest_halt_poll_shrink __read_mostly = 2; 29module_param(guest_halt_poll_shrink, uint, 0644); 30 31/* multiplication factor to grow per-cpu poll_limit_ns */ 32static unsigned int guest_halt_poll_grow __read_mostly = 2; 33module_param(guest_halt_poll_grow, uint, 0644); 34 35/* value in us to start growing per-cpu halt_poll_ns */ 36static unsigned int guest_halt_poll_grow_start __read_mostly = 50000; 37module_param(guest_halt_poll_grow_start, uint, 0644); 38 39/* allow shrinking guest halt poll */ 40static bool guest_halt_poll_allow_shrink __read_mostly = true; 41module_param(guest_halt_poll_allow_shrink, bool, 0644); 42 43/** 44 * haltpoll_select - selects the next idle state to enter 45 * @drv: cpuidle driver containing state data 46 * @dev: the CPU 47 * @stop_tick: indication on whether or not to stop the tick 48 */ 49static int haltpoll_select(struct cpuidle_driver *drv, 50 struct cpuidle_device *dev, 51 bool *stop_tick) 52{ 53 s64 latency_req = cpuidle_governor_latency_req(dev->cpu); 54 55 if (!drv->state_count || latency_req == 0) { 56 *stop_tick = false; 57 return 0; 58 } 59 60 if (dev->poll_limit_ns == 0) 61 return 1; 62 63 /* Last state was poll? */ 64 if (dev->last_state_idx == 0) { 65 /* Halt if no event occurred on poll window */ 66 if (dev->poll_time_limit == true) 67 return 1; 68 69 *stop_tick = false; 70 /* Otherwise, poll again */ 71 return 0; 72 } 73 74 *stop_tick = false; 75 /* Last state was halt: poll */ 76 return 0; 77} 78 79static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns) 80{ 81 unsigned int val; 82 83 /* Grow cpu_halt_poll_us if 84 * cpu_halt_poll_us < block_ns < guest_halt_poll_us 85 */ 86 if (block_ns > dev->poll_limit_ns && block_ns <= guest_halt_poll_ns) { 87 val = dev->poll_limit_ns * guest_halt_poll_grow; 88 89 if (val < guest_halt_poll_grow_start) 90 val = guest_halt_poll_grow_start; 91 if (val > guest_halt_poll_ns) 92 val = guest_halt_poll_ns; 93 94 trace_guest_halt_poll_ns_grow(val, dev->poll_limit_ns); 95 dev->poll_limit_ns = val; 96 } else if (block_ns > guest_halt_poll_ns && 97 guest_halt_poll_allow_shrink) { 98 unsigned int shrink = guest_halt_poll_shrink; 99 100 val = dev->poll_limit_ns; 101 if (shrink == 0) { 102 val = 0; 103 } else { 104 val /= shrink; 105 /* Reset value to 0 if shrunk below grow_start */ 106 if (val < guest_halt_poll_grow_start) 107 val = 0; 108 } 109 110 trace_guest_halt_poll_ns_shrink(val, dev->poll_limit_ns); 111 dev->poll_limit_ns = val; 112 } 113} 114 115/** 116 * haltpoll_reflect - update variables and update poll time 117 * @dev: the CPU 118 * @index: the index of actual entered state 119 */ 120static void haltpoll_reflect(struct cpuidle_device *dev, int index) 121{ 122 dev->last_state_idx = index; 123 124 if (index != 0) 125 adjust_poll_limit(dev, dev->last_residency_ns); 126} 127 128/** 129 * haltpoll_enable_device - scans a CPU's states and does setup 130 * @drv: cpuidle driver 131 * @dev: the CPU 132 */ 133static int haltpoll_enable_device(struct cpuidle_driver *drv, 134 struct cpuidle_device *dev) 135{ 136 dev->poll_limit_ns = 0; 137 138 return 0; 139} 140 141static struct cpuidle_governor haltpoll_governor = { 142 .name = "haltpoll", 143 .rating = 9, 144 .enable = haltpoll_enable_device, 145 .select = haltpoll_select, 146 .reflect = haltpoll_reflect, 147}; 148 149static int __init init_haltpoll(void) 150{ 151 if (kvm_para_available()) 152 return cpuidle_register_governor(&haltpoll_governor); 153 154 return 0; 155} 156 157postcore_initcall(init_haltpoll); 158