Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * haltpoll.c - haltpoll idle governor
0004  *
0005  * Copyright 2019 Red Hat, Inc. and/or its affiliates.
0006  *
0007  * This work is licensed under the terms of the GNU GPL, version 2.  See
0008  * the COPYING file in the top-level directory.
0009  *
0010  * Authors: Marcelo Tosatti <mtosatti@redhat.com>
0011  */
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/cpuidle.h>
0015 #include <linux/time.h>
0016 #include <linux/ktime.h>
0017 #include <linux/hrtimer.h>
0018 #include <linux/tick.h>
0019 #include <linux/sched.h>
0020 #include <linux/module.h>
0021 #include <linux/kvm_para.h>
0022 #include <trace/events/power.h>
0023 
0024 static unsigned int guest_halt_poll_ns __read_mostly = 200000;
0025 module_param(guest_halt_poll_ns, uint, 0644);
0026 
0027 /* division factor to shrink halt_poll_ns */
0028 static unsigned int guest_halt_poll_shrink __read_mostly = 2;
0029 module_param(guest_halt_poll_shrink, uint, 0644);
0030 
0031 /* multiplication factor to grow per-cpu poll_limit_ns */
0032 static unsigned int guest_halt_poll_grow __read_mostly = 2;
0033 module_param(guest_halt_poll_grow, uint, 0644);
0034 
0035 /* value in us to start growing per-cpu halt_poll_ns */
0036 static unsigned int guest_halt_poll_grow_start __read_mostly = 50000;
0037 module_param(guest_halt_poll_grow_start, uint, 0644);
0038 
0039 /* allow shrinking guest halt poll */
0040 static bool guest_halt_poll_allow_shrink __read_mostly = true;
0041 module_param(guest_halt_poll_allow_shrink, bool, 0644);
0042 
0043 /**
0044  * haltpoll_select - selects the next idle state to enter
0045  * @drv: cpuidle driver containing state data
0046  * @dev: the CPU
0047  * @stop_tick: indication on whether or not to stop the tick
0048  */
0049 static int haltpoll_select(struct cpuidle_driver *drv,
0050                struct cpuidle_device *dev,
0051                bool *stop_tick)
0052 {
0053     s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
0054 
0055     if (!drv->state_count || latency_req == 0) {
0056         *stop_tick = false;
0057         return 0;
0058     }
0059 
0060     if (dev->poll_limit_ns == 0)
0061         return 1;
0062 
0063     /* Last state was poll? */
0064     if (dev->last_state_idx == 0) {
0065         /* Halt if no event occurred on poll window */
0066         if (dev->poll_time_limit == true)
0067             return 1;
0068 
0069         *stop_tick = false;
0070         /* Otherwise, poll again */
0071         return 0;
0072     }
0073 
0074     *stop_tick = false;
0075     /* Last state was halt: poll */
0076     return 0;
0077 }
0078 
0079 static void adjust_poll_limit(struct cpuidle_device *dev, u64 block_ns)
0080 {
0081     unsigned int val;
0082 
0083     /* Grow cpu_halt_poll_us if
0084      * cpu_halt_poll_us < block_ns < guest_halt_poll_us
0085      */
0086     if (block_ns > dev->poll_limit_ns && block_ns <= guest_halt_poll_ns) {
0087         val = dev->poll_limit_ns * guest_halt_poll_grow;
0088 
0089         if (val < guest_halt_poll_grow_start)
0090             val = guest_halt_poll_grow_start;
0091         if (val > guest_halt_poll_ns)
0092             val = guest_halt_poll_ns;
0093 
0094         trace_guest_halt_poll_ns_grow(val, dev->poll_limit_ns);
0095         dev->poll_limit_ns = val;
0096     } else if (block_ns > guest_halt_poll_ns &&
0097            guest_halt_poll_allow_shrink) {
0098         unsigned int shrink = guest_halt_poll_shrink;
0099 
0100         val = dev->poll_limit_ns;
0101         if (shrink == 0)
0102             val = 0;
0103         else
0104             val /= shrink;
0105         trace_guest_halt_poll_ns_shrink(val, dev->poll_limit_ns);
0106         dev->poll_limit_ns = val;
0107     }
0108 }
0109 
0110 /**
0111  * haltpoll_reflect - update variables and update poll time
0112  * @dev: the CPU
0113  * @index: the index of actual entered state
0114  */
0115 static void haltpoll_reflect(struct cpuidle_device *dev, int index)
0116 {
0117     dev->last_state_idx = index;
0118 
0119     if (index != 0)
0120         adjust_poll_limit(dev, dev->last_residency_ns);
0121 }
0122 
0123 /**
0124  * haltpoll_enable_device - scans a CPU's states and does setup
0125  * @drv: cpuidle driver
0126  * @dev: the CPU
0127  */
0128 static int haltpoll_enable_device(struct cpuidle_driver *drv,
0129                   struct cpuidle_device *dev)
0130 {
0131     dev->poll_limit_ns = 0;
0132 
0133     return 0;
0134 }
0135 
0136 static struct cpuidle_governor haltpoll_governor = {
0137     .name =         "haltpoll",
0138     .rating =       9,
0139     .enable =       haltpoll_enable_device,
0140     .select =       haltpoll_select,
0141     .reflect =      haltpoll_reflect,
0142 };
0143 
0144 static int __init init_haltpoll(void)
0145 {
0146     if (kvm_para_available())
0147         return cpuidle_register_governor(&haltpoll_governor);
0148 
0149     return 0;
0150 }
0151 
0152 postcore_initcall(init_haltpoll);