158 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * haltpoll.c - haltpoll idle governor4 *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. See8 * 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 enter45 * @drv: cpuidle driver containing state data46 * @dev: the CPU47 * @stop_tick: indication on whether or not to stop the tick48 */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 if84 * cpu_halt_poll_us < block_ns < guest_halt_poll_us85 */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 time117 * @dev: the CPU118 * @index: the index of actual entered state119 */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 setup130 * @drv: cpuidle driver131 * @dev: the CPU132 */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