Current window based load tracking only saves history for five windows. A historically heavy task's heavy load will be completely forgotten after five windows of light load. Even before the five window expires, a heavy task wakes up on same CPU it used to run won't trigger any frequency change until end of the window. It would starve for the entire window. It also adds one "small" load window to history because it's accumulating load at a low frequency, further reducing the tracked load for this heavy task. Ideally, scheduler should be able to identify such tasks and notify governor to increase frequency immediately after it wakes up. Add a histogram for each task to track a much longer load history. A prediction will be made based on runtime of previous or current window, histogram data and load tracked in recent windows. Prediction of all tasks that is currently running or runnable on a CPU is aggregated and reported to CPUFreq governor in sched_get_cpus_busy(). sched_get_cpus_busy() now returns predicted busy time in addition to previous window busy time and new task busy time, scaled to the CPU maximum possible frequency. Tunables: - /proc/sys/kernel/sched_gov_alert_freq (KHz) This tunable can be used to further filter the notifications. Frequency alert notification is sent only when the predicted load exceeds previous window load by sched_gov_alert_freq converted to load. Change-Id: If29098cd2c5499163ceaff18668639db76ee8504 Suggested-by: Saravana Kannan <skannan@codeaurora.org> Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org> Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org> Signed-off-by: Junjie Wu <junjiew@codeaurora.org> [joonwoop@codeaurora.org: fixed merge conflicts around __migrate_task() and removed changes for CONFIG_SCHED_QHMP.]
135 lines
2.8 KiB
C
135 lines
2.8 KiB
C
#include "sched.h"
|
|
|
|
/*
|
|
* idle-task scheduling class.
|
|
*
|
|
* (NOTE: these are not related to SCHED_IDLE tasks which are
|
|
* handled in sched/fair.c)
|
|
*/
|
|
|
|
#ifdef CONFIG_SMP
|
|
static int
|
|
select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags)
|
|
{
|
|
return task_cpu(p); /* IDLE tasks as never migrated */
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*
|
|
* Idle tasks are unconditionally rescheduled:
|
|
*/
|
|
static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags)
|
|
{
|
|
resched_curr(rq);
|
|
}
|
|
|
|
static struct task_struct *
|
|
pick_next_task_idle(struct rq *rq, struct task_struct *prev)
|
|
{
|
|
put_prev_task(rq, prev);
|
|
|
|
schedstat_inc(rq, sched_goidle);
|
|
return rq->idle;
|
|
}
|
|
|
|
/*
|
|
* It is not legal to sleep in the idle task - print a warning
|
|
* message if some code attempts to do it:
|
|
*/
|
|
static void
|
|
dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
|
|
{
|
|
raw_spin_unlock_irq(&rq->lock);
|
|
printk(KERN_ERR "bad: scheduling from the idle thread!\n");
|
|
dump_stack();
|
|
raw_spin_lock_irq(&rq->lock);
|
|
}
|
|
|
|
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
|
|
{
|
|
idle_exit_fair(rq);
|
|
rq_last_tick_reset(rq);
|
|
}
|
|
|
|
static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
|
|
{
|
|
}
|
|
|
|
static void set_curr_task_idle(struct rq *rq)
|
|
{
|
|
}
|
|
|
|
static void switched_to_idle(struct rq *rq, struct task_struct *p)
|
|
{
|
|
BUG();
|
|
}
|
|
|
|
static void
|
|
prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
|
|
{
|
|
BUG();
|
|
}
|
|
|
|
static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void update_curr_idle(struct rq *rq)
|
|
{
|
|
}
|
|
|
|
#ifdef CONFIG_SCHED_HMP
|
|
|
|
static void
|
|
inc_hmp_sched_stats_idle(struct rq *rq, struct task_struct *p)
|
|
{
|
|
}
|
|
|
|
static void
|
|
dec_hmp_sched_stats_idle(struct rq *rq, struct task_struct *p)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fixup_hmp_sched_stats_idle(struct rq *rq, struct task_struct *p,
|
|
u32 new_task_load, u32 new_pred_demand)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Simple, special scheduling class for the per-CPU idle tasks:
|
|
*/
|
|
const struct sched_class idle_sched_class = {
|
|
/* .next is NULL */
|
|
/* no enqueue/yield_task for idle tasks */
|
|
|
|
/* dequeue is not valid, we print a debug message there: */
|
|
.dequeue_task = dequeue_task_idle,
|
|
|
|
.check_preempt_curr = check_preempt_curr_idle,
|
|
|
|
.pick_next_task = pick_next_task_idle,
|
|
.put_prev_task = put_prev_task_idle,
|
|
|
|
#ifdef CONFIG_SMP
|
|
.select_task_rq = select_task_rq_idle,
|
|
.set_cpus_allowed = set_cpus_allowed_common,
|
|
#endif
|
|
|
|
.set_curr_task = set_curr_task_idle,
|
|
.task_tick = task_tick_idle,
|
|
|
|
.get_rr_interval = get_rr_interval_idle,
|
|
|
|
.prio_changed = prio_changed_idle,
|
|
.switched_to = switched_to_idle,
|
|
.update_curr = update_curr_idle,
|
|
#ifdef CONFIG_SCHED_HMP
|
|
.inc_hmp_sched_stats = inc_hmp_sched_stats_idle,
|
|
.dec_hmp_sched_stats = dec_hmp_sched_stats_idle,
|
|
.fixup_hmp_sched_stats = fixup_hmp_sched_stats_idle,
|
|
#endif
|
|
};
|