| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: John Dias <joaodias@google.com> |
| Date: Thu, 15 Sep 2016 08:52:27 -0700 |
| Subject: NOUPSTREAM: ANDROID: sched: avoid placing RT threads on cores |
| handling softirqs |
| |
| In certain audio use cases, scheduling RT threads on cores that are |
| handling softirqs can lead to glitches. Prevent this behavior. |
| |
| [CPNOTE: 30/06/21] Lee: Still required, but potentially use PREEMPT_RT in Android to solve (big job) |
| |
| Bug: 31501544 |
| Bug: 168521633 |
| Change-Id: I99dd7aaa12c11270b28dbabea484bcc8fb8ba0c1 |
| Signed-off-by: John Dias <joaodias@google.com> |
| [elavila: Port to mainline, amend commit text] |
| Signed-off-by: J. Avila <elavila@google.com> |
| --- |
| init/Kconfig | 11 +++++++++++ |
| kernel/sched/cpupri.c | 45 +++++++++++++++++++++++++++++++++++++++++-- |
| kernel/sched/rt.c | 37 +++++++++++++++++++++++++++++------ |
| kernel/sched/sched.h | 11 +++++++++++ |
| 4 files changed, 96 insertions(+), 8 deletions(-) |
| |
| diff --git a/init/Kconfig b/init/Kconfig |
| --- a/init/Kconfig |
| +++ b/init/Kconfig |
| @@ -1274,6 +1274,17 @@ config SCHED_AUTOGROUP |
| desktop applications. Task group autogeneration is currently based |
| upon task session. |
| |
| +config RT_SOFTINT_OPTIMIZATION |
| + bool "Improve RT scheduling during long softint execution" |
| + depends on ARM64 |
| + depends on SMP |
| + default n |
| + help |
| + Enable an optimization which tries to avoid placing RT tasks on CPUs |
| + occupied by nonpreemptible tasks, such as a long softint, or CPUs |
| + which may soon block preemptions, such as a CPU running a ksoftirq |
| + thread which handles slow softints. |
| + |
| config SYSFS_DEPRECATED |
| bool "Enable deprecated sysfs features to support old userspace tools" |
| depends on SYSFS |
| diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c |
| --- a/kernel/sched/cpupri.c |
| +++ b/kernel/sched/cpupri.c |
| @@ -65,8 +65,29 @@ static int convert_prio(int prio) |
| return cpupri; |
| } |
| |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| +/** |
| + * drop_nopreempt_cpus - remove likely nonpreemptible cpus from the mask |
| + * @lowest_mask: mask with selected CPUs (non-NULL) |
| + */ |
| +static void |
| +drop_nopreempt_cpus(struct cpumask *lowest_mask) |
| +{ |
| + unsigned int cpu = cpumask_first(lowest_mask); |
| + while (cpu < nr_cpu_ids) { |
| + /* unlocked access */ |
| + struct task_struct *task = READ_ONCE(cpu_rq(cpu)->curr); |
| + if (task_may_not_preempt(task, cpu)) { |
| + cpumask_clear_cpu(cpu, lowest_mask); |
| + } |
| + cpu = cpumask_next(cpu, lowest_mask); |
| + } |
| +} |
| +#endif |
| + |
| static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p, |
| - struct cpumask *lowest_mask, int idx) |
| + struct cpumask *lowest_mask, int idx, |
| + bool drop_nopreempts) |
| { |
| struct cpupri_vec *vec = &cp->pri_to_cpu[idx]; |
| int skip = 0; |
| @@ -103,6 +124,11 @@ static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p, |
| if (lowest_mask) { |
| cpumask_and(lowest_mask, &p->cpus_mask, vec->mask); |
| |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| + if (drop_nopreempts) |
| + drop_nopreempt_cpus(lowest_mask); |
| +#endif |
| + |
| /* |
| * We have to ensure that we have at least one bit |
| * still set in the array, since the map could have |
| @@ -147,12 +173,16 @@ int cpupri_find_fitness(struct cpupri *cp, struct task_struct *p, |
| { |
| int task_pri = convert_prio(p->prio); |
| int idx, cpu; |
| + bool drop_nopreempts = task_pri <= MAX_RT_PRIO; |
| |
| BUG_ON(task_pri >= CPUPRI_NR_PRIORITIES); |
| |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| +retry: |
| +#endif |
| for (idx = 0; idx < task_pri; idx++) { |
| |
| - if (!__cpupri_find(cp, p, lowest_mask, idx)) |
| + if (!__cpupri_find(cp, p, lowest_mask, idx, drop_nopreempts)) |
| continue; |
| |
| if (!lowest_mask || !fitness_fn) |
| @@ -174,6 +204,17 @@ int cpupri_find_fitness(struct cpupri *cp, struct task_struct *p, |
| return 1; |
| } |
| |
| + /* |
| + * If we can't find any non-preemptible cpu's, retry so we can |
| + * find the lowest priority target and avoid priority inversion. |
| + */ |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| + if (drop_nopreempts) { |
| + drop_nopreempts = false; |
| + goto retry; |
| + } |
| +#endif |
| + |
| /* |
| * If we failed to find a fitting lowest_mask, kick off a new search |
| * but without taking into account any fitness criteria this time. |
| diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c |
| --- a/kernel/sched/rt.c |
| +++ b/kernel/sched/rt.c |
| @@ -1550,12 +1550,28 @@ static void yield_task_rt(struct rq *rq) |
| #ifdef CONFIG_SMP |
| static int find_lowest_rq(struct task_struct *task); |
| |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| +/* |
| + * Return whether the task on the given cpu is currently non-preemptible |
| + * while handling a softirq or is likely to block preemptions soon because |
| + * it is a ksoftirq thread. |
| + */ |
| +bool |
| +task_may_not_preempt(struct task_struct *task, int cpu) |
| +{ |
| + struct task_struct *cpu_ksoftirqd = per_cpu(ksoftirqd, cpu); |
| + return (task_thread_info(task)->preempt_count & SOFTIRQ_MASK) || |
| + task == cpu_ksoftirqd; |
| +} |
| +#endif /* CONFIG_RT_SOFTINT_OPTIMIZATION */ |
| + |
| static int |
| select_task_rq_rt(struct task_struct *p, int cpu, int flags) |
| { |
| struct task_struct *curr; |
| struct rq *rq; |
| bool test; |
| + bool may_not_preempt; |
| |
| /* For anything but wake ups, just return the task_cpu */ |
| if (!(flags & (WF_TTWU | WF_FORK))) |
| @@ -1567,7 +1583,12 @@ select_task_rq_rt(struct task_struct *p, int cpu, int flags) |
| curr = READ_ONCE(rq->curr); /* unlocked access */ |
| |
| /* |
| - * If the current task on @p's runqueue is an RT task, then |
| + * If the current task on @p's runqueue is a softirq task, |
| + * it may run without preemption for a time that is |
| + * ill-suited for a waiting RT task. Therefore, try to |
| + * wake this RT task on another runqueue. |
| + * |
| + * Also, if the current task on @p's runqueue is an RT task, then |
| * try to see if we can wake this RT task up on another |
| * runqueue. Otherwise simply start this RT task |
| * on its current runqueue. |
| @@ -1592,9 +1613,10 @@ select_task_rq_rt(struct task_struct *p, int cpu, int flags) |
| * requirement of the task - which is only important on heterogeneous |
| * systems like big.LITTLE. |
| */ |
| - test = curr && |
| - unlikely(rt_task(curr)) && |
| - (curr->nr_cpus_allowed < 2 || curr->prio <= p->prio); |
| + may_not_preempt = task_may_not_preempt(curr, cpu); |
| + test = (curr && (may_not_preempt || |
| + (unlikely(rt_task(curr)) && |
| + (curr->nr_cpus_allowed < 2 || curr->prio <= p->prio)))); |
| |
| if (test || !rt_task_fits_capacity(p, cpu)) { |
| int target = find_lowest_rq(p); |
| @@ -1607,11 +1629,14 @@ select_task_rq_rt(struct task_struct *p, int cpu, int flags) |
| goto out_unlock; |
| |
| /* |
| - * Don't bother moving it if the destination CPU is |
| + * If cpu is non-preemptible, prefer remote cpu |
| + * even if it's running a higher-prio task. |
| + * Otherwise: Don't bother moving it if the destination CPU is |
| * not running a lower priority task. |
| */ |
| if (target != -1 && |
| - p->prio < cpu_rq(target)->rt.highest_prio.curr) |
| + (may_not_preempt || |
| + p->prio < cpu_rq(target)->rt.highest_prio.curr)) |
| cpu = target; |
| } |
| |
| diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h |
| --- a/kernel/sched/sched.h |
| +++ b/kernel/sched/sched.h |
| @@ -3092,3 +3092,14 @@ extern int sched_dynamic_mode(const char *str); |
| extern void sched_dynamic_update(int mode); |
| #endif |
| |
| +/* |
| + * task_may_not_preempt - check whether a task may not be preemptible soon |
| + */ |
| +#ifdef CONFIG_RT_SOFTINT_OPTIMIZATION |
| +extern bool task_may_not_preempt(struct task_struct *task, int cpu); |
| +#else |
| +static inline bool task_may_not_preempt(struct task_struct *task, int cpu) |
| +{ |
| + return false; |
| +} |
| +#endif /* CONFIG_RT_SOFTINT_OPTIMIZATION */ |