Blame view
drivers/cpuidle/governors/ladder.c
5.04 KB
4f86d3a8e
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * ladder.c - the residency ladder algorithm * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de> * * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> * Shaohua Li <shaohua.li@intel.com> * Adam Belay <abelay@novell.com> * * This code is licenced under the GPL. */ #include <linux/kernel.h> #include <linux/cpuidle.h> |
e8db0be12
|
17 |
#include <linux/pm_qos.h> |
70e522a02
|
18 |
#include <linux/module.h> |
4f86d3a8e
|
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 |
#include <linux/jiffies.h> #include <asm/io.h> #include <asm/uaccess.h> #define PROMOTION_COUNT 4 #define DEMOTION_COUNT 1 struct ladder_device_state { struct { u32 promotion_count; u32 demotion_count; u32 promotion_time; u32 demotion_time; } threshold; struct { int promotion_count; int demotion_count; } stats; }; struct ladder_device { struct ladder_device_state states[CPUIDLE_STATE_MAX]; int last_state_idx; }; static DEFINE_PER_CPU(struct ladder_device, ladder_devices); /** * ladder_do_selection - prepares private data for a state change * @ldev: the ladder device * @old_idx: the current state index * @new_idx: the new target state index */ static inline void ladder_do_selection(struct ladder_device *ldev, int old_idx, int new_idx) { ldev->states[old_idx].stats.promotion_count = 0; ldev->states[old_idx].stats.demotion_count = 0; ldev->last_state_idx = new_idx; } /** * ladder_select_state - selects the next state to enter |
46bcfad7a
|
63 |
* @drv: cpuidle driver |
4f86d3a8e
|
64 65 |
* @dev: the CPU */ |
46bcfad7a
|
66 67 |
static int ladder_select_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) |
4f86d3a8e
|
68 |
{ |
229b6863b
|
69 |
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); |
4f86d3a8e
|
70 71 |
struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; |
ed77134bf
|
72 |
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); |
4f86d3a8e
|
73 |
|
a2bd92023
|
74 75 76 77 78 |
/* Special case when user has set very strict latency requirement */ if (unlikely(latency_req == 0)) { ladder_do_selection(ldev, last_idx, 0); return 0; } |
4f86d3a8e
|
79 |
last_state = &ldev->states[last_idx]; |
b73026b9c
|
80 |
last_residency = cpuidle_get_last_residency(dev) - drv->states[last_idx].exit_latency; |
4f86d3a8e
|
81 82 |
/* consider promotion */ |
46bcfad7a
|
83 |
if (last_idx < drv->state_count - 1 && |
66804c13f
|
84 |
!drv->states[last_idx + 1].disabled && |
62d6ae880
|
85 |
!dev->states_usage[last_idx + 1].disable && |
4f86d3a8e
|
86 |
last_residency > last_state->threshold.promotion_time && |
46bcfad7a
|
87 |
drv->states[last_idx + 1].exit_latency <= latency_req) { |
4f86d3a8e
|
88 89 90 91 92 93 94 95 96 |
last_state->stats.promotion_count++; last_state->stats.demotion_count = 0; if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { ladder_do_selection(ldev, last_idx, last_idx + 1); return last_idx + 1; } } /* consider demotion */ |
a2bd92023
|
97 |
if (last_idx > CPUIDLE_DRIVER_STATE_START && |
66804c13f
|
98 99 |
(drv->states[last_idx].disabled || dev->states_usage[last_idx].disable || |
62d6ae880
|
100 |
drv->states[last_idx].exit_latency > latency_req)) { |
06d9e908b
|
101 102 103 |
int i; for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) { |
46bcfad7a
|
104 |
if (drv->states[i].exit_latency <= latency_req) |
06d9e908b
|
105 106 107 108 109 110 111 |
break; } ladder_do_selection(ldev, last_idx, i); return i; } if (last_idx > CPUIDLE_DRIVER_STATE_START && |
4f86d3a8e
|
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
last_residency < last_state->threshold.demotion_time) { last_state->stats.demotion_count++; last_state->stats.promotion_count = 0; if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) { ladder_do_selection(ldev, last_idx, last_idx - 1); return last_idx - 1; } } /* otherwise remain at the current state */ return last_idx; } /** * ladder_enable_device - setup for the governor |
46bcfad7a
|
127 |
* @drv: cpuidle driver |
4f86d3a8e
|
128 129 |
* @dev: the CPU */ |
46bcfad7a
|
130 131 |
static int ladder_enable_device(struct cpuidle_driver *drv, struct cpuidle_device *dev) |
4f86d3a8e
|
132 133 134 135 136 |
{ int i; struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu); struct ladder_device_state *lstate; struct cpuidle_state *state; |
a2bd92023
|
137 |
ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START; |
4f86d3a8e
|
138 |
|
146924461
|
139 |
for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { |
46bcfad7a
|
140 |
state = &drv->states[i]; |
4f86d3a8e
|
141 142 143 144 145 146 147 |
lstate = &ldev->states[i]; lstate->stats.promotion_count = 0; lstate->stats.demotion_count = 0; lstate->threshold.promotion_count = PROMOTION_COUNT; lstate->threshold.demotion_count = DEMOTION_COUNT; |
46bcfad7a
|
148 |
if (i < drv->state_count - 1) |
4f86d3a8e
|
149 |
lstate->threshold.promotion_time = state->exit_latency; |
146924461
|
150 |
if (i > CPUIDLE_DRIVER_STATE_START) |
4f86d3a8e
|
151 152 153 154 155 |
lstate->threshold.demotion_time = state->exit_latency; } return 0; } |
e978aa7d7
|
156 157 158 159 160 161 162 |
/** * ladder_reflect - update the correct last_state_idx * @dev: the CPU * @index: the index of actual state entered */ static void ladder_reflect(struct cpuidle_device *dev, int index) { |
229b6863b
|
163 |
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); |
e978aa7d7
|
164 165 166 |
if (index > 0) ldev->last_state_idx = index; } |
4f86d3a8e
|
167 168 169 170 171 |
static struct cpuidle_governor ladder_governor = { .name = "ladder", .rating = 10, .enable = ladder_enable_device, .select = ladder_select_state, |
e978aa7d7
|
172 |
.reflect = ladder_reflect, |
4f86d3a8e
|
173 174 175 176 177 178 179 180 181 182 |
.owner = THIS_MODULE, }; /** * init_ladder - initializes the governor */ static int __init init_ladder(void) { return cpuidle_register_governor(&ladder_governor); } |
137b944e1
|
183 |
postcore_initcall(init_ladder); |