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