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