brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 6617eb4 Raw
199 lines · c
1/*2 * ladder.c - the residency ladder algorithm3 *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 424#define DEMOTION_COUNT 125 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 change47 * @dev: the CPU48 * @ldev: the ladder device49 * @old_idx: the current state index50 * @new_idx: the new target state index51 */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 enter63 * @drv: cpuidle driver64 * @dev: the CPU65 * @dummy: not used66 */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 governor130 * @drv: cpuidle driver131 * @dev: the CPU132 */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_idx165 * @dev: the CPU166 * @index: the index of actual state entered167 */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 governor184 */185static int __init init_ladder(void)186{187	/*188	 * When NO_HZ is disabled, or when booting with nohz=off, the ladder189	 * governor is better so give it a higher rating than the menu190	 * 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);199