0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/kernel.h>
0016 #include <linux/cpuidle.h>
0017 #include <linux/jiffies.h>
0018 #include <linux/tick.h>
0019
0020 #include <asm/io.h>
0021 #include <linux/uaccess.h>
0022
0023 #define PROMOTION_COUNT 4
0024 #define DEMOTION_COUNT 1
0025
0026 struct ladder_device_state {
0027 struct {
0028 u32 promotion_count;
0029 u32 demotion_count;
0030 u64 promotion_time_ns;
0031 u64 demotion_time_ns;
0032 } threshold;
0033 struct {
0034 int promotion_count;
0035 int demotion_count;
0036 } stats;
0037 };
0038
0039 struct ladder_device {
0040 struct ladder_device_state states[CPUIDLE_STATE_MAX];
0041 };
0042
0043 static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
0044
0045
0046
0047
0048
0049
0050
0051 static inline void ladder_do_selection(struct cpuidle_device *dev,
0052 struct ladder_device *ldev,
0053 int old_idx, int new_idx)
0054 {
0055 ldev->states[old_idx].stats.promotion_count = 0;
0056 ldev->states[old_idx].stats.demotion_count = 0;
0057 dev->last_state_idx = new_idx;
0058 }
0059
0060
0061
0062
0063
0064
0065
0066 static int ladder_select_state(struct cpuidle_driver *drv,
0067 struct cpuidle_device *dev, bool *dummy)
0068 {
0069 struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
0070 struct ladder_device_state *last_state;
0071 int last_idx = dev->last_state_idx;
0072 int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
0073 s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
0074 s64 last_residency;
0075
0076
0077 if (unlikely(latency_req == 0)) {
0078 ladder_do_selection(dev, ldev, last_idx, 0);
0079 return 0;
0080 }
0081
0082 last_state = &ldev->states[last_idx];
0083
0084 last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns;
0085
0086
0087 if (last_idx < drv->state_count - 1 &&
0088 !dev->states_usage[last_idx + 1].disable &&
0089 last_residency > last_state->threshold.promotion_time_ns &&
0090 drv->states[last_idx + 1].exit_latency_ns <= latency_req) {
0091 last_state->stats.promotion_count++;
0092 last_state->stats.demotion_count = 0;
0093 if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
0094 ladder_do_selection(dev, ldev, last_idx, last_idx + 1);
0095 return last_idx + 1;
0096 }
0097 }
0098
0099
0100 if (last_idx > first_idx &&
0101 (dev->states_usage[last_idx].disable ||
0102 drv->states[last_idx].exit_latency_ns > latency_req)) {
0103 int i;
0104
0105 for (i = last_idx - 1; i > first_idx; i--) {
0106 if (drv->states[i].exit_latency_ns <= latency_req)
0107 break;
0108 }
0109 ladder_do_selection(dev, ldev, last_idx, i);
0110 return i;
0111 }
0112
0113 if (last_idx > first_idx &&
0114 last_residency < last_state->threshold.demotion_time_ns) {
0115 last_state->stats.demotion_count++;
0116 last_state->stats.promotion_count = 0;
0117 if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
0118 ladder_do_selection(dev, ldev, last_idx, last_idx - 1);
0119 return last_idx - 1;
0120 }
0121 }
0122
0123
0124 return last_idx;
0125 }
0126
0127
0128
0129
0130
0131
0132 static int ladder_enable_device(struct cpuidle_driver *drv,
0133 struct cpuidle_device *dev)
0134 {
0135 int i;
0136 int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
0137 struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
0138 struct ladder_device_state *lstate;
0139 struct cpuidle_state *state;
0140
0141 dev->last_state_idx = first_idx;
0142
0143 for (i = first_idx; i < drv->state_count; i++) {
0144 state = &drv->states[i];
0145 lstate = &ldev->states[i];
0146
0147 lstate->stats.promotion_count = 0;
0148 lstate->stats.demotion_count = 0;
0149
0150 lstate->threshold.promotion_count = PROMOTION_COUNT;
0151 lstate->threshold.demotion_count = DEMOTION_COUNT;
0152
0153 if (i < drv->state_count - 1)
0154 lstate->threshold.promotion_time_ns = state->exit_latency_ns;
0155 if (i > first_idx)
0156 lstate->threshold.demotion_time_ns = state->exit_latency_ns;
0157 }
0158
0159 return 0;
0160 }
0161
0162
0163
0164
0165
0166
0167 static void ladder_reflect(struct cpuidle_device *dev, int index)
0168 {
0169 if (index > 0)
0170 dev->last_state_idx = index;
0171 }
0172
0173 static struct cpuidle_governor ladder_governor = {
0174 .name = "ladder",
0175 .rating = 10,
0176 .enable = ladder_enable_device,
0177 .select = ladder_select_state,
0178 .reflect = ladder_reflect,
0179 };
0180
0181
0182
0183
0184 static int __init init_ladder(void)
0185 {
0186
0187
0188
0189
0190
0191 if (!tick_nohz_enabled)
0192 ladder_governor.rating = 25;
0193
0194 return cpuidle_register_governor(&ladder_governor);
0195 }
0196
0197 postcore_initcall(init_ladder);