mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
sched/sugov: Ignore 'busy' filter when rq is capped by uclamp_max
[ Upstream commit7a17e1db12
] sugov_update_single_{freq, perf}() contains a 'busy' filter that ensures we don't bring the frqeuency down if there's no idle time (CPU is busy). The problem is that with uclamp_max we will have scenarios where a busy task is capped to run at a lower frequency and this filter prevents applying the capping when this task starts running. We handle this by skipping the filter when uclamp is enabled and the rq is being capped by uclamp_max. We introduce a new function uclamp_rq_is_capped() to help detecting when this capping is taking effect. Some code shuffling was required to allow using cpu_util_{cfs, rt}() in this new function. On 2 Core SMT2 Intel laptop I see: Without this patch: uclampset -M 0 sysbench --test=cpu --threads = 4 run produces a score of ~3200 consistently. Which is the highest possible. Compiling the kernel also results in frequency running at max 3.1GHz all the time - running uclampset -M 400 to cap it has no effect without this patch. With this patch: uclampset -M 0 sysbench --test=cpu --threads = 4 run produces a score of ~1100 with some outliers in ~1700. Uclamp max aggregates the performance requirements, so having high values sometimes is expected if some other task happens to require that frequency starts running at the same time. When compiling the kernel with uclampset -M 400 I can see the frequencies mostly in the ~2GHz region. Helpful to conserve power and prevent heating when not plugged in. Fixes:982d9cdc22
("sched/cpufreq, sched/uclamp: Add clamps for FAIR and RT tasks") Signed-off-by: Qais Yousef <qais.yousef@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/20211216225320.2957053-2-qais.yousef@arm.com Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
dd282f7aea
commit
d9f51b85bf
2 changed files with 107 additions and 84 deletions
|
@ -348,8 +348,11 @@ static void sugov_update_single_freq(struct update_util_data *hook, u64 time,
|
||||||
/*
|
/*
|
||||||
* Do not reduce the frequency if the CPU has not been idle
|
* Do not reduce the frequency if the CPU has not been idle
|
||||||
* recently, as the reduction is likely to be premature then.
|
* recently, as the reduction is likely to be premature then.
|
||||||
|
*
|
||||||
|
* Except when the rq is capped by uclamp_max.
|
||||||
*/
|
*/
|
||||||
if (sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq) {
|
if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
|
||||||
|
sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq) {
|
||||||
next_f = sg_policy->next_freq;
|
next_f = sg_policy->next_freq;
|
||||||
|
|
||||||
/* Restore cached freq as next_freq has changed */
|
/* Restore cached freq as next_freq has changed */
|
||||||
|
@ -395,8 +398,11 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time,
|
||||||
/*
|
/*
|
||||||
* Do not reduce the target performance level if the CPU has not been
|
* Do not reduce the target performance level if the CPU has not been
|
||||||
* idle recently, as the reduction is likely to be premature then.
|
* idle recently, as the reduction is likely to be premature then.
|
||||||
|
*
|
||||||
|
* Except when the rq is capped by uclamp_max.
|
||||||
*/
|
*/
|
||||||
if (sugov_cpu_is_busy(sg_cpu) && sg_cpu->util < prev_util)
|
if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
|
||||||
|
sugov_cpu_is_busy(sg_cpu) && sg_cpu->util < prev_util)
|
||||||
sg_cpu->util = prev_util;
|
sg_cpu->util = prev_util;
|
||||||
|
|
||||||
cpufreq_driver_adjust_perf(sg_cpu->cpu, map_util_perf(sg_cpu->bw_dl),
|
cpufreq_driver_adjust_perf(sg_cpu->cpu, map_util_perf(sg_cpu->bw_dl),
|
||||||
|
|
|
@ -2841,88 +2841,6 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags)
|
||||||
static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {}
|
static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {}
|
||||||
#endif /* CONFIG_CPU_FREQ */
|
#endif /* CONFIG_CPU_FREQ */
|
||||||
|
|
||||||
#ifdef CONFIG_UCLAMP_TASK
|
|
||||||
unsigned long uclamp_eff_value(struct task_struct *p, enum uclamp_id clamp_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* uclamp_rq_util_with - clamp @util with @rq and @p effective uclamp values.
|
|
||||||
* @rq: The rq to clamp against. Must not be NULL.
|
|
||||||
* @util: The util value to clamp.
|
|
||||||
* @p: The task to clamp against. Can be NULL if you want to clamp
|
|
||||||
* against @rq only.
|
|
||||||
*
|
|
||||||
* Clamps the passed @util to the max(@rq, @p) effective uclamp values.
|
|
||||||
*
|
|
||||||
* If sched_uclamp_used static key is disabled, then just return the util
|
|
||||||
* without any clamping since uclamp aggregation at the rq level in the fast
|
|
||||||
* path is disabled, rendering this operation a NOP.
|
|
||||||
*
|
|
||||||
* Use uclamp_eff_value() if you don't care about uclamp values at rq level. It
|
|
||||||
* will return the correct effective uclamp value of the task even if the
|
|
||||||
* static key is disabled.
|
|
||||||
*/
|
|
||||||
static __always_inline
|
|
||||||
unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util,
|
|
||||||
struct task_struct *p)
|
|
||||||
{
|
|
||||||
unsigned long min_util = 0;
|
|
||||||
unsigned long max_util = 0;
|
|
||||||
|
|
||||||
if (!static_branch_likely(&sched_uclamp_used))
|
|
||||||
return util;
|
|
||||||
|
|
||||||
if (p) {
|
|
||||||
min_util = uclamp_eff_value(p, UCLAMP_MIN);
|
|
||||||
max_util = uclamp_eff_value(p, UCLAMP_MAX);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ignore last runnable task's max clamp, as this task will
|
|
||||||
* reset it. Similarly, no need to read the rq's min clamp.
|
|
||||||
*/
|
|
||||||
if (rq->uclamp_flags & UCLAMP_FLAG_IDLE)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
min_util = max_t(unsigned long, min_util, READ_ONCE(rq->uclamp[UCLAMP_MIN].value));
|
|
||||||
max_util = max_t(unsigned long, max_util, READ_ONCE(rq->uclamp[UCLAMP_MAX].value));
|
|
||||||
out:
|
|
||||||
/*
|
|
||||||
* Since CPU's {min,max}_util clamps are MAX aggregated considering
|
|
||||||
* RUNNABLE tasks with _different_ clamps, we can end up with an
|
|
||||||
* inversion. Fix it now when the clamps are applied.
|
|
||||||
*/
|
|
||||||
if (unlikely(min_util >= max_util))
|
|
||||||
return min_util;
|
|
||||||
|
|
||||||
return clamp(util, min_util, max_util);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When uclamp is compiled in, the aggregation at rq level is 'turned off'
|
|
||||||
* by default in the fast path and only gets turned on once userspace performs
|
|
||||||
* an operation that requires it.
|
|
||||||
*
|
|
||||||
* Returns true if userspace opted-in to use uclamp and aggregation at rq level
|
|
||||||
* hence is active.
|
|
||||||
*/
|
|
||||||
static inline bool uclamp_is_used(void)
|
|
||||||
{
|
|
||||||
return static_branch_likely(&sched_uclamp_used);
|
|
||||||
}
|
|
||||||
#else /* CONFIG_UCLAMP_TASK */
|
|
||||||
static inline
|
|
||||||
unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util,
|
|
||||||
struct task_struct *p)
|
|
||||||
{
|
|
||||||
return util;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool uclamp_is_used(void)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_UCLAMP_TASK */
|
|
||||||
|
|
||||||
#ifdef arch_scale_freq_capacity
|
#ifdef arch_scale_freq_capacity
|
||||||
# ifndef arch_scale_freq_invariant
|
# ifndef arch_scale_freq_invariant
|
||||||
# define arch_scale_freq_invariant() true
|
# define arch_scale_freq_invariant() true
|
||||||
|
@ -3020,6 +2938,105 @@ static inline unsigned long cpu_util_rt(struct rq *rq)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_UCLAMP_TASK
|
||||||
|
unsigned long uclamp_eff_value(struct task_struct *p, enum uclamp_id clamp_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uclamp_rq_util_with - clamp @util with @rq and @p effective uclamp values.
|
||||||
|
* @rq: The rq to clamp against. Must not be NULL.
|
||||||
|
* @util: The util value to clamp.
|
||||||
|
* @p: The task to clamp against. Can be NULL if you want to clamp
|
||||||
|
* against @rq only.
|
||||||
|
*
|
||||||
|
* Clamps the passed @util to the max(@rq, @p) effective uclamp values.
|
||||||
|
*
|
||||||
|
* If sched_uclamp_used static key is disabled, then just return the util
|
||||||
|
* without any clamping since uclamp aggregation at the rq level in the fast
|
||||||
|
* path is disabled, rendering this operation a NOP.
|
||||||
|
*
|
||||||
|
* Use uclamp_eff_value() if you don't care about uclamp values at rq level. It
|
||||||
|
* will return the correct effective uclamp value of the task even if the
|
||||||
|
* static key is disabled.
|
||||||
|
*/
|
||||||
|
static __always_inline
|
||||||
|
unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util,
|
||||||
|
struct task_struct *p)
|
||||||
|
{
|
||||||
|
unsigned long min_util = 0;
|
||||||
|
unsigned long max_util = 0;
|
||||||
|
|
||||||
|
if (!static_branch_likely(&sched_uclamp_used))
|
||||||
|
return util;
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
min_util = uclamp_eff_value(p, UCLAMP_MIN);
|
||||||
|
max_util = uclamp_eff_value(p, UCLAMP_MAX);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore last runnable task's max clamp, as this task will
|
||||||
|
* reset it. Similarly, no need to read the rq's min clamp.
|
||||||
|
*/
|
||||||
|
if (rq->uclamp_flags & UCLAMP_FLAG_IDLE)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
min_util = max_t(unsigned long, min_util, READ_ONCE(rq->uclamp[UCLAMP_MIN].value));
|
||||||
|
max_util = max_t(unsigned long, max_util, READ_ONCE(rq->uclamp[UCLAMP_MAX].value));
|
||||||
|
out:
|
||||||
|
/*
|
||||||
|
* Since CPU's {min,max}_util clamps are MAX aggregated considering
|
||||||
|
* RUNNABLE tasks with _different_ clamps, we can end up with an
|
||||||
|
* inversion. Fix it now when the clamps are applied.
|
||||||
|
*/
|
||||||
|
if (unlikely(min_util >= max_util))
|
||||||
|
return min_util;
|
||||||
|
|
||||||
|
return clamp(util, min_util, max_util);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is the rq being capped/throttled by uclamp_max? */
|
||||||
|
static inline bool uclamp_rq_is_capped(struct rq *rq)
|
||||||
|
{
|
||||||
|
unsigned long rq_util;
|
||||||
|
unsigned long max_util;
|
||||||
|
|
||||||
|
if (!static_branch_likely(&sched_uclamp_used))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rq_util = cpu_util_cfs(cpu_of(rq)) + cpu_util_rt(rq);
|
||||||
|
max_util = READ_ONCE(rq->uclamp[UCLAMP_MAX].value);
|
||||||
|
|
||||||
|
return max_util != SCHED_CAPACITY_SCALE && rq_util >= max_util;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When uclamp is compiled in, the aggregation at rq level is 'turned off'
|
||||||
|
* by default in the fast path and only gets turned on once userspace performs
|
||||||
|
* an operation that requires it.
|
||||||
|
*
|
||||||
|
* Returns true if userspace opted-in to use uclamp and aggregation at rq level
|
||||||
|
* hence is active.
|
||||||
|
*/
|
||||||
|
static inline bool uclamp_is_used(void)
|
||||||
|
{
|
||||||
|
return static_branch_likely(&sched_uclamp_used);
|
||||||
|
}
|
||||||
|
#else /* CONFIG_UCLAMP_TASK */
|
||||||
|
static inline
|
||||||
|
unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util,
|
||||||
|
struct task_struct *p)
|
||||||
|
{
|
||||||
|
return util;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool uclamp_rq_is_capped(struct rq *rq) { return false; }
|
||||||
|
|
||||||
|
static inline bool uclamp_is_used(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_UCLAMP_TASK */
|
||||||
|
|
||||||
#ifdef CONFIG_HAVE_SCHED_AVG_IRQ
|
#ifdef CONFIG_HAVE_SCHED_AVG_IRQ
|
||||||
static inline unsigned long cpu_util_irq(struct rq *rq)
|
static inline unsigned long cpu_util_irq(struct rq *rq)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue