blob: d85bde88b4345569c794f5dd7134a2b2a20b6ce0 [file] [log] [blame]
Len Brown4f86d3a2007-10-03 18:58:00 -04001/*
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>
Jean Pihete8db0be2011-08-25 15:35:03 +020017#include <linux/pm_qos.h>
Paul Gortmaker70e522a2011-08-29 17:52:39 -040018#include <linux/module.h>
Len Brown4f86d3a2007-10-03 18:58:00 -040019#include <linux/jiffies.h>
Jean Delvare66a5f6b2016-01-11 17:41:53 +010020#include <linux/tick.h>
Alex Shi2b8870c2016-08-16 12:07:31 +080021#include <linux/cpu.h>
Len Brown4f86d3a2007-10-03 18:58:00 -040022
23#include <asm/io.h>
24#include <asm/uaccess.h>
25
26#define PROMOTION_COUNT 4
27#define DEMOTION_COUNT 1
28
29struct ladder_device_state {
30 struct {
31 u32 promotion_count;
32 u32 demotion_count;
33 u32 promotion_time;
34 u32 demotion_time;
35 } threshold;
36 struct {
37 int promotion_count;
38 int demotion_count;
39 } stats;
40};
41
42struct ladder_device {
43 struct ladder_device_state states[CPUIDLE_STATE_MAX];
44 int last_state_idx;
45};
46
47static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
48
49/**
50 * ladder_do_selection - prepares private data for a state change
51 * @ldev: the ladder device
52 * @old_idx: the current state index
53 * @new_idx: the new target state index
54 */
55static inline void ladder_do_selection(struct ladder_device *ldev,
56 int old_idx, int new_idx)
57{
58 ldev->states[old_idx].stats.promotion_count = 0;
59 ldev->states[old_idx].stats.demotion_count = 0;
60 ldev->last_state_idx = new_idx;
61}
62
63/**
64 * ladder_select_state - selects the next state to enter
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053065 * @drv: cpuidle driver
Len Brown4f86d3a2007-10-03 18:58:00 -040066 * @dev: the CPU
67 */
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053068static int ladder_select_state(struct cpuidle_driver *drv,
69 struct cpuidle_device *dev)
Len Brown4f86d3a2007-10-03 18:58:00 -040070{
Christoph Lameter229b6862014-08-17 12:30:30 -050071 struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
Len Brown4f86d3a2007-10-03 18:58:00 -040072 struct ladder_device_state *last_state;
Alex Shi2b8870c2016-08-16 12:07:31 +080073 struct device *device = get_cpu_device(dev->cpu);
Len Brown4f86d3a2007-10-03 18:58:00 -040074 int last_residency, last_idx = ldev->last_state_idx;
Mark Grossed771342010-05-06 01:59:26 +020075 int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Alex Shi2b8870c2016-08-16 12:07:31 +080076 int resume_latency = dev_pm_qos_read_value(device);
77
78 /* resume_latency is 0 means no restriction */
79 if (resume_latency && resume_latency < latency_req)
80 latency_req = resume_latency;
Len Brown4f86d3a2007-10-03 18:58:00 -040081
venkatesh.pallipadi@intel.coma2bd92022008-07-30 19:21:42 -070082 /* Special case when user has set very strict latency requirement */
83 if (unlikely(latency_req == 0)) {
84 ladder_do_selection(ldev, last_idx, 0);
85 return 0;
86 }
87
Len Brown4f86d3a2007-10-03 18:58:00 -040088 last_state = &ldev->states[last_idx];
89
Len Brownb73026b2014-12-16 01:52:07 -050090 last_residency = cpuidle_get_last_residency(dev) - drv->states[last_idx].exit_latency;
Len Brown4f86d3a2007-10-03 18:58:00 -040091
92 /* consider promotion */
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053093 if (last_idx < drv->state_count - 1 &&
Rafael J. Wysocki66804c12012-08-15 20:28:52 +020094 !drv->states[last_idx + 1].disabled &&
Carsten Emde62d6ae82012-07-19 20:34:10 +000095 !dev->states_usage[last_idx + 1].disable &&
Len Brown4f86d3a2007-10-03 18:58:00 -040096 last_residency > last_state->threshold.promotion_time &&
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053097 drv->states[last_idx + 1].exit_latency <= latency_req) {
Len Brown4f86d3a2007-10-03 18:58:00 -040098 last_state->stats.promotion_count++;
99 last_state->stats.demotion_count = 0;
100 if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
101 ladder_do_selection(ldev, last_idx, last_idx + 1);
102 return last_idx + 1;
103 }
104 }
105
106 /* consider demotion */
venkatesh.pallipadi@intel.coma2bd92022008-07-30 19:21:42 -0700107 if (last_idx > CPUIDLE_DRIVER_STATE_START &&
Rafael J. Wysocki66804c12012-08-15 20:28:52 +0200108 (drv->states[last_idx].disabled ||
109 dev->states_usage[last_idx].disable ||
Carsten Emde62d6ae82012-07-19 20:34:10 +0000110 drv->states[last_idx].exit_latency > latency_req)) {
venkatesh.pallipadi@intel.com06d9e902008-07-30 19:21:44 -0700111 int i;
112
113 for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +0530114 if (drv->states[i].exit_latency <= latency_req)
venkatesh.pallipadi@intel.com06d9e902008-07-30 19:21:44 -0700115 break;
116 }
117 ladder_do_selection(ldev, last_idx, i);
118 return i;
119 }
120
121 if (last_idx > CPUIDLE_DRIVER_STATE_START &&
Len Brown4f86d3a2007-10-03 18:58:00 -0400122 last_residency < last_state->threshold.demotion_time) {
123 last_state->stats.demotion_count++;
124 last_state->stats.promotion_count = 0;
125 if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
126 ladder_do_selection(ldev, last_idx, last_idx - 1);
127 return last_idx - 1;
128 }
129 }
130
131 /* otherwise remain at the current state */
132 return last_idx;
133}
134
135/**
136 * ladder_enable_device - setup for the governor
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +0530137 * @drv: cpuidle driver
Len Brown4f86d3a2007-10-03 18:58:00 -0400138 * @dev: the CPU
139 */
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +0530140static int ladder_enable_device(struct cpuidle_driver *drv,
141 struct cpuidle_device *dev)
Len Brown4f86d3a2007-10-03 18:58:00 -0400142{
143 int i;
144 struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
145 struct ladder_device_state *lstate;
146 struct cpuidle_state *state;
147
venkatesh.pallipadi@intel.coma2bd92022008-07-30 19:21:42 -0700148 ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
Len Brown4f86d3a2007-10-03 18:58:00 -0400149
Mohammad Merajul Islam Molla14692442014-07-28 09:58:50 +0600150 for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +0530151 state = &drv->states[i];
Len Brown4f86d3a2007-10-03 18:58:00 -0400152 lstate = &ldev->states[i];
153
154 lstate->stats.promotion_count = 0;
155 lstate->stats.demotion_count = 0;
156
157 lstate->threshold.promotion_count = PROMOTION_COUNT;
158 lstate->threshold.demotion_count = DEMOTION_COUNT;
159
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +0530160 if (i < drv->state_count - 1)
Len Brown4f86d3a2007-10-03 18:58:00 -0400161 lstate->threshold.promotion_time = state->exit_latency;
Mohammad Merajul Islam Molla14692442014-07-28 09:58:50 +0600162 if (i > CPUIDLE_DRIVER_STATE_START)
Len Brown4f86d3a2007-10-03 18:58:00 -0400163 lstate->threshold.demotion_time = state->exit_latency;
164 }
165
166 return 0;
167}
168
Deepthi Dharware978aa72011-10-28 16:20:09 +0530169/**
170 * ladder_reflect - update the correct last_state_idx
171 * @dev: the CPU
172 * @index: the index of actual state entered
173 */
174static void ladder_reflect(struct cpuidle_device *dev, int index)
175{
Christoph Lameter229b6862014-08-17 12:30:30 -0500176 struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
Deepthi Dharware978aa72011-10-28 16:20:09 +0530177 if (index > 0)
178 ldev->last_state_idx = index;
179}
180
Len Brown4f86d3a2007-10-03 18:58:00 -0400181static struct cpuidle_governor ladder_governor = {
182 .name = "ladder",
183 .rating = 10,
184 .enable = ladder_enable_device,
185 .select = ladder_select_state,
Deepthi Dharware978aa72011-10-28 16:20:09 +0530186 .reflect = ladder_reflect,
Len Brown4f86d3a2007-10-03 18:58:00 -0400187 .owner = THIS_MODULE,
188};
189
190/**
191 * init_ladder - initializes the governor
192 */
193static int __init init_ladder(void)
194{
Jean Delvare66a5f6b2016-01-11 17:41:53 +0100195 /*
196 * When NO_HZ is disabled, or when booting with nohz=off, the ladder
197 * governor is better so give it a higher rating than the menu
198 * governor.
199 */
200 if (!tick_nohz_enabled)
201 ladder_governor.rating = 25;
202
Len Brown4f86d3a2007-10-03 18:58:00 -0400203 return cpuidle_register_governor(&ladder_governor);
204}
205
Daniel Lezcano137b9442013-06-12 15:08:48 +0200206postcore_initcall(init_ladder);