cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ladder.c (5323B)


      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 * @ldev: the ladder device
     48 * @old_idx: the current state index
     49 * @new_idx: the new target state index
     50 */
     51static inline void ladder_do_selection(struct cpuidle_device *dev,
     52				       struct ladder_device *ldev,
     53				       int old_idx, int new_idx)
     54{
     55	ldev->states[old_idx].stats.promotion_count = 0;
     56	ldev->states[old_idx].stats.demotion_count = 0;
     57	dev->last_state_idx = new_idx;
     58}
     59
     60/**
     61 * ladder_select_state - selects the next state to enter
     62 * @drv: cpuidle driver
     63 * @dev: the CPU
     64 * @dummy: not used
     65 */
     66static int ladder_select_state(struct cpuidle_driver *drv,
     67			       struct cpuidle_device *dev, bool *dummy)
     68{
     69	struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
     70	struct ladder_device_state *last_state;
     71	int last_idx = dev->last_state_idx;
     72	int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
     73	s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
     74	s64 last_residency;
     75
     76	/* Special case when user has set very strict latency requirement */
     77	if (unlikely(latency_req == 0)) {
     78		ladder_do_selection(dev, ldev, last_idx, 0);
     79		return 0;
     80	}
     81
     82	last_state = &ldev->states[last_idx];
     83
     84	last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns;
     85
     86	/* consider promotion */
     87	if (last_idx < drv->state_count - 1 &&
     88	    !dev->states_usage[last_idx + 1].disable &&
     89	    last_residency > last_state->threshold.promotion_time_ns &&
     90	    drv->states[last_idx + 1].exit_latency_ns <= latency_req) {
     91		last_state->stats.promotion_count++;
     92		last_state->stats.demotion_count = 0;
     93		if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
     94			ladder_do_selection(dev, ldev, last_idx, last_idx + 1);
     95			return last_idx + 1;
     96		}
     97	}
     98
     99	/* consider demotion */
    100	if (last_idx > first_idx &&
    101	    (dev->states_usage[last_idx].disable ||
    102	    drv->states[last_idx].exit_latency_ns > latency_req)) {
    103		int i;
    104
    105		for (i = last_idx - 1; i > first_idx; i--) {
    106			if (drv->states[i].exit_latency_ns <= latency_req)
    107				break;
    108		}
    109		ladder_do_selection(dev, ldev, last_idx, i);
    110		return i;
    111	}
    112
    113	if (last_idx > first_idx &&
    114	    last_residency < last_state->threshold.demotion_time_ns) {
    115		last_state->stats.demotion_count++;
    116		last_state->stats.promotion_count = 0;
    117		if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
    118			ladder_do_selection(dev, ldev, last_idx, last_idx - 1);
    119			return last_idx - 1;
    120		}
    121	}
    122
    123	/* otherwise remain at the current state */
    124	return last_idx;
    125}
    126
    127/**
    128 * ladder_enable_device - setup for the governor
    129 * @drv: cpuidle driver
    130 * @dev: the CPU
    131 */
    132static int ladder_enable_device(struct cpuidle_driver *drv,
    133				struct cpuidle_device *dev)
    134{
    135	int i;
    136	int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
    137	struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
    138	struct ladder_device_state *lstate;
    139	struct cpuidle_state *state;
    140
    141	dev->last_state_idx = first_idx;
    142
    143	for (i = first_idx; i < drv->state_count; i++) {
    144		state = &drv->states[i];
    145		lstate = &ldev->states[i];
    146
    147		lstate->stats.promotion_count = 0;
    148		lstate->stats.demotion_count = 0;
    149
    150		lstate->threshold.promotion_count = PROMOTION_COUNT;
    151		lstate->threshold.demotion_count = DEMOTION_COUNT;
    152
    153		if (i < drv->state_count - 1)
    154			lstate->threshold.promotion_time_ns = state->exit_latency_ns;
    155		if (i > first_idx)
    156			lstate->threshold.demotion_time_ns = state->exit_latency_ns;
    157	}
    158
    159	return 0;
    160}
    161
    162/**
    163 * ladder_reflect - update the correct last_state_idx
    164 * @dev: the CPU
    165 * @index: the index of actual state entered
    166 */
    167static void ladder_reflect(struct cpuidle_device *dev, int index)
    168{
    169	if (index > 0)
    170		dev->last_state_idx = index;
    171}
    172
    173static struct cpuidle_governor ladder_governor = {
    174	.name =		"ladder",
    175	.rating =	10,
    176	.enable =	ladder_enable_device,
    177	.select =	ladder_select_state,
    178	.reflect =	ladder_reflect,
    179};
    180
    181/**
    182 * init_ladder - initializes the governor
    183 */
    184static int __init init_ladder(void)
    185{
    186	/*
    187	 * When NO_HZ is disabled, or when booting with nohz=off, the ladder
    188	 * governor is better so give it a higher rating than the menu
    189	 * governor.
    190	 */
    191	if (!tick_nohz_enabled)
    192		ladder_governor.rating = 25;
    193
    194	return cpuidle_register_governor(&ladder_governor);
    195}
    196
    197postcore_initcall(init_ladder);