Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 336726
b: refs/heads/master
c: 69a37be
h: refs/heads/master
v: v3
  • Loading branch information
Youquan Song authored and Rafael J. Wysocki committed Nov 14, 2012
1 parent 57aaef5 commit 23cd6a9
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 6 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e45a00d679a788217f35ee4214a32d6d1924160b
refs/heads/master: 69a37beabf1f0a6705c08e879bdd5d82ff6486c4
75 changes: 70 additions & 5 deletions trunk/drivers/cpuidle/governors/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
#define MAX_INTERESTING 50000
#define STDDEV_THRESH 400

/* 60 * 60 > STDDEV_THRESH * INTERVALS = 400 * 8 */
#define MAX_DEVIATION 60

static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer);
static DEFINE_PER_CPU(int, hrtimer_status);
/* menu hrtimer mode */
enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT};

/*
* Concepts and ideas behind the menu governor
Expand Down Expand Up @@ -191,17 +198,42 @@ static u64 div_round64(u64 dividend, u32 divisor)
return div_u64(dividend + (divisor / 2), divisor);
}

/* Cancel the hrtimer if it is not triggered yet */
void menu_hrtimer_cancel(void)
{
int cpu = smp_processor_id();
struct hrtimer *hrtmr = &per_cpu(menu_hrtimer, cpu);

/* The timer is still not time out*/
if (per_cpu(hrtimer_status, cpu)) {
hrtimer_cancel(hrtmr);
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP;
}
}
EXPORT_SYMBOL_GPL(menu_hrtimer_cancel);

/* Call back for hrtimer is triggered */
static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer)
{
int cpu = smp_processor_id();

per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP;

return HRTIMER_NORESTART;
}

/*
* Try detecting repeating patterns by keeping track of the last 8
* intervals, and checking if the standard deviation of that set
* of points is below a threshold. If it is... then use the
* average of these 8 points as the estimated value.
*/
static void detect_repeating_patterns(struct menu_device *data)
static int detect_repeating_patterns(struct menu_device *data)
{
int i;
uint64_t avg = 0;
uint64_t stddev = 0; /* contains the square of the std deviation */
int ret = 0;

/* first calculate average and standard deviation of the past */
for (i = 0; i < INTERVALS; i++)
Expand All @@ -210,7 +242,7 @@ static void detect_repeating_patterns(struct menu_device *data)

/* if the avg is beyond the known next tick, it's worthless */
if (avg > data->expected_us)
return;
return 0;

for (i = 0; i < INTERVALS; i++)
stddev += (data->intervals[i] - avg) *
Expand All @@ -223,8 +255,12 @@ static void detect_repeating_patterns(struct menu_device *data)
* repeating pattern and predict we keep doing this.
*/

if (avg && stddev < STDDEV_THRESH)
if (avg && stddev < STDDEV_THRESH) {
data->predicted_us = avg;
ret = 1;
}

return ret;
}

/**
Expand All @@ -240,6 +276,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
int i;
int multiplier;
struct timespec t;
int repeat = 0, low_predicted = 0;
int cpu = smp_processor_id();
struct hrtimer *hrtmr = &per_cpu(menu_hrtimer, cpu);

if (data->needs_update) {
menu_update(drv, dev);
Expand Down Expand Up @@ -274,7 +313,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket],
RESOLUTION * DECAY);

detect_repeating_patterns(data);
repeat = detect_repeating_patterns(data);

/*
* We want to default to C1 (hlt), not to busy polling
Expand All @@ -295,8 +334,10 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)

if (s->disabled || su->disable)
continue;
if (s->target_residency > data->predicted_us)
if (s->target_residency > data->predicted_us) {
low_predicted = 1;
continue;
}
if (s->exit_latency > latency_req)
continue;
if (s->exit_latency * multiplier > data->predicted_us)
Expand All @@ -309,6 +350,27 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
}
}

/* not deepest C-state chosen for low predicted residency */
if (low_predicted) {
unsigned int timer_us = 0;

/*
* Set a timer to detect whether this sleep is much
* longer than repeat mode predicted. If the timer
* triggers, the code will evaluate whether to put
* the CPU into a deeper C-state.
* The timer is cancelled on CPU wakeup.
*/
timer_us = 2 * (data->predicted_us + MAX_DEVIATION);

if (repeat && (4 * timer_us < data->expected_us)) {
hrtimer_start(hrtmr, ns_to_ktime(1000 * timer_us),
HRTIMER_MODE_REL_PINNED);
/* In repeat case, menu hrtimer is started */
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_REPEAT;
}
}

return data->last_state_idx;
}

Expand Down Expand Up @@ -399,6 +461,9 @@ static int menu_enable_device(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
{
struct menu_device *data = &per_cpu(menu_devices, dev->cpu);
struct hrtimer *t = &per_cpu(menu_hrtimer, dev->cpu);
hrtimer_init(t, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
t->function = menu_hrtimer_notify;

memset(data, 0, sizeof(struct menu_device));

Expand Down
6 changes: 6 additions & 0 deletions trunk/include/linux/tick.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,10 @@ static inline u64 get_cpu_idle_time_us(int cpu, u64 *unused) { return -1; }
static inline u64 get_cpu_iowait_time_us(int cpu, u64 *unused) { return -1; }
# endif /* !NO_HZ */

# ifdef CONFIG_CPU_IDLE_GOV_MENU
extern void menu_hrtimer_cancel(void);
# else
static inline void menu_hrtimer_cancel(void) {}
# endif /* CONFIG_CPU_IDLE_GOV_MENU */

#endif
4 changes: 4 additions & 0 deletions trunk/kernel/time/tick-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ void tick_nohz_irq_exit(void)
if (!ts->inidle)
return;

/* Cancel the timer because CPU already waken up from the C-states*/
menu_hrtimer_cancel();
__tick_nohz_idle_enter(ts);
}

Expand Down Expand Up @@ -621,6 +623,8 @@ void tick_nohz_idle_exit(void)

ts->inidle = 0;

/* Cancel the timer because CPU already waken up from the C-states*/
menu_hrtimer_cancel();
if (ts->idle_active || ts->tick_stopped)
now = ktime_get();

Expand Down

0 comments on commit 23cd6a9

Please sign in to comment.