Blob Blame History Raw
From 0e8d22ad8010838573d07b529c15a944af641785 Mon Sep 17 00:00:00 2001
From: John Ogness <john.ogness@linutronix.de>
Date: Fri, 4 Feb 2022 16:01:17 +0106
Subject: [PATCH] printk: avoid preempt_disable() for PREEMPT_RT

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

During non-normal operation, printk() calls will attempt to
write the messages directly to the consoles. This involves
using console_trylock() to acquire @console_sem.

Preemption is disabled while directly printing to the consoles
in order to ensure that the printing task is not scheduled away
while holding @console_sem, thus blocking all other printers
and causing delays in printing.

Commit fd5f7cde1b85 ("printk: Never set console_may_schedule in
console_trylock()") specifically reverted a previous attempt at
allowing preemption while printing.

However, on PREEMPT_RT systems, disabling preemption while
printing is not allowed because console drivers typically
acquire a spin lock (which under PREEMPT_RT is an rtmutex).
Since direct printing is only used during early boot and
non-panic dumps, the risks of delayed print output for these
scenarios will be accepted under PREEMPT_RT.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Mel Gorman <mgorman@suse.de>
---
 kernel/printk/printk.c |   31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -277,10 +277,12 @@ static void __up_console_sem(unsigned lo
 }
 #define up_console_sem() __up_console_sem(_RET_IP_)
 
+#ifndef CONFIG_PREEMPT_RT
 static bool panic_in_progress(void)
 {
 	return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
 }
+#endif
 
 /*
  * Tracks whether kthread printers are all paused. A value of true implies
@@ -1919,6 +1921,7 @@ static int console_lock_spinning_disable
 	return 1;
 }
 
+#if !IS_ENABLED(CONFIG_PREEMPT_RT)
 /**
  * console_trylock_spinning - try to get console_lock by busy waiting
  *
@@ -1992,6 +1995,7 @@ static int console_trylock_spinning(void
 
 	return 1;
 }
+#endif /* CONFIG_PREEMPT_RT */
 
 /*
  * Call the specified console driver, asking it to write out the specified
@@ -2325,19 +2329,31 @@ asmlinkage int vprintk_emit(int facility
 	/* If called from the scheduler, we can not call up(). */
 	if (!in_sched && allow_direct_printing()) {
 		/*
+		 * Try to acquire and then immediately release the console
+		 * semaphore.  The release will print out buffers.
+		 */
+#if IS_ENABLED(CONFIG_PREEMPT_RT)
+		/*
+		 * Use the non-spinning trylock since PREEMPT_RT does not
+		 * support console lock handovers.
+		 *
+		 * Direct printing will most likely involve taking spinlocks.
+		 * For PREEMPT_RT, this is only allowed if in a preemptible
+		 * context.
+		 */
+		if (preemptible() && console_trylock())
+			console_unlock();
+#else
+		/*
 		 * Disable preemption to avoid being preempted while holding
 		 * console_sem which would prevent anyone from printing to
 		 * console
 		 */
 		preempt_disable();
-		/*
-		 * Try to acquire and then immediately release the console
-		 * semaphore.  The release will print out buffers and wake up
-		 * /dev/kmsg and syslog() users.
-		 */
 		if (console_trylock_spinning())
 			console_unlock();
 		preempt_enable();
+#endif
 	}
 
 	wake_up_klogd();
@@ -2965,8 +2981,13 @@ static bool console_emit_next_record(str
 		len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time);
 	}
 
+#if IS_ENABLED(CONFIG_PREEMPT_RT)
+	/* PREEMPT_RT does not support console lock handovers. */
+	allow_handover = false;
+#else
 	/* Handovers may only happen between trylock contexts. */
 	allow_handover = (handover && atomic_read(&console_lock_count) == -1);
+#endif
 
 	if (allow_handover) {
 		/*