From: Vineeth Pillai <virem...@linux.microsoft.com>

There are multiple loops in pick_next_task that iterate over CPUs in
smt_mask. During a hotplug event, sibling could be removed from the
smt_mask while pick_next_task is running. So we cannot trust the mask
across the different loops. This can confuse the logic.

Add a retry logic if smt_mask changes between the loops.

Reported-by: Joel Fernandes (Google) <j...@joelfernandes.org>
Signed-off-by: Vineeth Pillai <virem...@linux.microsoft.com>
Signed-off-by: Joel Fernandes (Google) <j...@joelfernandes.org>
---
 kernel/sched/core.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 48a49168e57f..5da5b2317b21 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4613,6 +4613,7 @@ pick_next_task(struct rq *rq, struct task_struct *prev, 
struct rq_flags *rf)
        const struct sched_class *class;
        const struct cpumask *smt_mask;
        int i, j, cpu, occ = 0;
+       int smt_weight;
        bool need_sync;
 
        if (!sched_core_enabled(rq))
@@ -4648,6 +4649,9 @@ pick_next_task(struct rq *rq, struct task_struct *prev, 
struct rq_flags *rf)
        cpu = cpu_of(rq);
        smt_mask = cpu_smt_mask(cpu);
 
+retry_select:
+       smt_weight = cpumask_weight(smt_mask);
+
        /*
         * core->core_task_seq, rq->core_pick_seq, rq->core_sched_seq
         *
@@ -4691,6 +4695,14 @@ pick_next_task(struct rq *rq, struct task_struct *prev, 
struct rq_flags *rf)
                        struct rq *rq_i = cpu_rq(i);
                        struct task_struct *p;
 
+                       /*
+                        * During hotplug online a sibling can be added in
+                        * the smt_mask * while we are here. If so, we would
+                        * need to restart selection by resetting all over.
+                        */
+                       if (unlikely(smt_weight != cpumask_weight(smt_mask)))
+                               goto retry_select;
+
                        if (rq_i->core_pick)
                                continue;
 
@@ -4790,7 +4802,15 @@ next_class:;
        for_each_cpu_or(i, smt_mask, cpumask_of(cpu)) {
                struct rq *rq_i = cpu_rq(i);
 
-               WARN_ON_ONCE(!rq_i->core_pick);
+               WARN_ON_ONCE(smt_weight == cpumask_weight(smt_mask) && 
!rq->core_pick);
+
+               /*
+                * During hotplug online a sibling can be added in the smt_mask
+                * while we are here. We might have missed picking a task for 
it.
+                * Ignore it now as a schedule on that sibling will correct 
itself.
+                */
+               if (!rq_i->core_pick)
+                       continue;
 
                if (is_idle_task(rq_i->core_pick) && rq_i->nr_running)
                        rq_i->core_forceidle = true;
-- 
2.28.0.220.ged08abb693-goog

Reply via email to