Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1353918 > unrolled thread
| Started by | Pavan Kondeti <pkondeti@codeaurora.org> |
|---|---|
| First post | 2016-03-09 10:30 +0100 |
| Last post | 2016-03-10 00:50 +0100 |
| Articles | 6 — 5 participants |
Back to article view | Back to linux.kernel
Migrated CFS task getting an unfair advantage Pavan Kondeti <pkondeti@codeaurora.org> - 2016-03-09 10:30 +0100
Re: Migrated CFS task getting an unfair advantage Peter Zijlstra <peterz@infradead.org> - 2016-03-09 13:10 +0100
Re: Migrated CFS task getting an unfair advantage pavankumar kondeti <pavankumar.kondeti@gmail.com> - 2016-03-09 14:10 +0100
Re: Migrated CFS task getting an unfair advantage Andrew Hunter <ahh@google.com> - 2016-03-09 20:10 +0100
Re: Migrated CFS task getting an unfair advantage Peter Zijlstra <peterz@infradead.org> - 2016-03-09 20:30 +0100
Re: Migrated CFS task getting an unfair advantage Byungchul Park <byungchul.park@lge.com> - 2016-03-10 00:50 +0100
| From | Pavan Kondeti <pkondeti@codeaurora.org> |
|---|---|
| Date | 2016-03-09 10:30 +0100 |
| Subject | Migrated CFS task getting an unfair advantage |
| Message-ID | <raIBk-5QY-3@gated-at.bofh.it> |
Hi When a CFS task is enqueued during migration (load balance or change in affinity), its vruntime is normalized before updating the current and cfs_rq->min_vruntime. If the current entity is a low priority task or belongs to a cgroup that has lower cpu.shares and it is the only entity queued, there is a possibility of big update to the cfs_rq->min_vruntime. As the migrated task is normalized before this update, it gets an unfair advantage over tasks queued after this point. If the migrated task is a CPU hogger, the other CFS tasks queued on this CPU gets starved. This problem can be simulated by running the below workload: - create NR_CPU low prio CFS tasks affined to each CPU and put them under a cgroup with cpu.shares = 52 (1024/20). These tasks run forever. - create another CFS task which runs forever and periodically hops across all CPUs by changing the affinity. - We could see the destination CPU's cfs_rq->min_vruntime falls behind the migrated task by few hundreds of msec after migration. If we add the migrated task to destination CPU cfs_rq's rb tree before updating the current in enqueue_entity(), the cfs_rq->min_vruntime does not go beyond the newly migrated task. Is this an acceptable solution? Thanks, Pavan -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
[toc] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-03-09 13:10 +0100 |
| Message-ID | <raL6b-7IU-21@gated-at.bofh.it> |
| In reply to | #1353918 |
On Wed, Mar 09, 2016 at 02:52:57PM +0530, Pavan Kondeti wrote:
> When a CFS task is enqueued during migration (load balance or change in
> affinity), its vruntime is normalized before updating the current and
> cfs_rq->min_vruntime.
static void
enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
{
/*
* Update the normalized vruntime before updating min_vruntime
* through calling update_curr().
*/
if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
se->vruntime += cfs_rq->min_vruntime;
update_curr(cfs_rq);
This, right? Some idiot wrote a comment but forgot to explain why.
> If the current entity is a low priority task or belongs to a cgroup
> that has lower cpu.shares and it is the only entity queued, there is a
> possibility of big update to the cfs_rq->min_vruntime.
> As the migrated task is normalized before this update, it gets an
> unfair advantage over tasks queued after this point. If the migrated
> task is a CPU hogger, the other CFS tasks queued on this CPU gets
> starved.
Because it takes a whole while for the newly placed task to gain on the
previous task, right?
> If we add the migrated task to destination CPU cfs_rq's rb tree before
> updating the current in enqueue_entity(), the cfs_rq->min_vruntime
> does not go beyond the newly migrated task. Is this an acceptable
> solution?
Hurm.. so I'm not sure how that would solve anything. The existing task
would still be shot far into the future.
What you want is to normalize after update_curr()... but we cannot do
that in the case cfs_rq->curr == se (which I suppose is what that
comment is on about).
Does something like the below work?
---
kernel/sched/fair.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 33130529e9b5..3c114d971d84 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3157,17 +3157,25 @@ static inline void check_schedstat_required(void)
static void
enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
{
+ bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING);
+ bool curr = cfs_rq->curr == se;
+
/*
- * Update the normalized vruntime before updating min_vruntime
- * through calling update_curr().
+ * If we're the current task, we must renormalise before calling
+ * update_curr().
*/
- if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
+ if (renorm && curr)
se->vruntime += cfs_rq->min_vruntime;
+ update_curr(cfs_rq);
+
/*
- * Update run-time statistics of the 'current'.
+ * Otherwise, renormalise after, such that we're placed at the current
+ * moment in time, instead of some random moment in the past.
*/
- update_curr(cfs_rq);
+ if (renorm && !curr)
+ se->vruntime += cfs_rq->min_vruntime;
+
enqueue_entity_load_avg(cfs_rq, se);
account_entity_enqueue(cfs_rq, se);
update_cfs_shares(cfs_rq);
@@ -3183,7 +3191,7 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
update_stats_enqueue(cfs_rq, se);
check_spread(cfs_rq, se);
}
- if (se != cfs_rq->curr)
+ if (!curr)
__enqueue_entity(cfs_rq, se);
se->on_rq = 1;
[toc] | [prev] | [next] | [standalone]
| From | pavankumar kondeti <pavankumar.kondeti@gmail.com> |
|---|---|
| Date | 2016-03-09 14:10 +0100 |
| Message-ID | <raM2f-8kD-21@gated-at.bofh.it> |
| In reply to | #1354062 |
Hi Peter,
On Wed, Mar 9, 2016 at 5:34 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Wed, Mar 09, 2016 at 02:52:57PM +0530, Pavan Kondeti wrote:
>
>> When a CFS task is enqueued during migration (load balance or change in
>> affinity), its vruntime is normalized before updating the current and
>> cfs_rq->min_vruntime.
>
> static void
> enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
> {
> /*
> * Update the normalized vruntime before updating min_vruntime
> * through calling update_curr().
> */
> if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
> se->vruntime += cfs_rq->min_vruntime;
>
> update_curr(cfs_rq);
>
> This, right? Some idiot wrote a comment but forgot to explain why.
>
>> If the current entity is a low priority task or belongs to a cgroup
>> that has lower cpu.shares and it is the only entity queued, there is a
>> possibility of big update to the cfs_rq->min_vruntime.
>
>> As the migrated task is normalized before this update, it gets an
>> unfair advantage over tasks queued after this point. If the migrated
>> task is a CPU hogger, the other CFS tasks queued on this CPU gets
>> starved.
>
> Because it takes a whole while for the newly placed task to gain on the
> previous task, right?
>
Yes. The newly woken up task vruntime is adjusted wrt the cfs_rq->min_vruntime.
The cfs_rq->min_vruntime can potentially be hundreds of msec beyond the
migrated task.
>> If we add the migrated task to destination CPU cfs_rq's rb tree before
>> updating the current in enqueue_entity(), the cfs_rq->min_vruntime
>> does not go beyond the newly migrated task. Is this an acceptable
>> solution?
>
> Hurm.. so I'm not sure how that would solve anything. The existing task
> would still be shot far into the future.
>
In my testing, the problem is gone with this approach.
The update_min_vruntime() called from update_curr() has a check to make
sure that cfs_rq->min_vruntime does not go beyond the leftmost entity
(in this case it would be the migrated task) vruntime. so we don't see
the migrated task getting any advantage.
> What you want is to normalize after update_curr()... but we cannot do
> that in the case cfs_rq->curr == se (which I suppose is what that
> comment is on about).
>
> Does something like the below work?
>
Thanks for providing this patch. It solved the problem.
> ---
> kernel/sched/fair.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 33130529e9b5..3c114d971d84 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -3157,17 +3157,25 @@ static inline void check_schedstat_required(void)
> static void
> enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
> {
> + bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING);
> + bool curr = cfs_rq->curr == se;
> +
> /*
> - * Update the normalized vruntime before updating min_vruntime
> - * through calling update_curr().
> + * If we're the current task, we must renormalise before calling
> + * update_curr().
> */
> - if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
> + if (renorm && curr)
> se->vruntime += cfs_rq->min_vruntime;
>
> + update_curr(cfs_rq);
> +
> /*
> - * Update run-time statistics of the 'current'.
> + * Otherwise, renormalise after, such that we're placed at the current
> + * moment in time, instead of some random moment in the past.
> */
> - update_curr(cfs_rq);
> + if (renorm && !curr)
> + se->vruntime += cfs_rq->min_vruntime;
> +
> enqueue_entity_load_avg(cfs_rq, se);
> account_entity_enqueue(cfs_rq, se);
> update_cfs_shares(cfs_rq);
> @@ -3183,7 +3191,7 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
> update_stats_enqueue(cfs_rq, se);
> check_spread(cfs_rq, se);
> }
> - if (se != cfs_rq->curr)
> + if (!curr)
> __enqueue_entity(cfs_rq, se);
> se->on_rq = 1;
>
[toc] | [prev] | [next] | [standalone]
| From | Andrew Hunter <ahh@google.com> |
|---|---|
| Date | 2016-03-09 20:10 +0100 |
| Message-ID | <raREB-3Sz-13@gated-at.bofh.it> |
| In reply to | #1354098 |
At Google, we essentially reverted 88ec22d and the subsequent tweaks
to it, keeping vruntime absolute always , instead using
task_move_group_fair to change the basis of relative min_vruntime
between cpus. We found this made it a lot easier to reason about and
work with corss-cpu computations. I could post the patch if it would
be of interest...
On Wed, Mar 9, 2016 at 5:06 AM, pavankumar kondeti
<pavankumar.kondeti@gmail.com> wrote:
> Hi Peter,
>
> On Wed, Mar 9, 2016 at 5:34 PM, Peter Zijlstra <peterz@infradead.org> wrote:
>> On Wed, Mar 09, 2016 at 02:52:57PM +0530, Pavan Kondeti wrote:
>>
>>> When a CFS task is enqueued during migration (load balance or change in
>>> affinity), its vruntime is normalized before updating the current and
>>> cfs_rq->min_vruntime.
>>
>> static void
>> enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
>> {
>> /*
>> * Update the normalized vruntime before updating min_vruntime
>> * through calling update_curr().
>> */
>> if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
>> se->vruntime += cfs_rq->min_vruntime;
>>
>> update_curr(cfs_rq);
>>
>> This, right? Some idiot wrote a comment but forgot to explain why.
>>
>>> If the current entity is a low priority task or belongs to a cgroup
>>> that has lower cpu.shares and it is the only entity queued, there is a
>>> possibility of big update to the cfs_rq->min_vruntime.
>>
>>> As the migrated task is normalized before this update, it gets an
>>> unfair advantage over tasks queued after this point. If the migrated
>>> task is a CPU hogger, the other CFS tasks queued on this CPU gets
>>> starved.
>>
>> Because it takes a whole while for the newly placed task to gain on the
>> previous task, right?
>>
>
> Yes. The newly woken up task vruntime is adjusted wrt the cfs_rq->min_vruntime.
> The cfs_rq->min_vruntime can potentially be hundreds of msec beyond the
> migrated task.
>
>>> If we add the migrated task to destination CPU cfs_rq's rb tree before
>>> updating the current in enqueue_entity(), the cfs_rq->min_vruntime
>>> does not go beyond the newly migrated task. Is this an acceptable
>>> solution?
>>
>> Hurm.. so I'm not sure how that would solve anything. The existing task
>> would still be shot far into the future.
>>
> In my testing, the problem is gone with this approach.
>
> The update_min_vruntime() called from update_curr() has a check to make
> sure that cfs_rq->min_vruntime does not go beyond the leftmost entity
> (in this case it would be the migrated task) vruntime. so we don't see
> the migrated task getting any advantage.
>
>> What you want is to normalize after update_curr()... but we cannot do
>> that in the case cfs_rq->curr == se (which I suppose is what that
>> comment is on about).
>>
>> Does something like the below work?
>>
>
> Thanks for providing this patch. It solved the problem.
>
>> ---
>> kernel/sched/fair.c | 20 ++++++++++++++------
>> 1 file changed, 14 insertions(+), 6 deletions(-)
>>
>> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>> index 33130529e9b5..3c114d971d84 100644
>> --- a/kernel/sched/fair.c
>> +++ b/kernel/sched/fair.c
>> @@ -3157,17 +3157,25 @@ static inline void check_schedstat_required(void)
>> static void
>> enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
>> {
>> + bool renorm = !(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING);
>> + bool curr = cfs_rq->curr == se;
>> +
>> /*
>> - * Update the normalized vruntime before updating min_vruntime
>> - * through calling update_curr().
>> + * If we're the current task, we must renormalise before calling
>> + * update_curr().
>> */
>> - if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
>> + if (renorm && curr)
>> se->vruntime += cfs_rq->min_vruntime;
>>
>> + update_curr(cfs_rq);
>> +
>> /*
>> - * Update run-time statistics of the 'current'.
>> + * Otherwise, renormalise after, such that we're placed at the current
>> + * moment in time, instead of some random moment in the past.
>> */
>> - update_curr(cfs_rq);
>> + if (renorm && !curr)
>> + se->vruntime += cfs_rq->min_vruntime;
>> +
>> enqueue_entity_load_avg(cfs_rq, se);
>> account_entity_enqueue(cfs_rq, se);
>> update_cfs_shares(cfs_rq);
>> @@ -3183,7 +3191,7 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
>> update_stats_enqueue(cfs_rq, se);
>> check_spread(cfs_rq, se);
>> }
>> - if (se != cfs_rq->curr)
>> + if (!curr)
>> __enqueue_entity(cfs_rq, se);
>> se->on_rq = 1;
>>
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-03-09 20:30 +0100 |
| Message-ID | <raRXY-3Zs-11@gated-at.bofh.it> |
| In reply to | #1354363 |
On Wed, Mar 09, 2016 at 11:00:42AM -0800, Andrew Hunter wrote: > At Google, we essentially reverted 88ec22d and the subsequent tweaks > to it, keeping vruntime absolute always , instead using > task_move_group_fair to change the basis of relative min_vruntime > between cpus. We found this made it a lot easier to reason about and > work with corss-cpu computations. I could post the patch if it would > be of interest... Yes please, Paul said he would many times.
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-03-10 00:50 +0100 |
| Message-ID | <raW1C-716-57@gated-at.bofh.it> |
| In reply to | #1354363 |
On Wed, Mar 09, 2016 at 11:00:42AM -0800, Andrew Hunter wrote: > At Google, we essentially reverted 88ec22d and the subsequent tweaks > to it, keeping vruntime absolute always , instead using > task_move_group_fair to change the basis of relative min_vruntime Hello, Andrew I am curious about how it can be done with absolute value. :-) > between cpus. We found this made it a lot easier to reason about and > work with corss-cpu computations. I could post the patch if it would > be of interest... I am interested in it. Thanks, Byungchul >
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web