File Index
Files and directories
Sign in
FILE

ladder.c

File preview with syntax highlighting.

linux-6.19.14/drivers/cpuidle/governors/ladder.c C
  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/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
 26struct 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
 39struct ladder_device {
 40	struct ladder_device_state states[CPUIDLE_STATE_MAX];
 41};
 42
 43static 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 */
 52static 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 */
 67static 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 */
133static 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 */
168static void ladder_reflect(struct cpuidle_device *dev, int index)
169{
170	if (index > 0)
171		dev->last_state_idx = index;
172}
173
174static 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 */
185static 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
198postcore_initcall(init_ladder);
~/ Read-only file preview.