199 lines · c
1/*2 * ladder.c - the residency ladder algorithm3 *4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>6 * Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de>7 *8 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>9 * Shaohua Li <shaohua.li@intel.com>10 * Adam Belay <abelay@novell.com>11 *12 * This code is licenced under the GPL.13 */14 15#include <linux/kernel.h>16#include <linux/cpuidle.h>17#include <linux/jiffies.h>18#include <linux/tick.h>19 20#include <asm/io.h>21#include <linux/uaccess.h>22 23#define PROMOTION_COUNT 424#define DEMOTION_COUNT 125 26struct ladder_device_state {27 struct {28 u32 promotion_count;29 u32 demotion_count;30 u64 promotion_time_ns;31 u64 demotion_time_ns;32 } threshold;33 struct {34 int promotion_count;35 int demotion_count;36 } stats;37};38 39struct ladder_device {40 struct ladder_device_state states[CPUIDLE_STATE_MAX];41};42 43static DEFINE_PER_CPU(struct ladder_device, ladder_devices);44 45/**46 * ladder_do_selection - prepares private data for a state change47 * @dev: the CPU48 * @ldev: the ladder device49 * @old_idx: the current state index50 * @new_idx: the new target state index51 */52static inline void ladder_do_selection(struct cpuidle_device *dev,53 struct ladder_device *ldev,54 int old_idx, int new_idx)55{56 ldev->states[old_idx].stats.promotion_count = 0;57 ldev->states[old_idx].stats.demotion_count = 0;58 dev->last_state_idx = new_idx;59}60 61/**62 * ladder_select_state - selects the next state to enter63 * @drv: cpuidle driver64 * @dev: the CPU65 * @dummy: not used66 */67static int ladder_select_state(struct cpuidle_driver *drv,68 struct cpuidle_device *dev, bool *dummy)69{70 struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);71 struct ladder_device_state *last_state;72 int last_idx = dev->last_state_idx;73 int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;74 s64 latency_req = cpuidle_governor_latency_req(dev->cpu);75 s64 last_residency;76 77 /* Special case when user has set very strict latency requirement */78 if (unlikely(latency_req == 0)) {79 ladder_do_selection(dev, ldev, last_idx, 0);80 return 0;81 }82 83 last_state = &ldev->states[last_idx];84 85 last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns;86 87 /* consider promotion */88 if (last_idx < drv->state_count - 1 &&89 !dev->states_usage[last_idx + 1].disable &&90 last_residency > last_state->threshold.promotion_time_ns &&91 drv->states[last_idx + 1].exit_latency_ns <= latency_req) {92 last_state->stats.promotion_count++;93 last_state->stats.demotion_count = 0;94 if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {95 ladder_do_selection(dev, ldev, last_idx, last_idx + 1);96 return last_idx + 1;97 }98 }99 100 /* consider demotion */101 if (last_idx > first_idx &&102 (dev->states_usage[last_idx].disable ||103 drv->states[last_idx].exit_latency_ns > latency_req)) {104 int i;105 106 for (i = last_idx - 1; i > first_idx; i--) {107 if (drv->states[i].exit_latency_ns <= latency_req)108 break;109 }110 ladder_do_selection(dev, ldev, last_idx, i);111 return i;112 }113 114 if (last_idx > first_idx &&115 last_residency < last_state->threshold.demotion_time_ns) {116 last_state->stats.demotion_count++;117 last_state->stats.promotion_count = 0;118 if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {119 ladder_do_selection(dev, ldev, last_idx, last_idx - 1);120 return last_idx - 1;121 }122 }123 124 /* otherwise remain at the current state */125 return last_idx;126}127 128/**129 * ladder_enable_device - setup for the governor130 * @drv: cpuidle driver131 * @dev: the CPU132 */133static int ladder_enable_device(struct cpuidle_driver *drv,134 struct cpuidle_device *dev)135{136 int i;137 int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;138 struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);139 struct ladder_device_state *lstate;140 struct cpuidle_state *state;141 142 dev->last_state_idx = first_idx;143 144 for (i = first_idx; i < drv->state_count; i++) {145 state = &drv->states[i];146 lstate = &ldev->states[i];147 148 lstate->stats.promotion_count = 0;149 lstate->stats.demotion_count = 0;150 151 lstate->threshold.promotion_count = PROMOTION_COUNT;152 lstate->threshold.demotion_count = DEMOTION_COUNT;153 154 if (i < drv->state_count - 1)155 lstate->threshold.promotion_time_ns = state->exit_latency_ns;156 if (i > first_idx)157 lstate->threshold.demotion_time_ns = state->exit_latency_ns;158 }159 160 return 0;161}162 163/**164 * ladder_reflect - update the correct last_state_idx165 * @dev: the CPU166 * @index: the index of actual state entered167 */168static void ladder_reflect(struct cpuidle_device *dev, int index)169{170 if (index > 0)171 dev->last_state_idx = index;172}173 174static struct cpuidle_governor ladder_governor = {175 .name = "ladder",176 .rating = 10,177 .enable = ladder_enable_device,178 .select = ladder_select_state,179 .reflect = ladder_reflect,180};181 182/**183 * init_ladder - initializes the governor184 */185static int __init init_ladder(void)186{187 /*188 * When NO_HZ is disabled, or when booting with nohz=off, the ladder189 * governor is better so give it a higher rating than the menu190 * governor.191 */192 if (!tick_nohz_enabled)193 ladder_governor.rating = 25;194 195 return cpuidle_register_governor(&ladder_governor);196}197 198postcore_initcall(init_ladder);199