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 24 static unsigned int guest_halt_poll_ns __read_mostly = 200000; 25 module_param(guest_halt_poll_ns, uint, 0644); 26 27 /* division factor to shrink halt_poll_ns */ 28 static unsigned int guest_halt_poll_shrink __read_mostly = 2; 29 module_param(guest_halt_poll_shrink, uint, 0644); 30 31 /* multiplication factor to grow per-cpu poll_limit_ns */ 32 static unsigned int guest_halt_poll_grow __read_mostly = 2; 33 module_param(guest_halt_poll_grow, uint, 0644); 34 35 /* value in us to start growing per-cpu halt_poll_ns */ 36 static unsigned int guest_halt_poll_grow_start __read_mostly = 50000; 37 module_param(guest_halt_poll_grow_start, uint, 0644); 38 39 /* allow shrinking guest halt poll */ 40 static bool guest_halt_poll_allow_shrink __read_mostly = true; 41 module_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 */ 49 static 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 79 static 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 */ 120 static 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 */ 133 static 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 141 static 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 149 static int __init init_haltpoll(void) 150 { 151 if (kvm_para_available()) 152 return cpuidle_register_governor(&haltpoll_governor); 153 154 return 0; 155 } 156 157 postcore_initcall(init_haltpoll); 158