Blob Blame History Raw
From feb99f13e43cf03cce030227c9fa74b96bc0a77b Mon Sep 17 00:00:00 2001
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Mon, 27 Sep 2021 09:38:14 +0200
Subject: [PATCH] smp: Wake ksoftirqd on PREEMPT_RT instead do_softirq().

References: SLE Realtime Extension
Patch-mainline: Queued in subsystem maintainer repository
Git-commit: 4fb621b286e220fa36eb8fe6bf11cf7dd4801694
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git

The softirq implementation on PREEMPT_RT does not provide do_softirq(). The
softirq can not be handled directly here because migration_cpu_stop() is
invoked with disabled preemption/ interrupts.

A known user of scheduling softirqs from a remote function call is the block
layer. It won't happen on PREEMPT_RT because it doesn't make sense for
latency/ performance reasons and is disabled. Nevertheless this should
be handled in case of a new user pops up rather than simply ignoring it.

Waking ksoftirqd unconditionally can be problematic if softirqs were already
pending but not yet handled. This can happen since the migration thread
is running at a high priority and able to preempt a threaded-interrupt.
The woken-up ksoftirqd would catch-up all pending (and later raised)
softirqs which is not desired on PREEMPT_RT since it is no longer
handled where it has been originally raised. This in turn delays the
actual processing until a SCHED_OTHER task can run.

Wake the softirq thread on PREEMPT_RT if a remote function call raised
softirqs. Add warning in this case since this condition is not desired.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://lkml.kernel.org/r/YgKgL6aPj8aBES6G@linutronix.de
Signed-off-by: Mel Gorman <mgorman@suse.de>
---
 kernel/smp.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/kernel/smp.c b/kernel/smp.c
index d1f2ef1386ca..0ce89c4c248f 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -694,10 +694,25 @@ void flush_smp_call_function_from_idle(void)
 	cfd_seq_store(this_cpu_ptr(&cfd_seq_local)->idle, CFD_SEQ_NOCPU,
 		      smp_processor_id(), CFD_SEQ_IDLE);
 	local_irq_save(flags);
-	flush_smp_call_function_queue(true);
-	if (local_softirq_pending())
-		do_softirq();
+	if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+		flush_smp_call_function_queue(true);
+		if (local_softirq_pending())
+			do_softirq();
+	} else {
+		unsigned int pending_prev;
+		unsigned int pending_post;
+
+		pending_prev = local_softirq_pending();
+		flush_smp_call_function_queue(true);
+		pending_post = local_softirq_pending();
 
+		if (WARN_ON_ONCE(!pending_prev && pending_post)) {
+			struct task_struct *ksoftirqd = this_cpu_ksoftirqd();
+
+			if (ksoftirqd && !task_is_running(ksoftirqd))
+				wake_up_process(ksoftirqd);
+		}
+	}
 	local_irq_restore(flags);
 }