*/
 
 /* real prio, less is less */
-static inline bool prio_less(struct task_struct *a, struct task_struct *b)
+static inline bool prio_less(struct task_struct *a, struct task_struct *b, bool in_fi)
 {
 
        int pa = __task_prio(a), pb = __task_prio(b);
        if (pa == -1) /* dl_prio() doesn't work because of stop_class above */
                return !dl_time_before(a->dl.deadline, b->dl.deadline);
 
-       if (pa == MAX_RT_PRIO + MAX_NICE)  { /* fair */
-               u64 vruntime = b->se.vruntime;
-
-               /*
-                * Normalize the vruntime if tasks are in different cpus.
-                */
-               if (task_cpu(a) != task_cpu(b)) {
-                       vruntime -= task_cfs_rq(b)->min_vruntime;
-                       vruntime += task_cfs_rq(a)->min_vruntime;
-               }
-
-               return !((s64)(a->se.vruntime - vruntime) <= 0);
-       }
+       if (pa == MAX_RT_PRIO + MAX_NICE)       /* fair */
+               return cfs_prio_less(a, b, in_fi);
 
        return false;
 }
                return false;
 
        /* flip prio, so high prio is leftmost */
-       if (prio_less(b, a))
+       if (prio_less(b, a, task_rq(a)->core->core_forceidle))
                return true;
 
        return false;
  * - Else returns idle_task.
  */
 static struct task_struct *
-pick_task(struct rq *rq, const struct sched_class *class, struct task_struct *max)
+pick_task(struct rq *rq, const struct sched_class *class, struct task_struct *max, bool in_fi)
 {
        struct task_struct *class_pick, *cookie_pick;
        unsigned long cookie = rq->core->core_cookie;
                 * higher priority than max.
                 */
                if (max && class_pick->core_cookie &&
-                   prio_less(class_pick, max))
+                   prio_less(class_pick, max, in_fi))
                        return idle_sched_class.pick_task(rq);
 
                return class_pick;
         * the core (so far) and it must be selected, otherwise we must go with
         * the cookie pick in order to satisfy the constraint.
         */
-       if (prio_less(cookie_pick, class_pick) &&
-           (!max || prio_less(max, class_pick)))
+       if (prio_less(cookie_pick, class_pick, in_fi) &&
+           (!max || prio_less(max, class_pick, in_fi)))
                return class_pick;
 
        return cookie_pick;
 }
 
+extern void task_vruntime_update(struct rq *rq, struct task_struct *p, bool in_fi);
+
 static struct task_struct *
 pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
 {
        struct task_struct *next, *max = NULL;
        const struct sched_class *class;
        const struct cpumask *smt_mask;
+       bool fi_before = false;
        bool need_sync;
        int i, j, cpu;
 
 
                if (!next->core_cookie) {
                        rq->core_pick = NULL;
+                       /*
+                        * For robustness, update the min_vruntime_fi for
+                        * unconstrained picks as well.
+                        */
+                       WARN_ON_ONCE(fi_before);
+                       task_vruntime_update(rq, next, false);
                        goto done;
                }
-               need_sync = true;
        }
 
        for_each_cpu(i, smt_mask) {
                         * highest priority task already selected for this
                         * core.
                         */
-                       p = pick_task(rq_i, class, max);
+                       p = pick_task(rq_i, class, max, fi_before);
                        if (!p)
                                continue;
 
                        rq_i->core_pick = p;
+                       if (rq_i->idle == p && rq_i->nr_running) {
+                               rq->core->core_forceidle = true;
+                               if (!fi_before)
+                                       rq->core->core_forceidle_seq++;
+                       }
 
                        /*
                         * If this new candidate is of higher priority than the
                                max = p;
 
                                if (old_max) {
+                                       rq->core->core_forceidle = false;
                                        for_each_cpu(j, smt_mask) {
                                                if (j == i)
                                                        continue;
                if (!rq_i->core_pick)
                        continue;
 
-               if (is_task_rq_idle(rq_i->core_pick) && rq_i->nr_running &&
-                   !rq_i->core->core_forceidle) {
-                       rq_i->core->core_forceidle = true;
-               }
+               /*
+                * Update for new !FI->FI transitions, or if continuing to be in !FI:
+                * fi_before     fi      update?
+                *  0            0       1
+                *  0            1       1
+                *  1            0       1
+                *  1            1       0
+                */
+               if (!(fi_before && rq->core->core_forceidle))
+                       task_vruntime_update(rq_i, rq_i->core_pick, rq->core->core_forceidle);
 
                if (i == cpu) {
                        rq_i->core_pick = NULL;
 
            __entity_slice_used(&curr->se, MIN_NR_TASKS_DURING_FORCEIDLE))
                resched_curr(rq);
 }
+
+/*
+ * se_fi_update - Update the cfs_rq->min_vruntime_fi in a CFS hierarchy if needed.
+ */
+static void se_fi_update(struct sched_entity *se, unsigned int fi_seq, bool forceidle)
+{
+       for_each_sched_entity(se) {
+               struct cfs_rq *cfs_rq = cfs_rq_of(se);
+
+               if (forceidle) {
+                       if (cfs_rq->forceidle_seq == fi_seq)
+                               break;
+                       cfs_rq->forceidle_seq = fi_seq;
+               }
+
+               cfs_rq->min_vruntime_fi = cfs_rq->min_vruntime;
+       }
+}
+
+void task_vruntime_update(struct rq *rq, struct task_struct *p, bool in_fi)
+{
+       struct sched_entity *se = &p->se;
+
+       if (p->sched_class != &fair_sched_class)
+               return;
+
+       se_fi_update(se, rq->core->core_forceidle_seq, in_fi);
+}
+
+bool cfs_prio_less(struct task_struct *a, struct task_struct *b, bool in_fi)
+{
+       struct rq *rq = task_rq(a);
+       struct sched_entity *sea = &a->se;
+       struct sched_entity *seb = &b->se;
+       struct cfs_rq *cfs_rqa;
+       struct cfs_rq *cfs_rqb;
+       s64 delta;
+
+       SCHED_WARN_ON(task_rq(b)->core != rq->core);
+
+#ifdef CONFIG_FAIR_GROUP_SCHED
+       /*
+        * Find an se in the hierarchy for tasks a and b, such that the se's
+        * are immediate siblings.
+        */
+       while (sea->cfs_rq->tg != seb->cfs_rq->tg) {
+               int sea_depth = sea->depth;
+               int seb_depth = seb->depth;
+
+               if (sea_depth >= seb_depth)
+                       sea = parent_entity(sea);
+               if (sea_depth <= seb_depth)
+                       seb = parent_entity(seb);
+       }
+
+       se_fi_update(sea, rq->core->core_forceidle_seq, in_fi);
+       se_fi_update(seb, rq->core->core_forceidle_seq, in_fi);
+
+       cfs_rqa = sea->cfs_rq;
+       cfs_rqb = seb->cfs_rq;
+#else
+       cfs_rqa = &task_rq(a)->cfs;
+       cfs_rqb = &task_rq(b)->cfs;
+#endif
+
+       /*
+        * Find delta after normalizing se's vruntime with its cfs_rq's
+        * min_vruntime_fi, which would have been updated in prior calls
+        * to se_fi_update().
+        */
+       delta = (s64)(sea->vruntime - seb->vruntime) +
+               (s64)(cfs_rqb->min_vruntime_fi - cfs_rqa->min_vruntime_fi);
+
+       return delta > 0;
+}
 #else
 static inline void task_tick_core(struct rq *rq, struct task_struct *curr) {}
 #endif