@@ -3666,41 +3666,140 @@ static inline void
3666
3666
dequeue_load_avg (struct cfs_rq * cfs_rq , struct sched_entity * se ) { }
3667
3667
#endif
3668
3668
3669
+ static void reweight_eevdf (struct cfs_rq * cfs_rq , struct sched_entity * se ,
3670
+ unsigned long weight )
3671
+ {
3672
+ unsigned long old_weight = se -> load .weight ;
3673
+ u64 avruntime = avg_vruntime (cfs_rq );
3674
+ s64 vlag , vslice ;
3675
+
3676
+ /*
3677
+ * VRUNTIME
3678
+ * ========
3679
+ *
3680
+ * COROLLARY #1: The virtual runtime of the entity needs to be
3681
+ * adjusted if re-weight at !0-lag point.
3682
+ *
3683
+ * Proof: For contradiction assume this is not true, so we can
3684
+ * re-weight without changing vruntime at !0-lag point.
3685
+ *
3686
+ * Weight VRuntime Avg-VRuntime
3687
+ * before w v V
3688
+ * after w' v' V'
3689
+ *
3690
+ * Since lag needs to be preserved through re-weight:
3691
+ *
3692
+ * lag = (V - v)*w = (V'- v')*w', where v = v'
3693
+ * ==> V' = (V - v)*w/w' + v (1)
3694
+ *
3695
+ * Let W be the total weight of the entities before reweight,
3696
+ * since V' is the new weighted average of entities:
3697
+ *
3698
+ * V' = (WV + w'v - wv) / (W + w' - w) (2)
3699
+ *
3700
+ * by using (1) & (2) we obtain:
3701
+ *
3702
+ * (WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v
3703
+ * ==> (WV-Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v
3704
+ * ==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v
3705
+ * ==> (V - v)*W/(W + w' - w) = (V - v)*w/w' (3)
3706
+ *
3707
+ * Since we are doing at !0-lag point which means V != v, we
3708
+ * can simplify (3):
3709
+ *
3710
+ * ==> W / (W + w' - w) = w / w'
3711
+ * ==> Ww' = Ww + ww' - ww
3712
+ * ==> W * (w' - w) = w * (w' - w)
3713
+ * ==> W = w (re-weight indicates w' != w)
3714
+ *
3715
+ * So the cfs_rq contains only one entity, hence vruntime of
3716
+ * the entity @v should always equal to the cfs_rq's weighted
3717
+ * average vruntime @V, which means we will always re-weight
3718
+ * at 0-lag point, thus breach assumption. Proof completed.
3719
+ *
3720
+ *
3721
+ * COROLLARY #2: Re-weight does NOT affect weighted average
3722
+ * vruntime of all the entities.
3723
+ *
3724
+ * Proof: According to corollary #1, Eq. (1) should be:
3725
+ *
3726
+ * (V - v)*w = (V' - v')*w'
3727
+ * ==> v' = V' - (V - v)*w/w' (4)
3728
+ *
3729
+ * According to the weighted average formula, we have:
3730
+ *
3731
+ * V' = (WV - wv + w'v') / (W - w + w')
3732
+ * = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w')
3733
+ * = (WV - wv + w'V' - Vw + wv) / (W - w + w')
3734
+ * = (WV + w'V' - Vw) / (W - w + w')
3735
+ *
3736
+ * ==> V'*(W - w + w') = WV + w'V' - Vw
3737
+ * ==> V' * (W - w) = (W - w) * V (5)
3738
+ *
3739
+ * If the entity is the only one in the cfs_rq, then reweight
3740
+ * always occurs at 0-lag point, so V won't change. Or else
3741
+ * there are other entities, hence W != w, then Eq. (5) turns
3742
+ * into V' = V. So V won't change in either case, proof done.
3743
+ *
3744
+ *
3745
+ * So according to corollary #1 & #2, the effect of re-weight
3746
+ * on vruntime should be:
3747
+ *
3748
+ * v' = V' - (V - v) * w / w' (4)
3749
+ * = V - (V - v) * w / w'
3750
+ * = V - vl * w / w'
3751
+ * = V - vl'
3752
+ */
3753
+ if (avruntime != se -> vruntime ) {
3754
+ vlag = (s64 )(avruntime - se -> vruntime );
3755
+ vlag = div_s64 (vlag * old_weight , weight );
3756
+ se -> vruntime = avruntime - vlag ;
3757
+ }
3758
+
3759
+ /*
3760
+ * DEADLINE
3761
+ * ========
3762
+ *
3763
+ * When the weight changes, the virtual time slope changes and
3764
+ * we should adjust the relative virtual deadline accordingly.
3765
+ *
3766
+ * d' = v' + (d - v)*w/w'
3767
+ * = V' - (V - v)*w/w' + (d - v)*w/w'
3768
+ * = V - (V - v)*w/w' + (d - v)*w/w'
3769
+ * = V + (d - V)*w/w'
3770
+ */
3771
+ vslice = (s64 )(se -> deadline - avruntime );
3772
+ vslice = div_s64 (vslice * old_weight , weight );
3773
+ se -> deadline = avruntime + vslice ;
3774
+ }
3775
+
3669
3776
static void reweight_entity (struct cfs_rq * cfs_rq , struct sched_entity * se ,
3670
3777
unsigned long weight )
3671
3778
{
3672
- unsigned long old_weight = se -> load . weight ;
3779
+ bool curr = cfs_rq -> curr == se ;
3673
3780
3674
3781
if (se -> on_rq ) {
3675
3782
/* commit outstanding execution time */
3676
- if (cfs_rq -> curr == se )
3783
+ if (curr )
3677
3784
update_curr (cfs_rq );
3678
3785
else
3679
- avg_vruntime_sub (cfs_rq , se );
3786
+ __dequeue_entity (cfs_rq , se );
3680
3787
update_load_sub (& cfs_rq -> load , se -> load .weight );
3681
3788
}
3682
3789
dequeue_load_avg (cfs_rq , se );
3683
3790
3684
- update_load_set (& se -> load , weight );
3685
-
3686
3791
if (!se -> on_rq ) {
3687
3792
/*
3688
3793
* Because we keep se->vlag = V - v_i, while: lag_i = w_i*(V - v_i),
3689
3794
* we need to scale se->vlag when w_i changes.
3690
3795
*/
3691
- se -> vlag = div_s64 (se -> vlag * old_weight , weight );
3796
+ se -> vlag = div_s64 (se -> vlag * se -> load . weight , weight );
3692
3797
} else {
3693
- s64 deadline = se -> deadline - se -> vruntime ;
3694
- /*
3695
- * When the weight changes, the virtual time slope changes and
3696
- * we should adjust the relative virtual deadline accordingly.
3697
- */
3698
- deadline = div_s64 (deadline * old_weight , weight );
3699
- se -> deadline = se -> vruntime + deadline ;
3700
- if (se != cfs_rq -> curr )
3701
- min_deadline_cb_propagate (& se -> run_node , NULL );
3798
+ reweight_eevdf (cfs_rq , se , weight );
3702
3799
}
3703
3800
3801
+ update_load_set (& se -> load , weight );
3802
+
3704
3803
#ifdef CONFIG_SMP
3705
3804
do {
3706
3805
u32 divider = get_pelt_divider (& se -> avg );
@@ -3712,8 +3811,17 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
3712
3811
enqueue_load_avg (cfs_rq , se );
3713
3812
if (se -> on_rq ) {
3714
3813
update_load_add (& cfs_rq -> load , se -> load .weight );
3715
- if (cfs_rq -> curr != se )
3716
- avg_vruntime_add (cfs_rq , se );
3814
+ if (!curr ) {
3815
+ /*
3816
+ * The entity's vruntime has been adjusted, so let's check
3817
+ * whether the rq-wide min_vruntime needs updated too. Since
3818
+ * the calculations above require stable min_vruntime rather
3819
+ * than up-to-date one, we do the update at the end of the
3820
+ * reweight process.
3821
+ */
3822
+ __enqueue_entity (cfs_rq , se );
3823
+ update_min_vruntime (cfs_rq );
3824
+ }
3717
3825
}
3718
3826
}
3719
3827
@@ -3857,14 +3965,11 @@ static void update_cfs_group(struct sched_entity *se)
3857
3965
3858
3966
#ifndef CONFIG_SMP
3859
3967
shares = READ_ONCE (gcfs_rq -> tg -> shares );
3860
-
3861
- if (likely (se -> load .weight == shares ))
3862
- return ;
3863
3968
#else
3864
- shares = calc_group_shares (gcfs_rq );
3969
+ shares = calc_group_shares (gcfs_rq );
3865
3970
#endif
3866
-
3867
- reweight_entity (cfs_rq_of (se ), se , shares );
3971
+ if ( unlikely ( se -> load . weight != shares ))
3972
+ reweight_entity (cfs_rq_of (se ), se , shares );
3868
3973
}
3869
3974
3870
3975
#else /* CONFIG_FAIR_GROUP_SCHED */
0 commit comments