Re: [PATCH v3] sched/fair: remove dead code on enqueue_task_fair()

From: Christian Loehle

Date: Tue Oct 13 2026 - 17:58:42 EST


On 10/13/26 12:58, Peter Zijlstra wrote:
> On Mon, Oct 12, 2026 at 03:59:40PM +0100, Christian Loehle wrote:
>> On 10/12/26 15:02, Peter Zijlstra wrote:
>
>>> Anyway, this is different from the initial use-case, where a
>>> sched_entity would get dequeue and re-enqueued while walking the cgroup
>>> hierarchy, and thus had to appear like nothing happened in between.
>>>
>>> In this case we're fine to advance time.
>>>
>>> Anyway... the normal block/wakeup paths are >< close to also hitting
>>> this in the case where balancing drops the rq->lock after dequeue. The
>>> only reason ttwu can't tickle this is because it has that ->on_cpu
>>> stuff. However, we cannot use that on the unthrottle path :/
>>>
>>> Bah.
>>>
>>> Does this work?
>>>
>>> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>>> index 5c98d8dfce5d..0edd8bfb6363 100644
>>> --- a/kernel/sched/fair.c
>>> +++ b/kernel/sched/fair.c
>>> @@ -8250,7 +8250,14 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
>>> weight = enqueue_hierarchy(p, flags);
>>> reweight_eevdf(cfs_rq, se, weight, false);
>>> place_entity(cfs_rq, se, flags | ENQUEUE_QUEUED);
>>> - __enqueue_entity(cfs_rq, se);
>>> +
>>> + /*
>>> + * This can actually happen with CFS_BANDWIDTH, in that case a task
>>> + * can get throttled, dequeued and then unthrottled, woken *before* it
>>> + * schedules out and does put_prev_task().
>>> + */
>>> + if (cfs_rq->curr != se)
>>> + __enqueue_entity(cfs_rq, se);
>>>
>>> if (!rq_h_nr_queued && rq->cfs.h_nr_queued)
>>> dl_server_start(&rq->fair_server);
>>
>> Tested-by: Christian Loehle <christian.loehle@xxxxxxx>
>> for my reproducer, that being said, and indeed the failure is:
>> throttle -> dequeue (not put_prev_task()'d yet) -> (from remote CPU) unthrottle -> enqueue()
>>
>> The dequeue()->enqueue() also lets place_entity()
>> 'double-account' the entity's (on dequeue() saved) lag, right?
>> After dequeue: curr == se, on_rq == 0 -> excl from average
>> enqueue_hierarchy(): curr == se, on_rq == 1 -> incl again
>> place_entity(): calculate placement assuming se is excl
>
> I'm not entirely sure I follow. With the above fix there is still only
> one place_entity(), which is after enqueue_hierarchy() and has
> ENQUEUE_QUEUED on.

Exactly.

>
> Here enqueue_hierarchy() adds the entity to h_nr_queued, and
> ENQUEUE_QUEUED tells place_entity() exactly this, so it can adjust.

... but place_entity(ENQUEUE_QUEUED) still does:

load = cfs_rq->sum_weight;
if (curr && curr->on_rq) // <-- this is true after enqueue_hierarchy()
load += avg_vruntime_weight(cfs_rq, curr->h_load.weight);

weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); // <- this assumes se isn't already part of cfs_rq weight, but it is (still)
lag *= load + weight;
...

(in this concrete enqueue()->dequeue() due to unthrottling case)