Skip to content

Commit 8a6edb5

Browse files
Peter Zijlstraingomolnar
authored andcommitted
sched: Fix migration_cpu_stop() requeueing
When affine_move_task(p) is called on a running task @p, which is not otherwise already changing affinity, we'll first set p->migration_pending and then do: stop_one_cpu(cpu_of_rq(rq), migration_cpu_stop, &arg); This then gets us to migration_cpu_stop() running on the CPU that was previously running our victim task @p. If we find that our task is no longer on that runqueue (this can happen because of a concurrent migration due to load-balance etc.), then we'll end up at the: } else if (dest_cpu < 1 || pending) { branch. Which we'll take because we set pending earlier. Here we first check if the task @p has already satisfied the affinity constraints, if so we bail early [A]. Otherwise we'll reissue migration_cpu_stop() onto the CPU that is now hosting our task @p: stop_one_cpu_nowait(cpu_of(rq), migration_cpu_stop, &pending->arg, &pending->stop_work); Except, we've never initialized pending->arg, which will be all 0s. This then results in running migration_cpu_stop() on the next CPU with arg->p == NULL, which gives the by now obvious result of fireworks. The cure is to change affine_move_task() to always use pending->arg, furthermore we can use the exact same pattern as the SCA_MIGRATE_ENABLE case, since we'll block on the pending->done completion anyway, no point in adding yet another completion in stop_one_cpu(). This then gives a clear distinction between the two migration_cpu_stop() use cases: - sched_exec() / migrate_task_to() : arg->pending == NULL - affine_move_task() : arg->pending != NULL; And we can have it ignore p->migration_pending when !arg->pending. Any stop work from sched_exec() / migrate_task_to() is in addition to stop works from affine_move_task(), which will be sufficient to issue the completion. Fixes: 6d337ea ("sched: Fix migrate_disable() vs set_cpus_allowed_ptr()") Cc: stable@kernel.org Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Valentin Schneider <valentin.schneider@arm.com> Link: https://lkml.kernel.org/r/20210224131355.357743989@infradead.org
1 parent a38fd87 commit 8a6edb5

1 file changed

Lines changed: 28 additions & 11 deletions

File tree

kernel/sched/core.c

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,6 +1922,24 @@ static int migration_cpu_stop(void *data)
19221922
rq_lock(rq, &rf);
19231923

19241924
pending = p->migration_pending;
1925+
if (pending && !arg->pending) {
1926+
/*
1927+
* This happens from sched_exec() and migrate_task_to(),
1928+
* neither of them care about pending and just want a task to
1929+
* maybe move about.
1930+
*
1931+
* Even if there is a pending, we can ignore it, since
1932+
* affine_move_task() will have it's own stop_work's in flight
1933+
* which will manage the completion.
1934+
*
1935+
* Notably, pending doesn't need to match arg->pending. This can
1936+
* happen when tripple concurrent affine_move_task() first sets
1937+
* pending, then clears pending and eventually sets another
1938+
* pending.
1939+
*/
1940+
pending = NULL;
1941+
}
1942+
19251943
/*
19261944
* If task_rq(p) != rq, it cannot be migrated here, because we're
19271945
* holding rq->lock, if p->on_rq == 0 it cannot get enqueued because
@@ -2194,10 +2212,6 @@ static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flag
21942212
int dest_cpu, unsigned int flags)
21952213
{
21962214
struct set_affinity_pending my_pending = { }, *pending = NULL;
2197-
struct migration_arg arg = {
2198-
.task = p,
2199-
.dest_cpu = dest_cpu,
2200-
};
22012215
bool complete = false;
22022216

22032217
/* Can the task run on the task's current CPU? If so, we're done */
@@ -2235,6 +2249,12 @@ static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flag
22352249
/* Install the request */
22362250
refcount_set(&my_pending.refs, 1);
22372251
init_completion(&my_pending.done);
2252+
my_pending.arg = (struct migration_arg) {
2253+
.task = p,
2254+
.dest_cpu = -1, /* any */
2255+
.pending = &my_pending,
2256+
};
2257+
22382258
p->migration_pending = &my_pending;
22392259
} else {
22402260
pending = p->migration_pending;
@@ -2265,12 +2285,6 @@ static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flag
22652285
p->migration_flags &= ~MDF_PUSH;
22662286
task_rq_unlock(rq, p, rf);
22672287

2268-
pending->arg = (struct migration_arg) {
2269-
.task = p,
2270-
.dest_cpu = -1,
2271-
.pending = pending,
2272-
};
2273-
22742288
stop_one_cpu_nowait(cpu_of(rq), migration_cpu_stop,
22752289
&pending->arg, &pending->stop_work);
22762290

@@ -2283,8 +2297,11 @@ static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flag
22832297
* is_migration_disabled(p) checks to the stopper, which will
22842298
* run on the same CPU as said p.
22852299
*/
2300+
refcount_inc(&pending->refs); /* pending->{arg,stop_work} */
22862301
task_rq_unlock(rq, p, rf);
2287-
stop_one_cpu(cpu_of(rq), migration_cpu_stop, &arg);
2302+
2303+
stop_one_cpu_nowait(cpu_of(rq), migration_cpu_stop,
2304+
&pending->arg, &pending->stop_work);
22882305

22892306
} else {
22902307

0 commit comments

Comments
 (0)