Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/cpuidle/governors/ladder.c
15111 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/pm_qos_params.h>
18
#include <linux/moduleparam.h>
19
#include <linux/jiffies.h>
20
21
#include <asm/io.h>
22
#include <asm/uaccess.h>
23
24
#define PROMOTION_COUNT 4
25
#define DEMOTION_COUNT 1
26
27
struct ladder_device_state {
28
struct {
29
u32 promotion_count;
30
u32 demotion_count;
31
u32 promotion_time;
32
u32 demotion_time;
33
} threshold;
34
struct {
35
int promotion_count;
36
int demotion_count;
37
} stats;
38
};
39
40
struct ladder_device {
41
struct ladder_device_state states[CPUIDLE_STATE_MAX];
42
int last_state_idx;
43
};
44
45
static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
46
47
/**
48
* ladder_do_selection - prepares private data for a state change
49
* @ldev: the ladder device
50
* @old_idx: the current state index
51
* @new_idx: the new target state index
52
*/
53
static inline void ladder_do_selection(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
ldev->last_state_idx = new_idx;
59
}
60
61
/**
62
* ladder_select_state - selects the next state to enter
63
* @dev: the CPU
64
*/
65
static int ladder_select_state(struct cpuidle_device *dev)
66
{
67
struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
68
struct ladder_device_state *last_state;
69
int last_residency, last_idx = ldev->last_state_idx;
70
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
71
72
/* Special case when user has set very strict latency requirement */
73
if (unlikely(latency_req == 0)) {
74
ladder_do_selection(ldev, last_idx, 0);
75
return 0;
76
}
77
78
last_state = &ldev->states[last_idx];
79
80
if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID)
81
last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency;
82
else
83
last_residency = last_state->threshold.promotion_time + 1;
84
85
/* consider promotion */
86
if (last_idx < dev->state_count - 1 &&
87
last_residency > last_state->threshold.promotion_time &&
88
dev->states[last_idx + 1].exit_latency <= latency_req) {
89
last_state->stats.promotion_count++;
90
last_state->stats.demotion_count = 0;
91
if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
92
ladder_do_selection(ldev, last_idx, last_idx + 1);
93
return last_idx + 1;
94
}
95
}
96
97
/* consider demotion */
98
if (last_idx > CPUIDLE_DRIVER_STATE_START &&
99
dev->states[last_idx].exit_latency > latency_req) {
100
int i;
101
102
for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
103
if (dev->states[i].exit_latency <= latency_req)
104
break;
105
}
106
ladder_do_selection(ldev, last_idx, i);
107
return i;
108
}
109
110
if (last_idx > CPUIDLE_DRIVER_STATE_START &&
111
last_residency < last_state->threshold.demotion_time) {
112
last_state->stats.demotion_count++;
113
last_state->stats.promotion_count = 0;
114
if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
115
ladder_do_selection(ldev, last_idx, last_idx - 1);
116
return last_idx - 1;
117
}
118
}
119
120
/* otherwise remain at the current state */
121
return last_idx;
122
}
123
124
/**
125
* ladder_enable_device - setup for the governor
126
* @dev: the CPU
127
*/
128
static int ladder_enable_device(struct cpuidle_device *dev)
129
{
130
int i;
131
struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
132
struct ladder_device_state *lstate;
133
struct cpuidle_state *state;
134
135
ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
136
137
for (i = 0; i < dev->state_count; i++) {
138
state = &dev->states[i];
139
lstate = &ldev->states[i];
140
141
lstate->stats.promotion_count = 0;
142
lstate->stats.demotion_count = 0;
143
144
lstate->threshold.promotion_count = PROMOTION_COUNT;
145
lstate->threshold.demotion_count = DEMOTION_COUNT;
146
147
if (i < dev->state_count - 1)
148
lstate->threshold.promotion_time = state->exit_latency;
149
if (i > 0)
150
lstate->threshold.demotion_time = state->exit_latency;
151
}
152
153
return 0;
154
}
155
156
static struct cpuidle_governor ladder_governor = {
157
.name = "ladder",
158
.rating = 10,
159
.enable = ladder_enable_device,
160
.select = ladder_select_state,
161
.owner = THIS_MODULE,
162
};
163
164
/**
165
* init_ladder - initializes the governor
166
*/
167
static int __init init_ladder(void)
168
{
169
return cpuidle_register_governor(&ladder_governor);
170
}
171
172
/**
173
* exit_ladder - exits the governor
174
*/
175
static void __exit exit_ladder(void)
176
{
177
cpuidle_unregister_governor(&ladder_governor);
178
}
179
180
MODULE_LICENSE("GPL");
181
module_init(init_ladder);
182
module_exit(exit_ladder);
183
184