Commit d93cef31 authored by Xunlei Pang's avatar Xunlei Pang Committed by Greg Kroah-Hartman

sched/fair: Fix bandwidth timer clock drift condition

commit 512ac999 upstream.

I noticed that cgroup task groups constantly get throttled even
if they have low CPU usage, this causes some jitters on the response
time to some of our business containers when enabling CPU quotas.

It's very simple to reproduce:

  mkdir /sys/fs/cgroup/cpu/test
  cd /sys/fs/cgroup/cpu/test
  echo 100000 > cpu.cfs_quota_us
  echo $$ > tasks

then repeat:

  cat cpu.stat | grep nr_throttled  # nr_throttled will increase steadily

After some analysis, we found that cfs_rq::runtime_remaining will
be cleared by expire_cfs_rq_runtime() due to two equal but stale
"cfs_{b|q}->runtime_expires" after period timer is re-armed.

The current condition to judge clock drift in expire_cfs_rq_runtime()
is wrong, the two runtime_expires are actually the same when clock
drift happens, so this condtion can never hit. The orginal design was
correctly done by this commit:

  a9cf55b2 ("sched: Expire invalid runtime")

... but was changed to be the current implementation due to its locking bug.

This patch introduces another way, it adds a new field in both structures
cfs_rq and cfs_bandwidth to record the expiration update sequence, and
uses them to figure out if clock drift happens (true if they are equal).
Signed-off-by: default avatarXunlei Pang <xlpang@linux.alibaba.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
[alakeshh: backport: Fixed merge conflicts:
 - sched.h: Fix the indentation and order in which the variables are
   declared to match with coding style of the existing code in 4.14
   Struct members of same type were declared in separate lines in
   upstream patch which has been changed back to having multiple
   members of same type in the same line.
   e.g. int a; int b; ->  int a, b; ]
Signed-off-by: default avatarAlakesh Haloi <alakeshh@amazon.com>
Reviewed-by: default avatarBen Segall <bsegall@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: <stable@vger.kernel.org> # 4.14.x
Fixes: 51f2176d ("sched/fair: Fix unlocked reads of some cfs_b->quota/period")
Link: http://lkml.kernel.org/r/20180620101834.24455-1-xlpang@linux.alibaba.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8e643473
...@@ -4087,6 +4087,7 @@ void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b) ...@@ -4087,6 +4087,7 @@ void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b)
now = sched_clock_cpu(smp_processor_id()); now = sched_clock_cpu(smp_processor_id());
cfs_b->runtime = cfs_b->quota; cfs_b->runtime = cfs_b->quota;
cfs_b->runtime_expires = now + ktime_to_ns(cfs_b->period); cfs_b->runtime_expires = now + ktime_to_ns(cfs_b->period);
cfs_b->expires_seq++;
} }
static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg) static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg)
...@@ -4109,6 +4110,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq) ...@@ -4109,6 +4110,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
struct task_group *tg = cfs_rq->tg; struct task_group *tg = cfs_rq->tg;
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(tg); struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(tg);
u64 amount = 0, min_amount, expires; u64 amount = 0, min_amount, expires;
int expires_seq;
/* note: this is a positive sum as runtime_remaining <= 0 */ /* note: this is a positive sum as runtime_remaining <= 0 */
min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining; min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining;
...@@ -4125,6 +4127,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq) ...@@ -4125,6 +4127,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
cfs_b->idle = 0; cfs_b->idle = 0;
} }
} }
expires_seq = cfs_b->expires_seq;
expires = cfs_b->runtime_expires; expires = cfs_b->runtime_expires;
raw_spin_unlock(&cfs_b->lock); raw_spin_unlock(&cfs_b->lock);
...@@ -4134,8 +4137,10 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq) ...@@ -4134,8 +4137,10 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
* spread between our sched_clock and the one on which runtime was * spread between our sched_clock and the one on which runtime was
* issued. * issued.
*/ */
if ((s64)(expires - cfs_rq->runtime_expires) > 0) if (cfs_rq->expires_seq != expires_seq) {
cfs_rq->expires_seq = expires_seq;
cfs_rq->runtime_expires = expires; cfs_rq->runtime_expires = expires;
}
return cfs_rq->runtime_remaining > 0; return cfs_rq->runtime_remaining > 0;
} }
...@@ -4161,12 +4166,9 @@ static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq) ...@@ -4161,12 +4166,9 @@ static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq)
* has not truly expired. * has not truly expired.
* *
* Fortunately we can check determine whether this the case by checking * Fortunately we can check determine whether this the case by checking
* whether the global deadline has advanced. It is valid to compare * whether the global deadline(cfs_b->expires_seq) has advanced.
* cfs_b->runtime_expires without any locks since we only care about
* exact equality, so a partial write will still work.
*/ */
if (cfs_rq->expires_seq == cfs_b->expires_seq) {
if (cfs_rq->runtime_expires != cfs_b->runtime_expires) {
/* extend local deadline, drift is bounded above by 2 ticks */ /* extend local deadline, drift is bounded above by 2 ticks */
cfs_rq->runtime_expires += TICK_NSEC; cfs_rq->runtime_expires += TICK_NSEC;
} else { } else {
......
...@@ -281,8 +281,9 @@ struct cfs_bandwidth { ...@@ -281,8 +281,9 @@ struct cfs_bandwidth {
u64 quota, runtime; u64 quota, runtime;
s64 hierarchical_quota; s64 hierarchical_quota;
u64 runtime_expires; u64 runtime_expires;
int expires_seq;
int idle, period_active; short idle, period_active;
struct hrtimer period_timer, slack_timer; struct hrtimer period_timer, slack_timer;
struct list_head throttled_cfs_rq; struct list_head throttled_cfs_rq;
...@@ -488,6 +489,7 @@ struct cfs_rq { ...@@ -488,6 +489,7 @@ struct cfs_rq {
#ifdef CONFIG_CFS_BANDWIDTH #ifdef CONFIG_CFS_BANDWIDTH
int runtime_enabled; int runtime_enabled;
int expires_seq;
u64 runtime_expires; u64 runtime_expires;
s64 runtime_remaining; s64 runtime_remaining;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment