summaryrefslogtreecommitdiff
path: root/kernel/sched/fair.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sched/fair.c')
-rw-r--r--kernel/sched/fair.c143
1 files changed, 63 insertions, 80 deletions
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c52c29dcb6a2..cfc982205b98 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -998,71 +998,17 @@ static inline bool __entity_less(struct rb_node *a, const struct rb_node *b)
return entity_before(__node_2_se(a), __node_2_se(b));
}
-static inline void __min_vruntime_update(struct sched_entity *se, struct rb_node *node)
+/* min() for wrapping vruntimes */
+static inline u64 __min_vruntime(u64 a, u64 b)
{
- if (node) {
- struct sched_entity *rse = __node_2_se(node);
-
- if (vruntime_cmp(se->min_vruntime, ">", rse->min_vruntime))
- se->min_vruntime = rse->min_vruntime;
- }
-}
-
-static inline void __min_slice_update(struct sched_entity *se, struct rb_node *node)
-{
- if (node) {
- struct sched_entity *rse = __node_2_se(node);
- if (rse->min_slice < se->min_slice)
- se->min_slice = rse->min_slice;
- }
-}
-
-static inline void __max_slice_update(struct sched_entity *se, struct rb_node *node)
-{
- if (node) {
- struct sched_entity *rse = __node_2_se(node);
- if (rse->max_slice > se->max_slice)
- se->max_slice = rse->max_slice;
- }
-}
-
-static inline void min_vruntime_copy(struct sched_entity *new, struct sched_entity *old)
-{
- new->min_vruntime = old->min_vruntime;
- new->min_slice = old->min_slice;
- new->max_slice = old->max_slice;
-}
-
-/*
- * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime)
- */
-static inline bool min_vruntime_update(struct sched_entity *se, bool exit)
-{
- u64 old_min_vruntime = se->min_vruntime;
- u64 old_min_slice = se->min_slice;
- u64 old_max_slice = se->max_slice;
- struct rb_node *node = &se->run_node;
-
- se->min_vruntime = se->vruntime;
- __min_vruntime_update(se, node->rb_right);
- __min_vruntime_update(se, node->rb_left);
-
- se->min_slice = se->slice;
- __min_slice_update(se, node->rb_right);
- __min_slice_update(se, node->rb_left);
-
- se->max_slice = se->slice;
- __max_slice_update(se, node->rb_right);
- __max_slice_update(se, node->rb_left);
-
- return se->min_vruntime == old_min_vruntime &&
- se->min_slice == old_min_slice &&
- se->max_slice == old_max_slice;
+ return vruntime_cmp(a, "<", b) ? a : b;
}
-
-RB_DECLARE_CALLBACKS_MULTI(static, min_vruntime_cb, struct sched_entity,
- run_node, min_vruntime_copy, min_vruntime_update);
+RB_DECLARE_CALLBACKS(static, min_vruntime_cb,
+ struct sched_entity, run_node,
+ RB_AUG(vruntime, min_vruntime, __min_vruntime),
+ RB_AUG(slice, min_slice, min),
+ RB_AUG(slice, max_slice, max));
/*
* Enqueue an entity into the rb-tree:
@@ -1142,13 +1088,8 @@ static inline void set_protect_slice(struct cfs_rq *cfs_rq, struct sched_entity
static inline void update_protect_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
u64 vruntime = min_vruntime(se->vruntime, avg_vruntime(cfs_rq));
- u64 slice = normalized_sysctl_sched_base_slice;
- u64 vprot;
-
- if (sched_feat(RUN_TO_PARITY))
- slice = cfs_rq_min_slice(cfs_rq);
-
- vprot = min_vruntime(se->vprot, vruntime + calc_delta_fair(slice, se));
+ u64 slice = cfs_rq_min_slice(cfs_rq);
+ u64 vprot = min_vruntime(se->vprot, vruntime + calc_delta_fair(slice, se));
if (sched_feat(PREEMPT_SHORT) && slice != se->slice)
vprot = min_vruntime(vprot, ineligible_vruntime(cfs_rq));
@@ -2196,7 +2137,7 @@ void init_sched_mm(struct task_struct *p) { }
static void task_tick_cache(struct rq *rq, struct task_struct *p) { }
static inline int get_pref_llc(struct task_struct *p,
- struct mm_struct *mm)
+ struct sched_cache_group *grp)
{
return -1;
}
@@ -6468,12 +6409,20 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
load = cfs_rq->sum_weight;
if (curr && curr->on_rq)
load += avg_vruntime_weight(cfs_rq, curr->h_load.weight);
-
- weight = avg_vruntime_weight(cfs_rq, se->h_load.weight);
- lag *= load + weight;
if (WARN_ON_ONCE(!load))
load = 1;
- lag = div64_long(lag, load);
+
+ weight = avg_vruntime_weight(cfs_rq, se->h_load.weight);
+
+ /*
+ * Rewrite, because the product (W + w_i)*vl'_i might
+ * overflow s64:
+ *
+ * vl_i = (W + w_i)*vl'_i / W
+ * = (W*vl'_i + w_i*vl'_i) / W
+ * = vl'_i + w_i*vl'_i/W
+ */
+ lag += div64_long(lag * weight, load);
/*
* A heavy entity (relative to the tree) will pull the
@@ -8214,6 +8163,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
struct sched_entity *se = &p->se;
struct cfs_rq *cfs_rq = &rq->cfs;
unsigned long weight;
+ bool curr;
if (task_is_throttled(p) && enqueue_throttled_task(p))
return;
@@ -8242,14 +8192,25 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
if (p->in_iowait)
cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT);
+ curr = (cfs_rq->curr == se);
+ /*
+ * This can actually happen with CFS_BANDWIDTH, a task can be throttled
+ * and dequeued, unthrottled from another CPU and then enqueued again
+ * before schedule().
+ */
+ if (curr)
+ place_entity(cfs_rq, se, flags);
if (se->on_rq && se->sched_delayed)
requeue_delayed_entity(cfs_rq, se);
weight = enqueue_hierarchy(p, flags);
- reweight_eevdf(cfs_rq, se, weight, false);
- place_entity(cfs_rq, se, flags | ENQUEUE_QUEUED);
- __enqueue_entity(cfs_rq, se);
+
+ if (!curr) {
+ reweight_eevdf(cfs_rq, se, weight, false);
+ place_entity(cfs_rq, se, flags | ENQUEUE_QUEUED);
+ __enqueue_entity(cfs_rq, se);
+ }
if (!rq_h_nr_queued && rq->cfs.h_nr_queued)
dl_server_start(&rq->fair_server);
@@ -10978,6 +10939,8 @@ static inline bool task_misfits_asym_cpu(struct lb_env *env, struct task_struct
return false;
}
+static inline bool sched_asym(struct sched_domain *sd, int dst_cpu, int src_cpu);
+
/*
* Check if task p can migrate from source LLC to
* destination LLC in terms of cache aware load balance.
@@ -11002,6 +10965,10 @@ static enum llc_mig can_migrate_llc_task(struct lb_env *env,
if (cpu < 0 || cpus_share_cache(src_cpu, dst_cpu))
return mig_unrestricted;
+ /* Prioritize asym packing to idle core over cache awareness */
+ if (env->idle && sched_asym(env->sd, dst_cpu, src_cpu))
+ return mig_unrestricted;
+
/* skip cache aware load balance for too many threads */
if (invalid_llc_nr(grp, p, dst_cpu) ||
exceed_llc_capacity(grp, dst_cpu)) {
@@ -12142,6 +12109,14 @@ static void record_sg_llc_stats(struct lb_env *env,
return;
/*
+ * A redo repeats stats published on the first pass. Hotplug can
+ * still leave env->cpus partial, so util would not match capacity.
+ */
+ if ((env->flags & LBF_ALL_PINNED) ||
+ !cpumask_subset(sched_group_span(group), env->cpus))
+ return;
+
+ /*
* At this point we know this group spans a LLC domain.
* Record the statistic of this group in its corresponding
* shared LLC domain.
@@ -12185,6 +12160,14 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs,
return false;
/*
+ * On asym packing domains, if the destination CPU
+ * has higher priority than all CPUs in the source group,
+ * prioritize asym packing.
+ */
+ if (sgs->group_asym_packing)
+ return false;
+
+ /*
* Skip cache aware tagging if nr_balanced_failed is sufficiently high.
* Threshold of cache_nice_tries is set to 1 higher than nr_balance_failed
* to avoid excessive task migration at the same time.
@@ -13599,12 +13582,12 @@ static int need_active_balance(struct lb_env *env)
{
struct sched_domain *sd = env->sd;
- if (alb_break_llc(env))
- return 0;
-
if (asym_active_balance(env))
return 1;
+ if (alb_break_llc(env))
+ return 0;
+
if (imbalanced_active_balance(env))
return 1;