Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/cpuidle/governors/ladder.c
26282 views
1
/*
2
* ladder.c - the residency ladder algorithm
3
*
4
* Copyright (C) 2001, 2002 Andy Grover <[email protected]>
5
* Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]>
6
* Copyright (C) 2004, 2005 Dominik Brodowski <[email protected]>
7
*
8
* (C) 2006-2007 Venkatesh Pallipadi <[email protected]>
9
* Shaohua Li <[email protected]>
10
* Adam Belay <[email protected]>
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 4
24
#define DEMOTION_COUNT 1
25
26
struct 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
39
struct ladder_device {
40
struct ladder_device_state states[CPUIDLE_STATE_MAX];
41
};
42
43
static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
44
45
/**
46
* ladder_do_selection - prepares private data for a state change
47
* @dev: the CPU
48
* @ldev: the ladder device
49
* @old_idx: the current state index
50
* @new_idx: the new target state index
51
*/
52
static 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 enter
63
* @drv: cpuidle driver
64
* @dev: the CPU
65
* @dummy: not used
66
*/
67
static 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 governor
130
* @drv: cpuidle driver
131
* @dev: the CPU
132
*/
133
static 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_idx
165
* @dev: the CPU
166
* @index: the index of actual state entered
167
*/
168
static void ladder_reflect(struct cpuidle_device *dev, int index)
169
{
170
if (index > 0)
171
dev->last_state_idx = index;
172
}
173
174
static 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 governor
184
*/
185
static int __init init_ladder(void)
186
{
187
/*
188
* When NO_HZ is disabled, or when booting with nohz=off, the ladder
189
* governor is better so give it a higher rating than the menu
190
* governor.
191
*/
192
if (!tick_nohz_enabled)
193
ladder_governor.rating = 25;
194
195
return cpuidle_register_governor(&ladder_governor);
196
}
197
198
postcore_initcall(init_ladder);
199
200