summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch')
-rw-r--r--debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch207
1 files changed, 207 insertions, 0 deletions
diff --git a/debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch b/debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch
new file mode 100644
index 000000000..8156ae1d1
--- /dev/null
+++ b/debian/patches-rt/0025-printk-Avoid-console_lock-dance-if-no-legacy-or-boot.patch
@@ -0,0 +1,207 @@
+From: John Ogness <john.ogness@linutronix.de>
+Date: Tue, 26 Sep 2023 12:44:07 +0000
+Subject: [PATCH 25/48] printk: Avoid console_lock dance if no legacy or boot
+ consoles
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.8/older/patches-6.8.2-rt11.tar.xz
+
+Currently the console lock is used to attempt legacy-type
+printing even if there are no legacy or boot consoles registered.
+If no such consoles are registered, the console lock does not
+need to be taken.
+
+Add tracking of legacy console registration and use it with
+boot console tracking to avoid unnecessary code paths, i.e.
+do not use the console lock if there are no boot consoles
+and no legacy consoles.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ kernel/printk/printk.c | 79 +++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 64 insertions(+), 15 deletions(-)
+
+--- a/kernel/printk/printk.c
++++ b/kernel/printk/printk.c
+@@ -464,6 +464,13 @@ static int console_msg_format = MSG_FORM
+ static DEFINE_MUTEX(syslog_lock);
+
+ /*
++ * Specifies if a legacy console is registered. If legacy consoles are
++ * present, it is necessary to perform the console lock/unlock dance
++ * whenever console flushing should occur.
++ */
++static bool have_legacy_console;
++
++/*
+ * Specifies if a boot console is registered. If boot consoles are present,
+ * nbcon consoles cannot print simultaneously and must be synchronized by
+ * the console lock. This is because boot consoles and nbcon consoles may
+@@ -471,6 +478,14 @@ static DEFINE_MUTEX(syslog_lock);
+ */
+ static bool have_boot_console;
+
++/*
++ * Specifies if the console lock/unlock dance is needed for console
++ * printing. If @have_boot_console is true, the nbcon consoles will
++ * be printed serially along with the legacy consoles because nbcon
++ * consoles cannot print simultaneously with boot consoles.
++ */
++#define printing_via_unlock (have_legacy_console || have_boot_console)
++
+ #ifdef CONFIG_PRINTK
+ DECLARE_WAIT_QUEUE_HEAD(log_wait);
+ /* All 3 protected by @syslog_lock. */
+@@ -2344,7 +2359,7 @@ asmlinkage int vprintk_emit(int facility
+ printed_len = vprintk_store(facility, level, dev_info, fmt, args);
+
+ /* If called from the scheduler, we can not call up(). */
+- if (!in_sched) {
++ if (!in_sched && printing_via_unlock) {
+ /*
+ * The caller may be holding system-critical or
+ * timing-sensitive locks. Disable preemption during
+@@ -2653,7 +2668,7 @@ void resume_console(void)
+ */
+ static int console_cpu_notify(unsigned int cpu)
+ {
+- if (!cpuhp_tasks_frozen) {
++ if (!cpuhp_tasks_frozen && printing_via_unlock) {
+ /* If trylock fails, someone else is doing the printing */
+ if (console_trylock())
+ console_unlock();
+@@ -3194,7 +3209,8 @@ void console_flush_on_panic(enum con_flu
+
+ nbcon_atomic_flush_pending();
+
+- console_flush_all(false, &next_seq, &handover);
++ if (printing_via_unlock)
++ console_flush_all(false, &next_seq, &handover);
+ }
+
+ /*
+@@ -3531,6 +3547,8 @@ void register_console(struct console *ne
+ */
+ nbcon_seq_force(newcon, newcon->seq);
+ newcon->seq = 0;
++ } else {
++ have_legacy_console = true;
+ }
+
+ if (newcon->flags & CON_BOOT)
+@@ -3605,6 +3623,7 @@ EXPORT_SYMBOL(register_console);
+ /* Must be called under console_list_lock(). */
+ static int unregister_console_locked(struct console *console)
+ {
++ bool found_legacy_con = false;
+ bool found_boot_con = false;
+ struct console *c;
+ int res;
+@@ -3661,9 +3680,13 @@ static int unregister_console_locked(str
+ for_each_console(c) {
+ if (c->flags & CON_BOOT)
+ found_boot_con = true;
++ if (!(c->flags & CON_NBCON))
++ found_legacy_con = true;
+ }
+ if (!found_boot_con)
+ have_boot_console = found_boot_con;
++ if (!found_legacy_con)
++ have_legacy_console = found_legacy_con;
+
+ return res;
+ }
+@@ -3824,22 +3847,34 @@ static bool __pr_flush(struct console *c
+ seq = prb_next_reserve_seq(prb);
+
+ /* Flush the consoles so that records up to @seq are printed. */
+- console_lock();
+- console_unlock();
++ if (printing_via_unlock) {
++ console_lock();
++ console_unlock();
++ }
+
+ for (;;) {
+ unsigned long begin_jiffies;
+ unsigned long slept_jiffies;
+-
+- diff = 0;
++ bool use_console_lock = printing_via_unlock;
+
+ /*
+- * Hold the console_lock to guarantee safe access to
+- * console->seq. Releasing console_lock flushes more
+- * records in case @seq is still not printed on all
+- * usable consoles.
++ * Ensure the compiler does not optimize @use_console_lock to
++ * be @printing_via_unlock since the latter can change at any
++ * time.
+ */
+- console_lock();
++ barrier();
++
++ diff = 0;
++
++ if (use_console_lock) {
++ /*
++ * Hold the console_lock to guarantee safe access to
++ * console->seq. Releasing console_lock flushes more
++ * records in case @seq is still not printed on all
++ * usable consoles.
++ */
++ console_lock();
++ }
+
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(c) {
+@@ -3859,6 +3894,7 @@ static bool __pr_flush(struct console *c
+ if (flags & CON_NBCON) {
+ printk_seq = nbcon_seq_read(c);
+ } else {
++ WARN_ON_ONCE(!use_console_lock);
+ printk_seq = c->seq;
+ }
+
+@@ -3870,7 +3906,8 @@ static bool __pr_flush(struct console *c
+ if (diff != last_diff && reset_on_progress)
+ remaining_jiffies = timeout_jiffies;
+
+- console_unlock();
++ if (use_console_lock)
++ console_unlock();
+
+ /* Note: @diff is 0 if there are no usable consoles. */
+ if (diff == 0 || remaining_jiffies == 0)
+@@ -3940,6 +3977,7 @@ static void __wake_up_klogd(int val)
+ return;
+
+ preempt_disable();
++
+ /*
+ * Guarantee any new records can be seen by tasks preparing to wait
+ * before this context checks if the wait queue is empty.
+@@ -3951,11 +3989,22 @@ static void __wake_up_klogd(int val)
+ *
+ * This pairs with devkmsg_read:A and syslog_print:A.
+ */
+- if (wq_has_sleeper(&log_wait) || /* LMM(__wake_up_klogd:A) */
+- (val & PRINTK_PENDING_OUTPUT)) {
++ if (!wq_has_sleeper(&log_wait)) /* LMM(__wake_up_klogd:A) */
++ val &= ~PRINTK_PENDING_WAKEUP;
++
++ /*
++ * Simple read is safe. register_console() would flush a newly
++ * registered legacy console when writing the message about it
++ * being enabled.
++ */
++ if (!printing_via_unlock)
++ val &= ~PRINTK_PENDING_OUTPUT;
++
++ if (val) {
+ this_cpu_or(printk_pending, val);
+ irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
+ }
++
+ preempt_enable();
+ }
+