|
@@ -61,25 +61,15 @@ const_debug unsigned int sysctl_sched_child_runs_first = 1;
|
|
|
*/
|
|
|
unsigned int __read_mostly sysctl_sched_compat_yield;
|
|
|
|
|
|
-/*
|
|
|
- * SCHED_BATCH wake-up granularity.
|
|
|
- * (default: 10 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
|
|
- *
|
|
|
- * This option delays the preemption effects of decoupled workloads
|
|
|
- * and reduces their over-scheduling. Synchronous workloads will still
|
|
|
- * have immediate wakeup/sleep latencies.
|
|
|
- */
|
|
|
-unsigned int sysctl_sched_batch_wakeup_granularity = 10000000UL;
|
|
|
-
|
|
|
/*
|
|
|
* SCHED_OTHER wake-up granularity.
|
|
|
- * (default: 5 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
|
|
+ * (default: 10 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
|
|
*
|
|
|
* This option delays the preemption effects of decoupled workloads
|
|
|
* and reduces their over-scheduling. Synchronous workloads will still
|
|
|
* have immediate wakeup/sleep latencies.
|
|
|
*/
|
|
|
-unsigned int sysctl_sched_wakeup_granularity = 5000000UL;
|
|
|
+unsigned int sysctl_sched_wakeup_granularity = 10000000UL;
|
|
|
|
|
|
const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
|
|
|
|
|
@@ -87,6 +77,11 @@ const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
|
|
|
* CFS operations on generic schedulable entities:
|
|
|
*/
|
|
|
|
|
|
+static inline struct task_struct *task_of(struct sched_entity *se)
|
|
|
+{
|
|
|
+ return container_of(se, struct task_struct, se);
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
|
|
|
/* cpu runqueue to which this cfs_rq is attached */
|
|
@@ -98,6 +93,54 @@ static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
|
|
|
/* An entity is a task if it doesn't "own" a runqueue */
|
|
|
#define entity_is_task(se) (!se->my_q)
|
|
|
|
|
|
+/* Walk up scheduling entities hierarchy */
|
|
|
+#define for_each_sched_entity(se) \
|
|
|
+ for (; se; se = se->parent)
|
|
|
+
|
|
|
+static inline struct cfs_rq *task_cfs_rq(struct task_struct *p)
|
|
|
+{
|
|
|
+ return p->se.cfs_rq;
|
|
|
+}
|
|
|
+
|
|
|
+/* runqueue on which this entity is (to be) queued */
|
|
|
+static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
|
|
|
+{
|
|
|
+ return se->cfs_rq;
|
|
|
+}
|
|
|
+
|
|
|
+/* runqueue "owned" by this group */
|
|
|
+static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp)
|
|
|
+{
|
|
|
+ return grp->my_q;
|
|
|
+}
|
|
|
+
|
|
|
+/* Given a group's cfs_rq on one cpu, return its corresponding cfs_rq on
|
|
|
+ * another cpu ('this_cpu')
|
|
|
+ */
|
|
|
+static inline struct cfs_rq *cpu_cfs_rq(struct cfs_rq *cfs_rq, int this_cpu)
|
|
|
+{
|
|
|
+ return cfs_rq->tg->cfs_rq[this_cpu];
|
|
|
+}
|
|
|
+
|
|
|
+/* Iterate thr' all leaf cfs_rq's on a runqueue */
|
|
|
+#define for_each_leaf_cfs_rq(rq, cfs_rq) \
|
|
|
+ list_for_each_entry_rcu(cfs_rq, &rq->leaf_cfs_rq_list, leaf_cfs_rq_list)
|
|
|
+
|
|
|
+/* Do the two (enqueued) entities belong to the same group ? */
|
|
|
+static inline int
|
|
|
+is_same_group(struct sched_entity *se, struct sched_entity *pse)
|
|
|
+{
|
|
|
+ if (se->cfs_rq == pse->cfs_rq)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct sched_entity *parent_entity(struct sched_entity *se)
|
|
|
+{
|
|
|
+ return se->parent;
|
|
|
+}
|
|
|
+
|
|
|
#else /* CONFIG_FAIR_GROUP_SCHED */
|
|
|
|
|
|
static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
|
|
@@ -107,13 +150,49 @@ static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
|
|
|
|
|
|
#define entity_is_task(se) 1
|
|
|
|
|
|
-#endif /* CONFIG_FAIR_GROUP_SCHED */
|
|
|
+#define for_each_sched_entity(se) \
|
|
|
+ for (; se; se = NULL)
|
|
|
|
|
|
-static inline struct task_struct *task_of(struct sched_entity *se)
|
|
|
+static inline struct cfs_rq *task_cfs_rq(struct task_struct *p)
|
|
|
{
|
|
|
- return container_of(se, struct task_struct, se);
|
|
|
+ return &task_rq(p)->cfs;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
|
|
|
+{
|
|
|
+ struct task_struct *p = task_of(se);
|
|
|
+ struct rq *rq = task_rq(p);
|
|
|
+
|
|
|
+ return &rq->cfs;
|
|
|
+}
|
|
|
+
|
|
|
+/* runqueue "owned" by this group */
|
|
|
+static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp)
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct cfs_rq *cpu_cfs_rq(struct cfs_rq *cfs_rq, int this_cpu)
|
|
|
+{
|
|
|
+ return &cpu_rq(this_cpu)->cfs;
|
|
|
+}
|
|
|
+
|
|
|
+#define for_each_leaf_cfs_rq(rq, cfs_rq) \
|
|
|
+ for (cfs_rq = &rq->cfs; cfs_rq; cfs_rq = NULL)
|
|
|
+
|
|
|
+static inline int
|
|
|
+is_same_group(struct sched_entity *se, struct sched_entity *pse)
|
|
|
+{
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct sched_entity *parent_entity(struct sched_entity *se)
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
+#endif /* CONFIG_FAIR_GROUP_SCHED */
|
|
|
+
|
|
|
|
|
|
/**************************************************************
|
|
|
* Scheduling class tree data structure manipulation methods:
|
|
@@ -254,6 +333,34 @@ int sched_nr_latency_handler(struct ctl_table *table, int write,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+/*
|
|
|
+ * delta *= w / rw
|
|
|
+ */
|
|
|
+static inline unsigned long
|
|
|
+calc_delta_weight(unsigned long delta, struct sched_entity *se)
|
|
|
+{
|
|
|
+ for_each_sched_entity(se) {
|
|
|
+ delta = calc_delta_mine(delta,
|
|
|
+ se->load.weight, &cfs_rq_of(se)->load);
|
|
|
+ }
|
|
|
+
|
|
|
+ return delta;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * delta *= rw / w
|
|
|
+ */
|
|
|
+static inline unsigned long
|
|
|
+calc_delta_fair(unsigned long delta, struct sched_entity *se)
|
|
|
+{
|
|
|
+ for_each_sched_entity(se) {
|
|
|
+ delta = calc_delta_mine(delta,
|
|
|
+ cfs_rq_of(se)->load.weight, &se->load);
|
|
|
+ }
|
|
|
+
|
|
|
+ return delta;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* The idea is to set a period in which each task runs once.
|
|
|
*
|
|
@@ -283,29 +390,54 @@ static u64 __sched_period(unsigned long nr_running)
|
|
|
*/
|
|
|
static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
{
|
|
|
- return calc_delta_mine(__sched_period(cfs_rq->nr_running),
|
|
|
- se->load.weight, &cfs_rq->load);
|
|
|
+ return calc_delta_weight(__sched_period(cfs_rq->nr_running), se);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * We calculate the vruntime slice.
|
|
|
+ * We calculate the vruntime slice of a to be inserted task
|
|
|
*
|
|
|
- * vs = s/w = p/rw
|
|
|
+ * vs = s*rw/w = p
|
|
|
*/
|
|
|
-static u64 __sched_vslice(unsigned long rq_weight, unsigned long nr_running)
|
|
|
+static u64 sched_vslice_add(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
{
|
|
|
- u64 vslice = __sched_period(nr_running);
|
|
|
+ unsigned long nr_running = cfs_rq->nr_running;
|
|
|
|
|
|
- vslice *= NICE_0_LOAD;
|
|
|
- do_div(vslice, rq_weight);
|
|
|
+ if (!se->on_rq)
|
|
|
+ nr_running++;
|
|
|
|
|
|
- return vslice;
|
|
|
+ return __sched_period(nr_running);
|
|
|
}
|
|
|
|
|
|
-static u64 sched_vslice_add(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
+/*
|
|
|
+ * The goal of calc_delta_asym() is to be asymmetrically around NICE_0_LOAD, in
|
|
|
+ * that it favours >=0 over <0.
|
|
|
+ *
|
|
|
+ * -20 |
|
|
|
+ * |
|
|
|
+ * 0 --------+-------
|
|
|
+ * .'
|
|
|
+ * 19 .'
|
|
|
+ *
|
|
|
+ */
|
|
|
+static unsigned long
|
|
|
+calc_delta_asym(unsigned long delta, struct sched_entity *se)
|
|
|
{
|
|
|
- return __sched_vslice(cfs_rq->load.weight + se->load.weight,
|
|
|
- cfs_rq->nr_running + 1);
|
|
|
+ struct load_weight lw = {
|
|
|
+ .weight = NICE_0_LOAD,
|
|
|
+ .inv_weight = 1UL << (WMULT_SHIFT-NICE_0_SHIFT)
|
|
|
+ };
|
|
|
+
|
|
|
+ for_each_sched_entity(se) {
|
|
|
+ struct load_weight *se_lw = &se->load;
|
|
|
+
|
|
|
+ if (se->load.weight < NICE_0_LOAD)
|
|
|
+ se_lw = &lw;
|
|
|
+
|
|
|
+ delta = calc_delta_mine(delta,
|
|
|
+ cfs_rq_of(se)->load.weight, se_lw);
|
|
|
+ }
|
|
|
+
|
|
|
+ return delta;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -322,11 +454,7 @@ __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
|
|
|
|
|
|
curr->sum_exec_runtime += delta_exec;
|
|
|
schedstat_add(cfs_rq, exec_clock, delta_exec);
|
|
|
- delta_exec_weighted = delta_exec;
|
|
|
- if (unlikely(curr->load.weight != NICE_0_LOAD)) {
|
|
|
- delta_exec_weighted = calc_delta_fair(delta_exec_weighted,
|
|
|
- &curr->load);
|
|
|
- }
|
|
|
+ delta_exec_weighted = calc_delta_fair(delta_exec, curr);
|
|
|
curr->vruntime += delta_exec_weighted;
|
|
|
}
|
|
|
|
|
@@ -413,20 +541,43 @@ update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
* Scheduling class queueing methods:
|
|
|
*/
|
|
|
|
|
|
+#if defined CONFIG_SMP && defined CONFIG_FAIR_GROUP_SCHED
|
|
|
+static void
|
|
|
+add_cfs_task_weight(struct cfs_rq *cfs_rq, unsigned long weight)
|
|
|
+{
|
|
|
+ cfs_rq->task_weight += weight;
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline void
|
|
|
+add_cfs_task_weight(struct cfs_rq *cfs_rq, unsigned long weight)
|
|
|
+{
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
static void
|
|
|
account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
{
|
|
|
update_load_add(&cfs_rq->load, se->load.weight);
|
|
|
+ if (!parent_entity(se))
|
|
|
+ inc_cpu_load(rq_of(cfs_rq), se->load.weight);
|
|
|
+ if (entity_is_task(se))
|
|
|
+ add_cfs_task_weight(cfs_rq, se->load.weight);
|
|
|
cfs_rq->nr_running++;
|
|
|
se->on_rq = 1;
|
|
|
+ list_add(&se->group_node, &cfs_rq->tasks);
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
{
|
|
|
update_load_sub(&cfs_rq->load, se->load.weight);
|
|
|
+ if (!parent_entity(se))
|
|
|
+ dec_cpu_load(rq_of(cfs_rq), se->load.weight);
|
|
|
+ if (entity_is_task(se))
|
|
|
+ add_cfs_task_weight(cfs_rq, -se->load.weight);
|
|
|
cfs_rq->nr_running--;
|
|
|
se->on_rq = 0;
|
|
|
+ list_del_init(&se->group_node);
|
|
|
}
|
|
|
|
|
|
static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
@@ -510,8 +661,12 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
|
|
|
|
|
|
if (!initial) {
|
|
|
/* sleeps upto a single latency don't count. */
|
|
|
- if (sched_feat(NEW_FAIR_SLEEPERS))
|
|
|
- vruntime -= sysctl_sched_latency;
|
|
|
+ if (sched_feat(NEW_FAIR_SLEEPERS)) {
|
|
|
+ if (sched_feat(NORMALIZED_SLEEPER))
|
|
|
+ vruntime -= calc_delta_weight(sysctl_sched_latency, se);
|
|
|
+ else
|
|
|
+ vruntime -= sysctl_sched_latency;
|
|
|
+ }
|
|
|
|
|
|
/* ensure we never gain time by being placed backwards. */
|
|
|
vruntime = max_vruntime(se->vruntime, vruntime);
|
|
@@ -627,20 +782,16 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
se->prev_sum_exec_runtime = se->sum_exec_runtime;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se);
|
|
|
+
|
|
|
static struct sched_entity *
|
|
|
pick_next(struct cfs_rq *cfs_rq, struct sched_entity *se)
|
|
|
{
|
|
|
- s64 diff, gran;
|
|
|
-
|
|
|
if (!cfs_rq->next)
|
|
|
return se;
|
|
|
|
|
|
- diff = cfs_rq->next->vruntime - se->vruntime;
|
|
|
- if (diff < 0)
|
|
|
- return se;
|
|
|
-
|
|
|
- gran = calc_delta_fair(sysctl_sched_wakeup_granularity, &cfs_rq->load);
|
|
|
- if (diff > gran)
|
|
|
+ if (wakeup_preempt_entity(cfs_rq->next, se) != 0)
|
|
|
return se;
|
|
|
|
|
|
return cfs_rq->next;
|
|
@@ -708,101 +859,6 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
|
|
|
* CFS operations on tasks:
|
|
|
*/
|
|
|
|
|
|
-#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
-
|
|
|
-/* Walk up scheduling entities hierarchy */
|
|
|
-#define for_each_sched_entity(se) \
|
|
|
- for (; se; se = se->parent)
|
|
|
-
|
|
|
-static inline struct cfs_rq *task_cfs_rq(struct task_struct *p)
|
|
|
-{
|
|
|
- return p->se.cfs_rq;
|
|
|
-}
|
|
|
-
|
|
|
-/* runqueue on which this entity is (to be) queued */
|
|
|
-static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
|
|
|
-{
|
|
|
- return se->cfs_rq;
|
|
|
-}
|
|
|
-
|
|
|
-/* runqueue "owned" by this group */
|
|
|
-static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp)
|
|
|
-{
|
|
|
- return grp->my_q;
|
|
|
-}
|
|
|
-
|
|
|
-/* Given a group's cfs_rq on one cpu, return its corresponding cfs_rq on
|
|
|
- * another cpu ('this_cpu')
|
|
|
- */
|
|
|
-static inline struct cfs_rq *cpu_cfs_rq(struct cfs_rq *cfs_rq, int this_cpu)
|
|
|
-{
|
|
|
- return cfs_rq->tg->cfs_rq[this_cpu];
|
|
|
-}
|
|
|
-
|
|
|
-/* Iterate thr' all leaf cfs_rq's on a runqueue */
|
|
|
-#define for_each_leaf_cfs_rq(rq, cfs_rq) \
|
|
|
- list_for_each_entry_rcu(cfs_rq, &rq->leaf_cfs_rq_list, leaf_cfs_rq_list)
|
|
|
-
|
|
|
-/* Do the two (enqueued) entities belong to the same group ? */
|
|
|
-static inline int
|
|
|
-is_same_group(struct sched_entity *se, struct sched_entity *pse)
|
|
|
-{
|
|
|
- if (se->cfs_rq == pse->cfs_rq)
|
|
|
- return 1;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static inline struct sched_entity *parent_entity(struct sched_entity *se)
|
|
|
-{
|
|
|
- return se->parent;
|
|
|
-}
|
|
|
-
|
|
|
-#else /* CONFIG_FAIR_GROUP_SCHED */
|
|
|
-
|
|
|
-#define for_each_sched_entity(se) \
|
|
|
- for (; se; se = NULL)
|
|
|
-
|
|
|
-static inline struct cfs_rq *task_cfs_rq(struct task_struct *p)
|
|
|
-{
|
|
|
- return &task_rq(p)->cfs;
|
|
|
-}
|
|
|
-
|
|
|
-static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
|
|
|
-{
|
|
|
- struct task_struct *p = task_of(se);
|
|
|
- struct rq *rq = task_rq(p);
|
|
|
-
|
|
|
- return &rq->cfs;
|
|
|
-}
|
|
|
-
|
|
|
-/* runqueue "owned" by this group */
|
|
|
-static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp)
|
|
|
-{
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-static inline struct cfs_rq *cpu_cfs_rq(struct cfs_rq *cfs_rq, int this_cpu)
|
|
|
-{
|
|
|
- return &cpu_rq(this_cpu)->cfs;
|
|
|
-}
|
|
|
-
|
|
|
-#define for_each_leaf_cfs_rq(rq, cfs_rq) \
|
|
|
- for (cfs_rq = &rq->cfs; cfs_rq; cfs_rq = NULL)
|
|
|
-
|
|
|
-static inline int
|
|
|
-is_same_group(struct sched_entity *se, struct sched_entity *pse)
|
|
|
-{
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-static inline struct sched_entity *parent_entity(struct sched_entity *se)
|
|
|
-{
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-#endif /* CONFIG_FAIR_GROUP_SCHED */
|
|
|
-
|
|
|
#ifdef CONFIG_SCHED_HRTICK
|
|
|
static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
|
|
|
{
|
|
@@ -916,7 +972,7 @@ static void yield_task_fair(struct rq *rq)
|
|
|
/*
|
|
|
* Already in the rightmost position?
|
|
|
*/
|
|
|
- if (unlikely(rightmost->vruntime < se->vruntime))
|
|
|
+ if (unlikely(!rightmost || rightmost->vruntime < se->vruntime))
|
|
|
return;
|
|
|
|
|
|
/*
|
|
@@ -955,7 +1011,9 @@ static int wake_idle(int cpu, struct task_struct *p)
|
|
|
return cpu;
|
|
|
|
|
|
for_each_domain(cpu, sd) {
|
|
|
- if (sd->flags & SD_WAKE_IDLE) {
|
|
|
+ if ((sd->flags & SD_WAKE_IDLE)
|
|
|
+ || ((sd->flags & SD_WAKE_IDLE_FAR)
|
|
|
+ && !task_hot(p, task_rq(p)->clock, sd))) {
|
|
|
cpus_and(tmp, sd->span, p->cpus_allowed);
|
|
|
for_each_cpu_mask(i, tmp) {
|
|
|
if (idle_cpu(i)) {
|
|
@@ -1099,6 +1157,58 @@ out:
|
|
|
}
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
|
|
+static unsigned long wakeup_gran(struct sched_entity *se)
|
|
|
+{
|
|
|
+ unsigned long gran = sysctl_sched_wakeup_granularity;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * More easily preempt - nice tasks, while not making it harder for
|
|
|
+ * + nice tasks.
|
|
|
+ */
|
|
|
+ gran = calc_delta_asym(sysctl_sched_wakeup_granularity, se);
|
|
|
+
|
|
|
+ return gran;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Should 'se' preempt 'curr'.
|
|
|
+ *
|
|
|
+ * |s1
|
|
|
+ * |s2
|
|
|
+ * |s3
|
|
|
+ * g
|
|
|
+ * |<--->|c
|
|
|
+ *
|
|
|
+ * w(c, s1) = -1
|
|
|
+ * w(c, s2) = 0
|
|
|
+ * w(c, s3) = 1
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int
|
|
|
+wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)
|
|
|
+{
|
|
|
+ s64 gran, vdiff = curr->vruntime - se->vruntime;
|
|
|
+
|
|
|
+ if (vdiff < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ gran = wakeup_gran(curr);
|
|
|
+ if (vdiff > gran)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* return depth at which a sched entity is present in the hierarchy */
|
|
|
+static inline int depth_se(struct sched_entity *se)
|
|
|
+{
|
|
|
+ int depth = 0;
|
|
|
+
|
|
|
+ for_each_sched_entity(se)
|
|
|
+ depth++;
|
|
|
+
|
|
|
+ return depth;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Preempt the current task with a newly woken task if needed:
|
|
@@ -1108,7 +1218,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
|
|
|
struct task_struct *curr = rq->curr;
|
|
|
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
|
|
|
struct sched_entity *se = &curr->se, *pse = &p->se;
|
|
|
- unsigned long gran;
|
|
|
+ int se_depth, pse_depth;
|
|
|
|
|
|
if (unlikely(rt_prio(p->prio))) {
|
|
|
update_rq_clock(rq);
|
|
@@ -1133,20 +1243,33 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
|
|
|
if (!sched_feat(WAKEUP_PREEMPT))
|
|
|
return;
|
|
|
|
|
|
- while (!is_same_group(se, pse)) {
|
|
|
+ /*
|
|
|
+ * preemption test can be made between sibling entities who are in the
|
|
|
+ * same cfs_rq i.e who have a common parent. Walk up the hierarchy of
|
|
|
+ * both tasks until we find their ancestors who are siblings of common
|
|
|
+ * parent.
|
|
|
+ */
|
|
|
+
|
|
|
+ /* First walk up until both entities are at same depth */
|
|
|
+ se_depth = depth_se(se);
|
|
|
+ pse_depth = depth_se(pse);
|
|
|
+
|
|
|
+ while (se_depth > pse_depth) {
|
|
|
+ se_depth--;
|
|
|
se = parent_entity(se);
|
|
|
+ }
|
|
|
+
|
|
|
+ while (pse_depth > se_depth) {
|
|
|
+ pse_depth--;
|
|
|
pse = parent_entity(pse);
|
|
|
}
|
|
|
|
|
|
- gran = sysctl_sched_wakeup_granularity;
|
|
|
- /*
|
|
|
- * More easily preempt - nice tasks, while not making
|
|
|
- * it harder for + nice tasks.
|
|
|
- */
|
|
|
- if (unlikely(se->load.weight > NICE_0_LOAD))
|
|
|
- gran = calc_delta_fair(gran, &se->load);
|
|
|
+ while (!is_same_group(se, pse)) {
|
|
|
+ se = parent_entity(se);
|
|
|
+ pse = parent_entity(pse);
|
|
|
+ }
|
|
|
|
|
|
- if (pse->vruntime + gran < se->vruntime)
|
|
|
+ if (wakeup_preempt_entity(se, pse) == 1)
|
|
|
resched_task(curr);
|
|
|
}
|
|
|
|
|
@@ -1197,15 +1320,27 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
|
|
|
* the current task:
|
|
|
*/
|
|
|
static struct task_struct *
|
|
|
-__load_balance_iterator(struct cfs_rq *cfs_rq, struct rb_node *curr)
|
|
|
+__load_balance_iterator(struct cfs_rq *cfs_rq, struct list_head *next)
|
|
|
{
|
|
|
- struct task_struct *p;
|
|
|
+ struct task_struct *p = NULL;
|
|
|
+ struct sched_entity *se;
|
|
|
+
|
|
|
+ if (next == &cfs_rq->tasks)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Skip over entities that are not tasks */
|
|
|
+ do {
|
|
|
+ se = list_entry(next, struct sched_entity, group_node);
|
|
|
+ next = next->next;
|
|
|
+ } while (next != &cfs_rq->tasks && !entity_is_task(se));
|
|
|
|
|
|
- if (!curr)
|
|
|
+ if (next == &cfs_rq->tasks)
|
|
|
return NULL;
|
|
|
|
|
|
- p = rb_entry(curr, struct task_struct, se.run_node);
|
|
|
- cfs_rq->rb_load_balance_curr = rb_next(curr);
|
|
|
+ cfs_rq->balance_iterator = next;
|
|
|
+
|
|
|
+ if (entity_is_task(se))
|
|
|
+ p = task_of(se);
|
|
|
|
|
|
return p;
|
|
|
}
|
|
@@ -1214,85 +1349,100 @@ static struct task_struct *load_balance_start_fair(void *arg)
|
|
|
{
|
|
|
struct cfs_rq *cfs_rq = arg;
|
|
|
|
|
|
- return __load_balance_iterator(cfs_rq, first_fair(cfs_rq));
|
|
|
+ return __load_balance_iterator(cfs_rq, cfs_rq->tasks.next);
|
|
|
}
|
|
|
|
|
|
static struct task_struct *load_balance_next_fair(void *arg)
|
|
|
{
|
|
|
struct cfs_rq *cfs_rq = arg;
|
|
|
|
|
|
- return __load_balance_iterator(cfs_rq, cfs_rq->rb_load_balance_curr);
|
|
|
+ return __load_balance_iterator(cfs_rq, cfs_rq->balance_iterator);
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
-static int cfs_rq_best_prio(struct cfs_rq *cfs_rq)
|
|
|
+static unsigned long
|
|
|
+__load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
|
|
|
+ unsigned long max_load_move, struct sched_domain *sd,
|
|
|
+ enum cpu_idle_type idle, int *all_pinned, int *this_best_prio,
|
|
|
+ struct cfs_rq *cfs_rq)
|
|
|
{
|
|
|
- struct sched_entity *curr;
|
|
|
- struct task_struct *p;
|
|
|
-
|
|
|
- if (!cfs_rq->nr_running || !first_fair(cfs_rq))
|
|
|
- return MAX_PRIO;
|
|
|
-
|
|
|
- curr = cfs_rq->curr;
|
|
|
- if (!curr)
|
|
|
- curr = __pick_next_entity(cfs_rq);
|
|
|
+ struct rq_iterator cfs_rq_iterator;
|
|
|
|
|
|
- p = task_of(curr);
|
|
|
+ cfs_rq_iterator.start = load_balance_start_fair;
|
|
|
+ cfs_rq_iterator.next = load_balance_next_fair;
|
|
|
+ cfs_rq_iterator.arg = cfs_rq;
|
|
|
|
|
|
- return p->prio;
|
|
|
+ return balance_tasks(this_rq, this_cpu, busiest,
|
|
|
+ max_load_move, sd, idle, all_pinned,
|
|
|
+ this_best_prio, &cfs_rq_iterator);
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
+#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
static unsigned long
|
|
|
load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
|
|
|
unsigned long max_load_move,
|
|
|
struct sched_domain *sd, enum cpu_idle_type idle,
|
|
|
int *all_pinned, int *this_best_prio)
|
|
|
{
|
|
|
- struct cfs_rq *busy_cfs_rq;
|
|
|
long rem_load_move = max_load_move;
|
|
|
- struct rq_iterator cfs_rq_iterator;
|
|
|
-
|
|
|
- cfs_rq_iterator.start = load_balance_start_fair;
|
|
|
- cfs_rq_iterator.next = load_balance_next_fair;
|
|
|
+ int busiest_cpu = cpu_of(busiest);
|
|
|
+ struct task_group *tg;
|
|
|
|
|
|
- for_each_leaf_cfs_rq(busiest, busy_cfs_rq) {
|
|
|
-#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
- struct cfs_rq *this_cfs_rq;
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry(tg, &task_groups, list) {
|
|
|
long imbalance;
|
|
|
- unsigned long maxload;
|
|
|
+ unsigned long this_weight, busiest_weight;
|
|
|
+ long rem_load, max_load, moved_load;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * empty group
|
|
|
+ */
|
|
|
+ if (!aggregate(tg, sd)->task_weight)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rem_load = rem_load_move * aggregate(tg, sd)->rq_weight;
|
|
|
+ rem_load /= aggregate(tg, sd)->load + 1;
|
|
|
+
|
|
|
+ this_weight = tg->cfs_rq[this_cpu]->task_weight;
|
|
|
+ busiest_weight = tg->cfs_rq[busiest_cpu]->task_weight;
|
|
|
+
|
|
|
+ imbalance = (busiest_weight - this_weight) / 2;
|
|
|
|
|
|
- this_cfs_rq = cpu_cfs_rq(busy_cfs_rq, this_cpu);
|
|
|
+ if (imbalance < 0)
|
|
|
+ imbalance = busiest_weight;
|
|
|
|
|
|
- imbalance = busy_cfs_rq->load.weight - this_cfs_rq->load.weight;
|
|
|
- /* Don't pull if this_cfs_rq has more load than busy_cfs_rq */
|
|
|
- if (imbalance <= 0)
|
|
|
+ max_load = max(rem_load, imbalance);
|
|
|
+ moved_load = __load_balance_fair(this_rq, this_cpu, busiest,
|
|
|
+ max_load, sd, idle, all_pinned, this_best_prio,
|
|
|
+ tg->cfs_rq[busiest_cpu]);
|
|
|
+
|
|
|
+ if (!moved_load)
|
|
|
continue;
|
|
|
|
|
|
- /* Don't pull more than imbalance/2 */
|
|
|
- imbalance /= 2;
|
|
|
- maxload = min(rem_load_move, imbalance);
|
|
|
+ move_group_shares(tg, sd, busiest_cpu, this_cpu);
|
|
|
|
|
|
- *this_best_prio = cfs_rq_best_prio(this_cfs_rq);
|
|
|
-#else
|
|
|
-# define maxload rem_load_move
|
|
|
-#endif
|
|
|
- /*
|
|
|
- * pass busy_cfs_rq argument into
|
|
|
- * load_balance_[start|next]_fair iterators
|
|
|
- */
|
|
|
- cfs_rq_iterator.arg = busy_cfs_rq;
|
|
|
- rem_load_move -= balance_tasks(this_rq, this_cpu, busiest,
|
|
|
- maxload, sd, idle, all_pinned,
|
|
|
- this_best_prio,
|
|
|
- &cfs_rq_iterator);
|
|
|
+ moved_load *= aggregate(tg, sd)->load;
|
|
|
+ moved_load /= aggregate(tg, sd)->rq_weight + 1;
|
|
|
|
|
|
- if (rem_load_move <= 0)
|
|
|
+ rem_load_move -= moved_load;
|
|
|
+ if (rem_load_move < 0)
|
|
|
break;
|
|
|
}
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
return max_load_move - rem_load_move;
|
|
|
}
|
|
|
+#else
|
|
|
+static unsigned long
|
|
|
+load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
|
|
|
+ unsigned long max_load_move,
|
|
|
+ struct sched_domain *sd, enum cpu_idle_type idle,
|
|
|
+ int *all_pinned, int *this_best_prio)
|
|
|
+{
|
|
|
+ return __load_balance_fair(this_rq, this_cpu, busiest,
|
|
|
+ max_load_move, sd, idle, all_pinned,
|
|
|
+ this_best_prio, &busiest->cfs);
|
|
|
+}
|
|
|
+#endif
|
|
|
|
|
|
static int
|
|
|
move_one_task_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
|
|
@@ -1461,16 +1611,40 @@ static const struct sched_class fair_sched_class = {
|
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_SCHED_DEBUG
|
|
|
+static void
|
|
|
+print_cfs_rq_tasks(struct seq_file *m, struct cfs_rq *cfs_rq, int depth)
|
|
|
+{
|
|
|
+ struct sched_entity *se;
|
|
|
+
|
|
|
+ if (!cfs_rq)
|
|
|
+ return;
|
|
|
+
|
|
|
+ list_for_each_entry_rcu(se, &cfs_rq->tasks, group_node) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = depth; i; i--)
|
|
|
+ seq_puts(m, " ");
|
|
|
+
|
|
|
+ seq_printf(m, "%lu %s %lu\n",
|
|
|
+ se->load.weight,
|
|
|
+ entity_is_task(se) ? "T" : "G",
|
|
|
+ calc_delta_weight(SCHED_LOAD_SCALE, se)
|
|
|
+ );
|
|
|
+ if (!entity_is_task(se))
|
|
|
+ print_cfs_rq_tasks(m, group_cfs_rq(se), depth + 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void print_cfs_stats(struct seq_file *m, int cpu)
|
|
|
{
|
|
|
struct cfs_rq *cfs_rq;
|
|
|
|
|
|
-#ifdef CONFIG_FAIR_GROUP_SCHED
|
|
|
- print_cfs_rq(m, cpu, &cpu_rq(cpu)->cfs);
|
|
|
-#endif
|
|
|
rcu_read_lock();
|
|
|
for_each_leaf_cfs_rq(cpu_rq(cpu), cfs_rq)
|
|
|
print_cfs_rq(m, cpu, cfs_rq);
|
|
|
+
|
|
|
+ seq_printf(m, "\nWeight tree:\n");
|
|
|
+ print_cfs_rq_tasks(m, &cpu_rq(cpu)->cfs, 1);
|
|
|
rcu_read_unlock();
|
|
|
}
|
|
|
#endif
|