blob: 1a64070948fb0aaff029a57d13bebd3cd375ad1e [file] [log] [blame]
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 */