diff options
| author | Peter Zijlstra <peterz@infradead.org> | 2019-01-07 07:52:31 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2019-01-27 06:29:37 -0500 |
| commit | c0ad4aa4d8416a39ad262a2bd68b30acd951bf0e (patch) | |
| tree | ef0c001f3783a68146915eb3fff67d08f800b5b7 /kernel/sched | |
| parent | f8a696f25ba09a1821dc6ca3db56f41c264fb896 (diff) | |
sched/fair: Robustify CFS-bandwidth timer locking
Traditionally hrtimer callbacks were run with IRQs disabled, but with
the introduction of HRTIMER_MODE_SOFT it is possible they run from
SoftIRQ context, which does _NOT_ have IRQs disabled.
Allow for the CFS bandwidth timers (period_timer and slack_timer) to
be ran from SoftIRQ context; this entails removing the assumption that
IRQs are already disabled from the locking.
While mainline doesn't strictly need this, -RT forces all timers not
explicitly marked with MODE_HARD into MODE_SOFT and trips over this.
And marking these timers as MODE_HARD doesn't make sense as they're
not required for RT operation and can potentially be quite expensive.
Reported-by: Tom Putzeys <tom.putzeys@be.atlascopco.com>
Tested-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20190107125231.GE14122@hirez.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/sched')
| -rw-r--r-- | kernel/sched/fair.c | 30 |
1 files changed, 16 insertions, 14 deletions
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b1374fbddd0d..3b61e19b504a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c | |||
| @@ -4565,7 +4565,7 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, | |||
| 4565 | struct rq *rq = rq_of(cfs_rq); | 4565 | struct rq *rq = rq_of(cfs_rq); |
| 4566 | struct rq_flags rf; | 4566 | struct rq_flags rf; |
| 4567 | 4567 | ||
| 4568 | rq_lock(rq, &rf); | 4568 | rq_lock_irqsave(rq, &rf); |
| 4569 | if (!cfs_rq_throttled(cfs_rq)) | 4569 | if (!cfs_rq_throttled(cfs_rq)) |
| 4570 | goto next; | 4570 | goto next; |
| 4571 | 4571 | ||
| @@ -4582,7 +4582,7 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, | |||
| 4582 | unthrottle_cfs_rq(cfs_rq); | 4582 | unthrottle_cfs_rq(cfs_rq); |
| 4583 | 4583 | ||
| 4584 | next: | 4584 | next: |
| 4585 | rq_unlock(rq, &rf); | 4585 | rq_unlock_irqrestore(rq, &rf); |
| 4586 | 4586 | ||
| 4587 | if (!remaining) | 4587 | if (!remaining) |
| 4588 | break; | 4588 | break; |
| @@ -4598,7 +4598,7 @@ next: | |||
| 4598 | * period the timer is deactivated until scheduling resumes; cfs_b->idle is | 4598 | * period the timer is deactivated until scheduling resumes; cfs_b->idle is |
| 4599 | * used to track this state. | 4599 | * used to track this state. |
| 4600 | */ | 4600 | */ |
| 4601 | static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun) | 4601 | static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, unsigned long flags) |
| 4602 | { | 4602 | { |
| 4603 | u64 runtime, runtime_expires; | 4603 | u64 runtime, runtime_expires; |
| 4604 | int throttled; | 4604 | int throttled; |
| @@ -4640,11 +4640,11 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun) | |||
| 4640 | while (throttled && cfs_b->runtime > 0 && !cfs_b->distribute_running) { | 4640 | while (throttled && cfs_b->runtime > 0 && !cfs_b->distribute_running) { |
| 4641 | runtime = cfs_b->runtime; | 4641 | runtime = cfs_b->runtime; |
| 4642 | cfs_b->distribute_running = 1; | 4642 | cfs_b->distribute_running = 1; |
| 4643 | raw_spin_unlock(&cfs_b->lock); | 4643 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4644 | /* we can't nest cfs_b->lock while distributing bandwidth */ | 4644 | /* we can't nest cfs_b->lock while distributing bandwidth */ |
| 4645 | runtime = distribute_cfs_runtime(cfs_b, runtime, | 4645 | runtime = distribute_cfs_runtime(cfs_b, runtime, |
| 4646 | runtime_expires); | 4646 | runtime_expires); |
| 4647 | raw_spin_lock(&cfs_b->lock); | 4647 | raw_spin_lock_irqsave(&cfs_b->lock, flags); |
| 4648 | 4648 | ||
| 4649 | cfs_b->distribute_running = 0; | 4649 | cfs_b->distribute_running = 0; |
| 4650 | throttled = !list_empty(&cfs_b->throttled_cfs_rq); | 4650 | throttled = !list_empty(&cfs_b->throttled_cfs_rq); |
| @@ -4753,17 +4753,18 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq) | |||
| 4753 | static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b) | 4753 | static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b) |
| 4754 | { | 4754 | { |
| 4755 | u64 runtime = 0, slice = sched_cfs_bandwidth_slice(); | 4755 | u64 runtime = 0, slice = sched_cfs_bandwidth_slice(); |
| 4756 | unsigned long flags; | ||
| 4756 | u64 expires; | 4757 | u64 expires; |
| 4757 | 4758 | ||
| 4758 | /* confirm we're still not at a refresh boundary */ | 4759 | /* confirm we're still not at a refresh boundary */ |
| 4759 | raw_spin_lock(&cfs_b->lock); | 4760 | raw_spin_lock_irqsave(&cfs_b->lock, flags); |
| 4760 | if (cfs_b->distribute_running) { | 4761 | if (cfs_b->distribute_running) { |
| 4761 | raw_spin_unlock(&cfs_b->lock); | 4762 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4762 | return; | 4763 | return; |
| 4763 | } | 4764 | } |
| 4764 | 4765 | ||
| 4765 | if (runtime_refresh_within(cfs_b, min_bandwidth_expiration)) { | 4766 | if (runtime_refresh_within(cfs_b, min_bandwidth_expiration)) { |
| 4766 | raw_spin_unlock(&cfs_b->lock); | 4767 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4767 | return; | 4768 | return; |
| 4768 | } | 4769 | } |
| 4769 | 4770 | ||
| @@ -4774,18 +4775,18 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b) | |||
| 4774 | if (runtime) | 4775 | if (runtime) |
| 4775 | cfs_b->distribute_running = 1; | 4776 | cfs_b->distribute_running = 1; |
| 4776 | 4777 | ||
| 4777 | raw_spin_unlock(&cfs_b->lock); | 4778 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4778 | 4779 | ||
| 4779 | if (!runtime) | 4780 | if (!runtime) |
| 4780 | return; | 4781 | return; |
| 4781 | 4782 | ||
| 4782 | runtime = distribute_cfs_runtime(cfs_b, runtime, expires); | 4783 | runtime = distribute_cfs_runtime(cfs_b, runtime, expires); |
| 4783 | 4784 | ||
| 4784 | raw_spin_lock(&cfs_b->lock); | 4785 | raw_spin_lock_irqsave(&cfs_b->lock, flags); |
| 4785 | if (expires == cfs_b->runtime_expires) | 4786 | if (expires == cfs_b->runtime_expires) |
| 4786 | lsub_positive(&cfs_b->runtime, runtime); | 4787 | lsub_positive(&cfs_b->runtime, runtime); |
| 4787 | cfs_b->distribute_running = 0; | 4788 | cfs_b->distribute_running = 0; |
| 4788 | raw_spin_unlock(&cfs_b->lock); | 4789 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4789 | } | 4790 | } |
| 4790 | 4791 | ||
| 4791 | /* | 4792 | /* |
| @@ -4863,20 +4864,21 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer) | |||
| 4863 | { | 4864 | { |
| 4864 | struct cfs_bandwidth *cfs_b = | 4865 | struct cfs_bandwidth *cfs_b = |
| 4865 | container_of(timer, struct cfs_bandwidth, period_timer); | 4866 | container_of(timer, struct cfs_bandwidth, period_timer); |
| 4867 | unsigned long flags; | ||
| 4866 | int overrun; | 4868 | int overrun; |
| 4867 | int idle = 0; | 4869 | int idle = 0; |
| 4868 | 4870 | ||
| 4869 | raw_spin_lock(&cfs_b->lock); | 4871 | raw_spin_lock_irqsave(&cfs_b->lock, flags); |
| 4870 | for (;;) { | 4872 | for (;;) { |
| 4871 | overrun = hrtimer_forward_now(timer, cfs_b->period); | 4873 | overrun = hrtimer_forward_now(timer, cfs_b->period); |
| 4872 | if (!overrun) | 4874 | if (!overrun) |
| 4873 | break; | 4875 | break; |
| 4874 | 4876 | ||
| 4875 | idle = do_sched_cfs_period_timer(cfs_b, overrun); | 4877 | idle = do_sched_cfs_period_timer(cfs_b, overrun, flags); |
| 4876 | } | 4878 | } |
| 4877 | if (idle) | 4879 | if (idle) |
| 4878 | cfs_b->period_active = 0; | 4880 | cfs_b->period_active = 0; |
| 4879 | raw_spin_unlock(&cfs_b->lock); | 4881 | raw_spin_unlock_irqrestore(&cfs_b->lock, flags); |
| 4880 | 4882 | ||
| 4881 | return idle ? HRTIMER_NORESTART : HRTIMER_RESTART; | 4883 | return idle ? HRTIMER_NORESTART : HRTIMER_RESTART; |
| 4882 | } | 4884 | } |
