]> git.baikalelectronics.ru Git - kernel.git/commitdiff
sched/fair: Sanitize vruntime of entity being migrated
authorVincent Guittot <vincent.guittot@linaro.org>
Fri, 17 Mar 2023 16:08:10 +0000 (17:08 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Apr 2023 09:16:41 +0000 (11:16 +0200)
commit a53ce18cacb477dd0513c607f187d16f0fa96f71 upstream.

Commit 829c1651e9c4 ("sched/fair: sanitize vruntime of entity being placed")
fixes an overflowing bug, but ignore a case that se->exec_start is reset
after a migration.

For fixing this case, we delay the reset of se->exec_start after
placing the entity which se->exec_start to detect long sleeping task.

In order to take into account a possible divergence between the clock_task
of 2 rqs, we increase the threshold to around 104 days.

Fixes: 829c1651e9c4 ("sched/fair: sanitize vruntime of entity being placed")
Originally-by: Zhang Qiao <zhangqiao22@huawei.com>
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Zhang Qiao <zhangqiao22@huawei.com>
Link: https://lore.kernel.org/r/20230317160810.107988-1-vincent.guittot@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
kernel/sched/core.c
kernel/sched/fair.c

index 8ab239fd1c8d35c198f60b4263545b51ac3020dc..d5765b7c92f791292d9f873b025ca767ec7ba57f 100644 (file)
@@ -1411,6 +1411,9 @@ static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
 
 void activate_task(struct rq *rq, struct task_struct *p, int flags)
 {
+       if (task_on_rq_migrating(p))
+               flags |= ENQUEUE_MIGRATED;
+
        if (task_contributes_to_load(p))
                rq->nr_uninterruptible--;
 
index 97ede6b9144228691fb5233287a197b13a8c0890..9fcba0d2ab19b6b3132c455403988890ae1bf271 100644 (file)
@@ -3877,11 +3877,33 @@ static void check_spread(struct cfs_rq *cfs_rq, struct sched_entity *se)
 #endif
 }
 
+static inline bool entity_is_long_sleeper(struct sched_entity *se)
+{
+       struct cfs_rq *cfs_rq;
+       u64 sleep_time;
+
+       if (se->exec_start == 0)
+               return false;
+
+       cfs_rq = cfs_rq_of(se);
+
+       sleep_time = rq_clock_task(rq_of(cfs_rq));
+
+       /* Happen while migrating because of clock task divergence */
+       if (sleep_time <= se->exec_start)
+               return false;
+
+       sleep_time -= se->exec_start;
+       if (sleep_time > ((1ULL << 63) / scale_load_down(NICE_0_LOAD)))
+               return true;
+
+       return false;
+}
+
 static void
 place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
 {
        u64 vruntime = cfs_rq->min_vruntime;
-       u64 sleep_time;
 
        /*
         * The 'current' period is already promised to the current tasks,
@@ -3908,13 +3930,24 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
 
        /*
         * Pull vruntime of the entity being placed to the base level of
-        * cfs_rq, to prevent boosting it if placed backwards.  If the entity
-        * slept for a long time, don't even try to compare its vruntime with
-        * the base as it may be too far off and the comparison may get
-        * inversed due to s64 overflow.
-        */
-       sleep_time = rq_clock_task(rq_of(cfs_rq)) - se->exec_start;
-       if ((s64)sleep_time > 60LL * NSEC_PER_SEC)
+        * cfs_rq, to prevent boosting it if placed backwards.
+        * However, min_vruntime can advance much faster than real time, with
+        * the extreme being when an entity with the minimal weight always runs
+        * on the cfs_rq. If the waking entity slept for a long time, its
+        * vruntime difference from min_vruntime may overflow s64 and their
+        * comparison may get inversed, so ignore the entity's original
+        * vruntime in that case.
+        * The maximal vruntime speedup is given by the ratio of normal to
+        * minimal weight: scale_load_down(NICE_0_LOAD) / MIN_SHARES.
+        * When placing a migrated waking entity, its exec_start has been set
+        * from a different rq. In order to take into account a possible
+        * divergence between new and prev rq's clocks task because of irq and
+        * stolen time, we take an additional margin.
+        * So, cutting off on the sleep time of
+        *     2^63 / scale_load_down(NICE_0_LOAD) ~ 104 days
+        * should be safe.
+        */
+       if (entity_is_long_sleeper(se))
                se->vruntime = vruntime;
        else
                se->vruntime = max_vruntime(se->vruntime, vruntime);
@@ -4013,6 +4046,9 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
 
        if (flags & ENQUEUE_WAKEUP)
                place_entity(cfs_rq, se, 0);
+       /* Entity has migrated, no longer consider this task hot */
+       if (flags & ENQUEUE_MIGRATED)
+               se->exec_start = 0;
 
        check_schedstat_required();
        update_stats_enqueue(cfs_rq, se, flags);
@@ -6638,9 +6674,6 @@ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu)
        /* Tell new CPU we are migrated */
        p->se.avg.last_update_time = 0;
 
-       /* We have migrated, no longer consider this task hot */
-       p->se.exec_start = 0;
-
        update_scan_period(p, new_cpu);
 }