[Devel] [PATCH vz9] sched: Do not set LBF_NEED_BREAK flag if scanned all the tasks
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Mon Dec 18 09:39:59 MSK 2023
On 16/12/2023 01:21, Konstantin Khorenko wrote:
> On 15.12.2023 12:19, Pavel Tikhomirov wrote:
>>
>>
>> On 14/12/2023 06:01, Konstantin Khorenko wrote:
>>> After ms commit b0defa7ae03e ("sched/fair: Make sure to try to detach at
>>> least one movable task") detach_tasks() does not stop on the condition
>>> (env->loop > env->loop_max) in case no movable task found.
>>>
>>> Instead of that (if there are no movable tasks in the rq) exits always
>>> happen on the loop_break check - thus with LBF_NEED_BREAK flag set.
>>>
>>> It's not a problem for mainstream because load_balance() proceeds with
>>> balancing in case LBF_NEED_BREAK is set only in case (env.loop <
>>> busiest->nr_running), but in Virtuozzo kernel with CFS_CPULIMIT feature
>>> right before that we try to move a whole task group (object of the
>>> CFS_CPULIMIT feature) and resets env.loop to zero,
>>> so we get a livelock here.
>>>
>>> Resetting env.loop makes sense in case we have successfully moved some
>>> tasks (in the scope of the task group), but if we failed to move any
>>> task, no progress is expected during further balancing attempts.
>>>
>>> Ways to fix it:
>>> 1. In load_balance() restore old env.loop in case no tasks were moved
>>> by move_task_groups()
>>> 2. Add a check in detach_tasks() to exit without LBF_NEED_BREAK
>>> flag in
>>> case we have scanned all tasks and have not found movable tasks.
>>>
>>> Current patch implements the second way.
>>>
>>> Caused by ms commit: b0defa7ae03e ("sched/fair: Make sure to try to
>>> detach at least one movable task")
>>>
>>> Fixes ms commit: bca010328248 ("sched: Port CONFIG_CFS_CPULIMIT
>>> feature")
>>
>> this is not "ms" commit
>>
>>>
Reviewed-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
>>> Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
>>>
>>> Feature: sched: ability to limit number of CPUs available to a CT
>>> ---
>>> kernel/sched/fair.c | 6 ++++++
>>> 1 file changed, 6 insertions(+)
>>>
>>> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>>> index 9367d16a8d85..e068bb90f197 100644
>>> --- a/kernel/sched/fair.c
>>> +++ b/kernel/sched/fair.c
>>> @@ -8771,6 +8771,12 @@ static int detach_tasks(struct lb_env *env)
>>> if (env->loop > env->loop_max &&
>>> !(env->flags & LBF_ALL_PINNED))
>>> break;
>>> + /*
>>> + * Quit if we have scanned all tasks even in case we haven't
>>> + * found any movable task.
>>> + */
>>> + if (env->loop > env->src_rq->nr_running)
>>> + break;
>>
>> The env->loop is clearly designed in load_balance function to be reused
>> on next iteration of "goto more_balance". The fact that we clear it to
>> zero in our code is really strange... Maybe we can have separate
>> env.vzloop variable for our part of the code (or we can properly
>> integrate with env.loop without zeroing it?)?
>
> Well, in fact load_balance() contains 2 other places where env.loop is
> reset to 0 as well and the execution is sent to "more_balance" after that.
Agree.
>
> In general i understand the idea of resetting env.loop in our case as well:
> we have moved the task group (our feature) and after that we would like to
> balance the load again - so resetting loop counter looks logic.
> The other question is that we reset env.loop even in case we have not
> really
> moved any task group and thus restarting the balancing process is
> incorrect.
>
> i thought about checking the result of move_task_groups() and if it's zero,
> just restore the env.loop counter to the value before moving the task
> group attempt.
> May be it is worth to do, but i just decided to resurrect the previous
> behavior -
> thus do not set LBF_NEED_BREAK flag if we reach the end of the run queue.
>
>
>> Do we also need same crazy LBF_ALL_PINNED change to move_task_groups's
>> "if (env->loop > env->loop_max) break;" check? Similar to what
>> b0defa7ae03ec ("sched/fair: Make sure to try to detach at least one
>> movable task") does in detach_tasks to allow it to handle the case then
>> lru tail is pinned?
>
> And again, i thought about it.
> move_task_groups() increases env.loop only in case it founds a task to
> move,
> so in case some tasks are pinned, env.loop is not increased and we will
> go further.
Agree.
>
> So in move_task_groups() we exit either
> 1. we scanned all groups (and did not find enough tasks to move to fix
> the imbalance)
> 2. we scanned some groups and found/moved tasks > env.loop_max
> 3. we scanned some groups, found/moved some tasks and their number was
> enough to fix the imbalance
Seems reasonable. Thanks for explanation!
>
>>> /* take a breather every nr_migrate tasks */
>>> if (env->loop > env->loop_break) {
>>
--
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
More information about the Devel
mailing list