mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
CPUIDLE: Port Linux menu governor to replace the initial ladder governor
The ladder governor has long pro/demotion delay shortcome while applying to tickless mode, because it needs to count usage. Menu governor chooses the next state simply via break-event prediction including the factors of next timer event & last residency time etc, so it would have faster response speed. Signed-off-by: Gang Wei <gang.wei@intel.com>
This commit is contained in:
1 parent
9851c8e77e
commit
246127c0bc
4 files changed
+287
-336
No files matched your search
@@ -1,5 +1,5 @@
|
||||
subdir-y += cpufreq
|
||||
|
||||
obj-y += boot.o
|
||||
obj-y += power.o suspend.o wakeup_prot.o cpu_idle.o
|
||||
obj-y += power.o suspend.o wakeup_prot.o cpu_idle.o cpuidle_menu.o
|
||||
obj-y += pmstat.o
|
||||
+72
-335
@@ -39,6 +39,7 @@
|
||||
#include <xen/smp.h>
|
||||
#include <xen/guest_access.h>
|
||||
#include <xen/keyhandler.h>
|
||||
#include <xen/cpuidle.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hpet.h>
|
||||
@@ -49,13 +50,10 @@
|
||||
#define DEBUG_PM_CX
|
||||
|
||||
#define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000)
|
||||
#define PM_TIMER_TICKS_TO_US(t) ((t * 1000) / (PM_TIMER_FREQUENCY / 1000))
|
||||
#define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */
|
||||
#define C3_OVERHEAD 4 /* 1us (3.579 ticks per us) */
|
||||
|
||||
#define ACPI_PROCESSOR_MAX_POWER 8
|
||||
#define ACPI_PROCESSOR_MAX_C2_LATENCY 100
|
||||
#define ACPI_PROCESSOR_MAX_C3_LATENCY 1000
|
||||
|
||||
static void (*lapic_timer_off)(void);
|
||||
static void (*lapic_timer_on)(void);
|
||||
|
||||
@@ -65,66 +63,6 @@ extern void (*pm_idle) (void);
|
||||
static void (*pm_idle_save) (void) __read_mostly;
|
||||
unsigned int max_cstate __read_mostly = 2;
|
||||
integer_param("max_cstate", max_cstate);
|
||||
/*
|
||||
* bm_history -- bit-mask with a bit per jiffy of bus-master activity
|
||||
* 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms
|
||||
* 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms
|
||||
* 100 HZ: 0x0000000F: 4 jiffies = 40ms
|
||||
* reduce history for more aggressive entry into C3
|
||||
*/
|
||||
unsigned int bm_history __read_mostly =
|
||||
(HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1));
|
||||
integer_param("bm_history", bm_history);
|
||||
|
||||
struct acpi_processor_cx;
|
||||
|
||||
struct acpi_processor_cx_policy
|
||||
{
|
||||
u32 count;
|
||||
struct acpi_processor_cx *state;
|
||||
struct
|
||||
{
|
||||
u32 time;
|
||||
u32 ticks;
|
||||
u32 count;
|
||||
u32 bm;
|
||||
} threshold;
|
||||
};
|
||||
|
||||
struct acpi_processor_cx
|
||||
{
|
||||
u8 valid;
|
||||
u8 type;
|
||||
u32 address;
|
||||
u8 space_id;
|
||||
u32 latency;
|
||||
u32 latency_ticks;
|
||||
u32 power;
|
||||
u32 usage;
|
||||
u64 time;
|
||||
struct acpi_processor_cx_policy promotion;
|
||||
struct acpi_processor_cx_policy demotion;
|
||||
};
|
||||
|
||||
struct acpi_processor_flags
|
||||
{
|
||||
u8 bm_control:1;
|
||||
u8 bm_check:1;
|
||||
u8 has_cst:1;
|
||||
u8 power_setup_done:1;
|
||||
u8 bm_rld_set:1;
|
||||
};
|
||||
|
||||
struct acpi_processor_power
|
||||
{
|
||||
struct acpi_processor_flags flags;
|
||||
struct acpi_processor_cx *state;
|
||||
s_time_t bm_check_timestamp;
|
||||
u32 default_state;
|
||||
u32 bm_activity;
|
||||
u32 count;
|
||||
struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER];
|
||||
};
|
||||
|
||||
static struct acpi_processor_power processor_powers[NR_CPUS];
|
||||
|
||||
@@ -133,26 +71,21 @@ static void print_acpi_power(uint32_t cpu, struct acpi_processor_power *power)
|
||||
uint32_t i;
|
||||
|
||||
printk("==cpu%d==\n", cpu);
|
||||
printk("active state:\t\tC%d\n", (power->state)?power->state->type:-1);
|
||||
printk("active state:\t\tC%d\n",
|
||||
(power->last_state) ? power->last_state->type : -1);
|
||||
printk("max_cstate:\t\tC%d\n", max_cstate);
|
||||
printk("bus master activity:\t%08x\n", power->bm_activity);
|
||||
printk("states:\n");
|
||||
|
||||
for ( i = 1; i < power->count; i++ )
|
||||
{
|
||||
printk((power->states[i].type == power->state->type) ? " *" : " ");
|
||||
if ( power->last_state &&
|
||||
power->states[i].type == power->last_state->type )
|
||||
printk(" *");
|
||||
else
|
||||
printk(" ");
|
||||
printk("C%d:\t\t", i);
|
||||
printk("type[C%d] ", power->states[i].type);
|
||||
if ( power->states[i].promotion.state )
|
||||
printk("promotion[C%d] ", power->states[i].promotion.state->type);
|
||||
else
|
||||
printk("promotion[--] ");
|
||||
if ( power->states[i].demotion.state )
|
||||
printk("demotion[C%d] ", power->states[i].demotion.state->type);
|
||||
else
|
||||
printk("demotion[--] ");
|
||||
printk("latency[%03d]\n ", power->states[i].latency);
|
||||
printk("\t\t\t");
|
||||
printk("latency[%03d] ", power->states[i].latency);
|
||||
printk("usage[%08d] ", power->states[i].usage);
|
||||
printk("duration[%"PRId64"]\n", power->states[i].time);
|
||||
}
|
||||
@@ -182,48 +115,6 @@ static inline u32 ticks_elapsed(u32 t1, u32 t2)
|
||||
return ((0xFFFFFFFF - t1) + t2);
|
||||
}
|
||||
|
||||
static void acpi_processor_power_activate(struct acpi_processor_power *power,
|
||||
struct acpi_processor_cx *new)
|
||||
{
|
||||
struct acpi_processor_cx *old;
|
||||
|
||||
if ( !power || !new )
|
||||
return;
|
||||
|
||||
old = power->state;
|
||||
|
||||
if ( old )
|
||||
old->promotion.count = 0;
|
||||
new->demotion.count = 0;
|
||||
|
||||
/* Cleanup from old state. */
|
||||
if ( old )
|
||||
{
|
||||
switch ( old->type )
|
||||
{
|
||||
case ACPI_STATE_C3:
|
||||
/* Disable bus master reload */
|
||||
if ( new->type != ACPI_STATE_C3 && power->flags.bm_check )
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare to use new state. */
|
||||
switch ( new->type )
|
||||
{
|
||||
case ACPI_STATE_C3:
|
||||
/* Enable bus master reload */
|
||||
if ( old->type != ACPI_STATE_C3 && power->flags.bm_check )
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
power->state = new;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void acpi_safe_halt(void)
|
||||
{
|
||||
smp_mb__after_clear_bit();
|
||||
@@ -263,6 +154,40 @@ static void acpi_idle_do_entry(struct acpi_processor_cx *cx)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void acpi_idle_update_bm_rld(struct acpi_processor_power *power,
|
||||
struct acpi_processor_cx *target)
|
||||
{
|
||||
if ( !power->flags.bm_check )
|
||||
return;
|
||||
|
||||
if ( power->flags.bm_rld_set && target->type != ACPI_STATE_C3 )
|
||||
{
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
|
||||
power->flags.bm_rld_set = 0;
|
||||
}
|
||||
|
||||
if ( !power->flags.bm_rld_set && target->type == ACPI_STATE_C3 )
|
||||
{
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
|
||||
power->flags.bm_rld_set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_idle_bm_check(void)
|
||||
{
|
||||
u32 bm_status = 0;
|
||||
|
||||
acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status);
|
||||
if ( bm_status )
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1);
|
||||
/*
|
||||
* TBD: PIIX4 Erratum #18: Note that BM_STS doesn't always reflect
|
||||
* the true state of bus mastering activity; forcing us to
|
||||
* manually check the BMIDEA bit of each IDE channel.
|
||||
*/
|
||||
return bm_status;
|
||||
}
|
||||
|
||||
static struct {
|
||||
spinlock_t lock;
|
||||
unsigned int count;
|
||||
@@ -272,7 +197,7 @@ static void acpi_processor_idle(void)
|
||||
{
|
||||
struct acpi_processor_power *power = NULL;
|
||||
struct acpi_processor_cx *cx = NULL;
|
||||
struct acpi_processor_cx *next_state = NULL;
|
||||
int next_state;
|
||||
int sleep_ticks = 0;
|
||||
u32 t1, t2 = 0;
|
||||
|
||||
@@ -290,7 +215,16 @@ static void acpi_processor_idle(void)
|
||||
return;
|
||||
}
|
||||
|
||||
cx = power->state;
|
||||
next_state = cpuidle_current_governor->select(power);
|
||||
if ( next_state > 0 )
|
||||
{
|
||||
cx = &power->states[next_state];
|
||||
if ( power->flags.bm_check && acpi_idle_bm_check()
|
||||
&& cx->type == ACPI_STATE_C3 )
|
||||
cx = power->safe_state;
|
||||
if ( cx->type > max_cstate )
|
||||
cx = &power->states[max_cstate];
|
||||
}
|
||||
if ( !cx )
|
||||
{
|
||||
if ( pm_idle_save )
|
||||
@@ -306,69 +240,14 @@ static void acpi_processor_idle(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check BM Activity
|
||||
* -----------------
|
||||
* Check for bus mastering activity (if required), record, and check
|
||||
* for demotion.
|
||||
*/
|
||||
if ( power->flags.bm_check )
|
||||
{
|
||||
u32 bm_status = 0;
|
||||
unsigned long diff = (NOW() - power->bm_check_timestamp) >> 23;
|
||||
|
||||
if ( diff > 31 )
|
||||
diff = 31;
|
||||
|
||||
power->bm_activity <<= diff;
|
||||
|
||||
acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status);
|
||||
if ( bm_status )
|
||||
{
|
||||
power->bm_activity |= 0x1;
|
||||
acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1);
|
||||
}
|
||||
/*
|
||||
* PIIX4 Erratum #18: Note that BM_STS doesn't always reflect
|
||||
* the true state of bus mastering activity; forcing us to
|
||||
* manually check the BMIDEA bit of each IDE channel.
|
||||
*/
|
||||
/*else if ( errata.piix4.bmisx )
|
||||
{
|
||||
if ( (inb_p(errata.piix4.bmisx + 0x02) & 0x01)
|
||||
|| (inb_p(errata.piix4.bmisx + 0x0A) & 0x01) )
|
||||
pr->power.bm_activity |= 0x1;
|
||||
}*/
|
||||
|
||||
power->bm_check_timestamp = NOW();
|
||||
|
||||
/*
|
||||
* If bus mastering is or was active this jiffy, demote
|
||||
* to avoid a faulty transition. Note that the processor
|
||||
* won't enter a low-power state during this call (to this
|
||||
* function) but should upon the next.
|
||||
*
|
||||
* TBD: A better policy might be to fallback to the demotion
|
||||
* state (use it for this quantum only) istead of
|
||||
* demoting -- and rely on duration as our sole demotion
|
||||
* qualification. This may, however, introduce DMA
|
||||
* issues (e.g. floppy DMA transfer overrun/underrun).
|
||||
*/
|
||||
if ( (power->bm_activity & 0x1) && cx->demotion.threshold.bm )
|
||||
{
|
||||
local_irq_enable();
|
||||
next_state = cx->demotion.state;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
power->last_state = cx;
|
||||
|
||||
/*
|
||||
* Sleep:
|
||||
* ------
|
||||
* Invoke the current Cx state to put the processor to sleep.
|
||||
*/
|
||||
if ( cx->type == ACPI_STATE_C2 || cx->type == ACPI_STATE_C3 )
|
||||
smp_mb__after_clear_bit();
|
||||
acpi_idle_update_bm_rld(power, cx);
|
||||
|
||||
switch ( cx->type )
|
||||
{
|
||||
@@ -480,162 +359,13 @@ static void acpi_processor_idle(void)
|
||||
|
||||
cx->usage++;
|
||||
if ( sleep_ticks > 0 )
|
||||
{
|
||||
power->last_residency = PM_TIMER_TICKS_TO_US(sleep_ticks);
|
||||
cx->time += sleep_ticks;
|
||||
|
||||
next_state = power->state;
|
||||
|
||||
/*
|
||||
* Promotion?
|
||||
* ----------
|
||||
* Track the number of longs (time asleep is greater than threshold)
|
||||
* and promote when the count threshold is reached. Note that bus
|
||||
* mastering activity may prevent promotions.
|
||||
* Do not promote above max_cstate.
|
||||
*/
|
||||
if ( cx->promotion.state &&
|
||||
((cx->promotion.state - power->states) <= max_cstate) )
|
||||
{
|
||||
if ( sleep_ticks > cx->promotion.threshold.ticks )
|
||||
{
|
||||
cx->promotion.count++;
|
||||
cx->demotion.count = 0;
|
||||
if ( cx->promotion.count >= cx->promotion.threshold.count )
|
||||
{
|
||||
if ( power->flags.bm_check )
|
||||
{
|
||||
if ( !(power->bm_activity & cx->promotion.threshold.bm) )
|
||||
{
|
||||
next_state = cx->promotion.state;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
next_state = cx->promotion.state;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Demotion?
|
||||
* ---------
|
||||
* Track the number of shorts (time asleep is less than time threshold)
|
||||
* and demote when the usage threshold is reached.
|
||||
*/
|
||||
if ( cx->demotion.state )
|
||||
{
|
||||
if ( sleep_ticks < cx->demotion.threshold.ticks )
|
||||
{
|
||||
cx->demotion.count++;
|
||||
cx->promotion.count = 0;
|
||||
if ( cx->demotion.count >= cx->demotion.threshold.count )
|
||||
{
|
||||
next_state = cx->demotion.state;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
/*
|
||||
* Demote if current state exceeds max_cstate
|
||||
*/
|
||||
if ( (power->state - power->states) > max_cstate )
|
||||
{
|
||||
if ( cx->demotion.state )
|
||||
next_state = cx->demotion.state;
|
||||
}
|
||||
|
||||
/*
|
||||
* New Cx State?
|
||||
* -------------
|
||||
* If we're going to start using a new Cx state we must clean up
|
||||
* from the previous and prepare to use the new.
|
||||
*/
|
||||
if ( next_state != power->state )
|
||||
acpi_processor_power_activate(power, next_state);
|
||||
}
|
||||
|
||||
static int acpi_processor_set_power_policy(struct acpi_processor_power *power)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int state_is_set = 0;
|
||||
struct acpi_processor_cx *lower = NULL;
|
||||
struct acpi_processor_cx *higher = NULL;
|
||||
struct acpi_processor_cx *cx;
|
||||
|
||||
if ( !power )
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* This function sets the default Cx state policy (OS idle handler).
|
||||
* Our scheme is to promote quickly to C2 but more conservatively
|
||||
* to C3. We're favoring C2 for its characteristics of low latency
|
||||
* (quick response), good power savings, and ability to allow bus
|
||||
* mastering activity. Note that the Cx state policy is completely
|
||||
* customizable and can be altered dynamically.
|
||||
*/
|
||||
|
||||
/* startup state */
|
||||
for ( i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++ )
|
||||
{
|
||||
cx = &power->states[i];
|
||||
if ( !cx->valid )
|
||||
continue;
|
||||
|
||||
if ( !state_is_set )
|
||||
power->state = cx;
|
||||
state_is_set++;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !state_is_set )
|
||||
return -ENODEV;
|
||||
|
||||
/* demotion */
|
||||
for ( i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++ )
|
||||
{
|
||||
cx = &power->states[i];
|
||||
if ( !cx->valid )
|
||||
continue;
|
||||
|
||||
if ( lower )
|
||||
{
|
||||
cx->demotion.state = lower;
|
||||
cx->demotion.threshold.ticks = cx->latency_ticks;
|
||||
cx->demotion.threshold.count = 1;
|
||||
if ( cx->type == ACPI_STATE_C3 )
|
||||
cx->demotion.threshold.bm = bm_history;
|
||||
}
|
||||
|
||||
lower = cx;
|
||||
}
|
||||
|
||||
/* promotion */
|
||||
for ( i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i-- )
|
||||
{
|
||||
cx = &power->states[i];
|
||||
if ( !cx->valid )
|
||||
continue;
|
||||
|
||||
if ( higher )
|
||||
{
|
||||
cx->promotion.state = higher;
|
||||
cx->promotion.threshold.ticks = cx->latency_ticks;
|
||||
if ( cx->type >= ACPI_STATE_C2 )
|
||||
cx->promotion.threshold.count = 4;
|
||||
else
|
||||
cx->promotion.threshold.count = 10;
|
||||
if ( higher->type == ACPI_STATE_C3 )
|
||||
cx->promotion.threshold.bm = bm_history;
|
||||
}
|
||||
|
||||
higher = cx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if ( cpuidle_current_governor->reflect )
|
||||
cpuidle_current_governor->reflect(power);
|
||||
}
|
||||
|
||||
static int init_cx_pminfo(struct acpi_processor_power *acpi_power)
|
||||
@@ -824,6 +554,8 @@ static int check_cx(struct acpi_processor_power *power, xen_processor_cx_t *cx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int latency_factor = 2;
|
||||
|
||||
static void set_cx(
|
||||
struct acpi_processor_power *acpi_power,
|
||||
xen_processor_cx_t *xen_cx)
|
||||
@@ -845,6 +577,9 @@ static void set_cx(
|
||||
cx->power = xen_cx->power;
|
||||
|
||||
cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency);
|
||||
cx->target_residency = cx->latency * latency_factor;
|
||||
if ( cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2 )
|
||||
acpi_power->safe_state = cx;
|
||||
}
|
||||
|
||||
int get_cpu_id(u8 acpi_id)
|
||||
@@ -939,6 +674,7 @@ long set_cx_pminfo(uint32_t cpu, struct xen_processor_power *power)
|
||||
|
||||
init_cx_pminfo(acpi_power);
|
||||
|
||||
acpi_power->cpu = cpu_id;
|
||||
acpi_power->flags.bm_check = power->flags.bm_check;
|
||||
acpi_power->flags.bm_control = power->flags.bm_control;
|
||||
acpi_power->flags.has_cst = power->flags.has_cst;
|
||||
@@ -953,10 +689,11 @@ long set_cx_pminfo(uint32_t cpu, struct xen_processor_power *power)
|
||||
set_cx(acpi_power, &xen_cx);
|
||||
}
|
||||
|
||||
if ( cpuidle_current_governor->enable &&
|
||||
cpuidle_current_governor->enable(acpi_power) )
|
||||
return -EFAULT;
|
||||
|
||||
/* FIXME: C-state dependency is not supported by far */
|
||||
|
||||
/* initialize default policy */
|
||||
acpi_processor_set_power_policy(acpi_power);
|
||||
|
||||
print_acpi_power(cpu_id, acpi_power);
|
||||
|
||||
@@ -981,7 +718,7 @@ int pmstat_get_cx_stat(uint32_t cpuid, struct pm_cx_stat *stat)
|
||||
uint64_t usage;
|
||||
int i;
|
||||
|
||||
stat->last = (power->state) ? power->state->type : 0;
|
||||
stat->last = (power->last_state) ? power->last_state->type : 0;
|
||||
stat->nr = processor_powers[cpuid].count;
|
||||
stat->idle_time = v->runstate.time[RUNSTATE_running];
|
||||
if ( v->is_running )
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* cpuidle_menu - menu governor for cpu idle, main idea come from Linux.
|
||||
* drivers/cpuidle/governors/menu.c
|
||||
*
|
||||
* Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
|
||||
* Copyright (C) 2007, 2008 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <xen/config.h>
|
||||
#include <xen/errno.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/acpi.h>
|
||||
#include <xen/timer.h>
|
||||
#include <xen/cpuidle.h>
|
||||
|
||||
#define BREAK_FUZZ 4 /* 4 us */
|
||||
#define USEC_PER_SEC 1000000
|
||||
|
||||
struct menu_device
|
||||
{
|
||||
int last_state_idx;
|
||||
unsigned int expected_us;
|
||||
unsigned int predicted_us;
|
||||
unsigned int last_measured_us;
|
||||
unsigned int elapsed_us;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct menu_device, menu_devices);
|
||||
|
||||
static s_time_t get_sleep_length_ns(void)
|
||||
{
|
||||
return per_cpu(timer_deadline, smp_processor_id()) - NOW();
|
||||
}
|
||||
|
||||
static int menu_select(struct acpi_processor_power *power)
|
||||
{
|
||||
struct menu_device *data = &__get_cpu_var(menu_devices);
|
||||
int i;
|
||||
|
||||
/* determine the expected residency time */
|
||||
data->expected_us = (u32) get_sleep_length_ns() / 1000;
|
||||
|
||||
/* find the deepest idle state that satisfies our constraints */
|
||||
for ( i = 1; i < power->count; i++ )
|
||||
{
|
||||
struct acpi_processor_cx *s = &power->states[i];
|
||||
|
||||
if ( s->target_residency > data->expected_us + s->latency )
|
||||
break;
|
||||
if ( s->target_residency > data->predicted_us )
|
||||
break;
|
||||
/* TBD: we need to check the QoS requirment in future */
|
||||
}
|
||||
|
||||
data->last_state_idx = i - 1;
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
static void menu_reflect(struct acpi_processor_power *power)
|
||||
{
|
||||
struct menu_device *data = &__get_cpu_var(menu_devices);
|
||||
struct acpi_processor_cx *target = &power->states[data->last_state_idx];
|
||||
unsigned int last_residency;
|
||||
unsigned int measured_us;
|
||||
|
||||
/*
|
||||
* Ugh, this idle state doesn't support residency measurements, so we
|
||||
* are basically lost in the dark. As a compromise, assume we slept
|
||||
* for one full standard timer tick. However, be aware that this
|
||||
* could potentially result in a suboptimal state transition.
|
||||
*/
|
||||
if ( target->type == ACPI_STATE_C1 )
|
||||
last_residency = USEC_PER_SEC / HZ;
|
||||
else
|
||||
last_residency = power->last_residency;
|
||||
|
||||
measured_us = last_residency + data->elapsed_us;
|
||||
|
||||
/* if wrapping, set to max uint (-1) */
|
||||
measured_us = data->elapsed_us <= measured_us ? measured_us : -1;
|
||||
|
||||
/* Predict time remaining until next break event */
|
||||
data->predicted_us = max(measured_us, data->last_measured_us);
|
||||
|
||||
/* Distinguish between expected & non-expected events */
|
||||
if ( last_residency + BREAK_FUZZ
|
||||
< data->expected_us + target->latency )
|
||||
{
|
||||
data->last_measured_us = measured_us;
|
||||
data->elapsed_us = 0;
|
||||
}
|
||||
else
|
||||
data->elapsed_us = measured_us;
|
||||
}
|
||||
|
||||
static int menu_enable_device(struct acpi_processor_power *power)
|
||||
{
|
||||
struct menu_device *data = &per_cpu(menu_devices, power->cpu);
|
||||
|
||||
memset(data, 0, sizeof(struct menu_device));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cpuidle_governor menu_governor =
|
||||
{
|
||||
.name = "menu",
|
||||
.rating = 20,
|
||||
.enable = menu_enable_device,
|
||||
.select = menu_select,
|
||||
.reflect = menu_reflect,
|
||||
};
|
||||
|
||||
struct cpuidle_governor *cpuidle_current_governor = &menu_governor;
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* cpuidle.h - xen idle state module derived from Linux
|
||||
*
|
||||
* (C) 2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
||||
* Shaohua Li <shaohua.li@intel.com>
|
||||
* Adam Belay <abelay@novell.com>
|
||||
* Copyright (C) 2008 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _XEN_CPUIDLE_H
|
||||
#define _XEN_CPUIDLE_H
|
||||
|
||||
#define ACPI_PROCESSOR_MAX_POWER 8
|
||||
#define CPUIDLE_NAME_LEN 16
|
||||
|
||||
struct acpi_processor_cx
|
||||
{
|
||||
u8 valid;
|
||||
u8 type;
|
||||
u32 address;
|
||||
u8 space_id;
|
||||
u32 latency;
|
||||
u32 latency_ticks;
|
||||
u32 power;
|
||||
u32 usage;
|
||||
u64 time;
|
||||
u32 target_residency;
|
||||
};
|
||||
|
||||
struct acpi_processor_flags
|
||||
{
|
||||
u8 bm_control:1;
|
||||
u8 bm_check:1;
|
||||
u8 has_cst:1;
|
||||
u8 power_setup_done:1;
|
||||
u8 bm_rld_set:1;
|
||||
};
|
||||
|
||||
struct acpi_processor_power
|
||||
{
|
||||
unsigned int cpu;
|
||||
struct acpi_processor_flags flags;
|
||||
struct acpi_processor_cx *last_state;
|
||||
struct acpi_processor_cx *safe_state;
|
||||
u32 last_residency;
|
||||
void *gdata; /* governor specific data */
|
||||
u32 count;
|
||||
struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER];
|
||||
};
|
||||
|
||||
struct cpuidle_governor
|
||||
{
|
||||
char name[CPUIDLE_NAME_LEN];
|
||||
unsigned int rating;
|
||||
|
||||
int (*enable) (struct acpi_processor_power *dev);
|
||||
void (*disable) (struct acpi_processor_power *dev);
|
||||
|
||||
int (*select) (struct acpi_processor_power *dev);
|
||||
void (*reflect) (struct acpi_processor_power *dev);
|
||||
};
|
||||
|
||||
extern struct cpuidle_governor *cpuidle_current_governor;
|
||||
|
||||
#endif /* _XEN_CPUIDLE_H */
|
||||