Branch data Line data Source code
1 : : /*
2 : : * ladder.c - the residency ladder algorithm
3 : : *
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/pm_qos.h>
18 : : #include <linux/module.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 : 0 : ldev->states[old_idx].stats.promotion_count = 0;
57 : 0 : ldev->states[old_idx].stats.demotion_count = 0;
58 : 0 : ldev->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 : : */
66 : 0 : static int ladder_select_state(struct cpuidle_driver *drv,
67 : 0 : struct cpuidle_device *dev)
68 : : {
69 : 0 : struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
70 : : struct ladder_device_state *last_state;
71 : 0 : int last_residency, last_idx = ldev->last_state_idx;
72 : 0 : int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
73 : :
74 : : /* Special case when user has set very strict latency requirement */
75 [ # # ]: 0 : if (unlikely(latency_req == 0)) {
76 : : ladder_do_selection(ldev, last_idx, 0);
77 : 0 : return 0;
78 : : }
79 : :
80 : 0 : last_state = &ldev->states[last_idx];
81 : :
82 [ # # ]: 0 : if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) {
83 : 0 : last_residency = cpuidle_get_last_residency(dev) - \
84 : 0 : drv->states[last_idx].exit_latency;
85 : : }
86 : : else
87 : 0 : last_residency = last_state->threshold.promotion_time + 1;
88 : :
89 : : /* consider promotion */
90 [ # # ][ # # ]: 0 : if (last_idx < drv->state_count - 1 &&
91 [ # # ]: 0 : !drv->states[last_idx + 1].disabled &&
92 [ # # ]: 0 : !dev->states_usage[last_idx + 1].disable &&
93 [ # # ]: 0 : last_residency > last_state->threshold.promotion_time &&
94 : 0 : drv->states[last_idx + 1].exit_latency <= latency_req) {
95 : 0 : last_state->stats.promotion_count++;
96 : 0 : last_state->stats.demotion_count = 0;
97 [ # # ]: 0 : if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
98 : : ladder_do_selection(ldev, last_idx, last_idx + 1);
99 : 0 : return last_idx + 1;
100 : : }
101 : : }
102 : :
103 : : /* consider demotion */
104 [ # # ][ # # ]: 0 : if (last_idx > CPUIDLE_DRIVER_STATE_START &&
105 [ # # ]: 0 : (drv->states[last_idx].disabled ||
106 [ # # ]: 0 : dev->states_usage[last_idx].disable ||
107 : 0 : drv->states[last_idx].exit_latency > latency_req)) {
108 : : int i;
109 : :
110 [ # # ]: 0 : for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
111 [ # # ]: 0 : if (drv->states[i].exit_latency <= latency_req)
112 : : break;
113 : : }
114 : : ladder_do_selection(ldev, last_idx, i);
115 : 0 : return i;
116 : : }
117 : :
118 [ # # ][ # # ]: 0 : if (last_idx > CPUIDLE_DRIVER_STATE_START &&
119 : 0 : last_residency < last_state->threshold.demotion_time) {
120 : 0 : last_state->stats.demotion_count++;
121 : 0 : last_state->stats.promotion_count = 0;
122 [ # # ]: 0 : if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
123 : 0 : ladder_do_selection(ldev, last_idx, last_idx - 1);
124 : 0 : return last_idx - 1;
125 : : }
126 : : }
127 : :
128 : : /* otherwise remain at the current state */
129 : 0 : return last_idx;
130 : : }
131 : :
132 : : /**
133 : : * ladder_enable_device - setup for the governor
134 : : * @drv: cpuidle driver
135 : : * @dev: the CPU
136 : : */
137 : 0 : static int ladder_enable_device(struct cpuidle_driver *drv,
138 : : struct cpuidle_device *dev)
139 : : {
140 : : int i;
141 : 0 : struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
142 : : struct ladder_device_state *lstate;
143 : : struct cpuidle_state *state;
144 : :
145 : 0 : ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
146 : :
147 [ # # ]: 0 : for (i = 0; i < drv->state_count; i++) {
148 : 0 : state = &drv->states[i];
149 : 0 : lstate = &ldev->states[i];
150 : :
151 : 0 : lstate->stats.promotion_count = 0;
152 : 0 : lstate->stats.demotion_count = 0;
153 : :
154 : 0 : lstate->threshold.promotion_count = PROMOTION_COUNT;
155 : 0 : lstate->threshold.demotion_count = DEMOTION_COUNT;
156 : :
157 [ # # ]: 0 : if (i < drv->state_count - 1)
158 : 0 : lstate->threshold.promotion_time = state->exit_latency;
159 [ # # ]: 0 : if (i > 0)
160 : 0 : lstate->threshold.demotion_time = state->exit_latency;
161 : : }
162 : :
163 : 0 : return 0;
164 : : }
165 : :
166 : : /**
167 : : * ladder_reflect - update the correct last_state_idx
168 : : * @dev: the CPU
169 : : * @index: the index of actual state entered
170 : : */
171 : 0 : static void ladder_reflect(struct cpuidle_device *dev, int index)
172 : : {
173 : 0 : struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
174 [ # # ]: 0 : if (index > 0)
175 : 0 : ldev->last_state_idx = index;
176 : 0 : }
177 : :
178 : : static struct cpuidle_governor ladder_governor = {
179 : : .name = "ladder",
180 : : .rating = 10,
181 : : .enable = ladder_enable_device,
182 : : .select = ladder_select_state,
183 : : .reflect = ladder_reflect,
184 : : .owner = THIS_MODULE,
185 : : };
186 : :
187 : : /**
188 : : * init_ladder - initializes the governor
189 : : */
190 : 0 : static int __init init_ladder(void)
191 : : {
192 : 0 : return cpuidle_register_governor(&ladder_governor);
193 : : }
194 : :
195 : : postcore_initcall(init_ladder);
|