summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch')
-rw-r--r--debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch532
1 files changed, 532 insertions, 0 deletions
diff --git a/debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch b/debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch
new file mode 100644
index 000000000..edcfbb989
--- /dev/null
+++ b/debian/patches-rt/0044-printk-Add-kthread-for-all-legacy-consoles.patch
@@ -0,0 +1,532 @@
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 22 Sep 2023 17:35:04 +0000
+Subject: [PATCH 44/46] printk: Add kthread for all legacy consoles
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.9/older/patches-6.9-rt5.tar.xz
+
+The write callback of legacy consoles makes use of spinlocks.
+This is not permitted with PREEMPT_RT in atomic contexts.
+
+For PREEMPT_RT, create a new kthread to handle printing of all
+the legacy consoles (and nbcon consoles if boot consoles are
+registered).
+
+Since, if running from the kthread, the consoles are printing
+in a task context, the legacy nbcon printing can use the
+device_lock(), write_thread(), device_unlock() callbacks for
+printing.
+
+Introduce the macro force_printkthreads() to query if the
+forced threading of legacy consoles is in effect.
+
+These changes only affect CONFIG_PREEMPT_RT.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ kernel/printk/internal.h | 11 +-
+ kernel/printk/nbcon.c | 52 ++++++----
+ kernel/printk/printk.c | 242 ++++++++++++++++++++++++++++++++++++++---------
+ 3 files changed, 243 insertions(+), 62 deletions(-)
+
+--- a/kernel/printk/internal.h
++++ b/kernel/printk/internal.h
+@@ -21,6 +21,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl
+ (con->flags & CON_BOOT) ? "boot" : "", \
+ con->name, con->index, ##__VA_ARGS__)
+
++#ifdef CONFIG_PREEMPT_RT
++# define force_printkthreads() (true)
++#else
++# define force_printkthreads() (false)
++#endif
++
+ #ifdef CONFIG_PRINTK
+
+ #ifdef CONFIG_PRINTK_CALLER
+@@ -90,9 +96,10 @@ void nbcon_free(struct console *con);
+ enum nbcon_prio nbcon_get_default_prio(void);
+ void nbcon_atomic_flush_pending(void);
+ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
+- int cookie);
++ int cookie, bool use_atomic);
+ void nbcon_kthread_create(struct console *con);
+ void nbcon_wake_threads(void);
++void nbcon_legacy_kthread_create(void);
+
+ /*
+ * Check if the given console is currently capable and allowed to print
+@@ -179,7 +186,7 @@ static inline void nbcon_free(struct con
+ static inline enum nbcon_prio nbcon_get_default_prio(void) { return NBCON_PRIO_NONE; }
+ static inline void nbcon_atomic_flush_pending(void) { }
+ static inline bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
+- int cookie) { return false; }
++ int cookie, bool use_atomic) { return false; }
+
+ static inline bool console_is_usable(struct console *con, short flags,
+ bool use_atomic) { return false; }
+--- a/kernel/printk/nbcon.c
++++ b/kernel/printk/nbcon.c
+@@ -1224,9 +1224,10 @@ enum nbcon_prio nbcon_get_default_prio(v
+ }
+
+ /*
+- * nbcon_atomic_emit_one - Print one record for an nbcon console using the
+- * write_atomic() callback
++ * nbcon_emit_one - Print one record for an nbcon console using the
++ * specified callback
+ * @wctxt: An initialized write context struct to use for this context
++ * @use_atomic: True if the write_atomic callback is to be used
+ *
+ * Return: True, when a record has been printed and there are still
+ * pending records. The caller might want to continue flushing.
+@@ -1239,7 +1240,7 @@ enum nbcon_prio nbcon_get_default_prio(v
+ * This is an internal helper to handle the locking of the console before
+ * calling nbcon_emit_next_record().
+ */
+-static bool nbcon_atomic_emit_one(struct nbcon_write_context *wctxt)
++static bool nbcon_emit_one(struct nbcon_write_context *wctxt, bool use_atomic)
+ {
+ struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
+
+@@ -1254,7 +1255,7 @@ static bool nbcon_atomic_emit_one(struct
+ * The higher priority printing context takes over responsibility
+ * to print the pending records.
+ */
+- if (!nbcon_emit_next_record(wctxt, true))
++ if (!nbcon_emit_next_record(wctxt, use_atomic))
+ return false;
+
+ nbcon_context_release(ctxt);
+@@ -1271,6 +1272,7 @@ static bool nbcon_atomic_emit_one(struct
+ * both the console_lock and the SRCU read lock. Otherwise it
+ * is set to false.
+ * @cookie: The cookie from the SRCU read lock.
++ * @use_atomic: True if the write_atomic callback is to be used
+ *
+ * Context: Any context except NMI.
+ * Return: True, when a record has been printed and there are still
+@@ -1286,26 +1288,38 @@ static bool nbcon_atomic_emit_one(struct
+ * Essentially it is the nbcon version of console_emit_next_record().
+ */
+ bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
+- int cookie)
++ int cookie, bool use_atomic)
+ {
+ struct nbcon_write_context wctxt = { };
+ struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt);
+ unsigned long flags;
+ bool progress;
+
+- /* Use the same procedure as console_emit_next_record(). */
+- printk_safe_enter_irqsave(flags);
+- console_lock_spinning_enable();
+- stop_critical_timings();
+-
+- ctxt->console = con;
+- ctxt->prio = nbcon_get_default_prio();
+-
+- progress = nbcon_atomic_emit_one(&wctxt);
+-
+- start_critical_timings();
+- *handover = console_lock_spinning_disable_and_check(cookie);
+- printk_safe_exit_irqrestore(flags);
++ ctxt->console = con;
++
++ if (use_atomic) {
++ /* Use the same procedure as console_emit_next_record(). */
++ printk_safe_enter_irqsave(flags);
++ console_lock_spinning_enable();
++ stop_critical_timings();
++
++ ctxt->prio = nbcon_get_default_prio();
++ progress = nbcon_emit_one(&wctxt, use_atomic);
++
++ start_critical_timings();
++ *handover = console_lock_spinning_disable_and_check(cookie);
++ printk_safe_exit_irqrestore(flags);
++ } else {
++ *handover = false;
++
++ con->device_lock(con, &flags);
++ cant_migrate();
++
++ ctxt->prio = nbcon_get_default_prio();
++ progress = nbcon_emit_one(&wctxt, use_atomic);
++
++ con->device_unlock(con, flags);
++ }
+
+ return progress;
+ }
+@@ -1622,6 +1636,8 @@ static int __init printk_setup_threads(v
+ printk_threads_enabled = true;
+ for_each_console(con)
+ nbcon_kthread_create(con);
++ if (force_printkthreads() && printing_via_unlock)
++ nbcon_legacy_kthread_create();
+ console_list_unlock();
+ return 0;
+ }
+--- a/kernel/printk/printk.c
++++ b/kernel/printk/printk.c
+@@ -487,6 +487,9 @@ bool have_boot_console;
+
+ #ifdef CONFIG_PRINTK
+ DECLARE_WAIT_QUEUE_HEAD(log_wait);
++
++static DECLARE_WAIT_QUEUE_HEAD(legacy_wait);
++
+ /* All 3 protected by @syslog_lock. */
+ /* the next printk record to read by syslog(READ) or /proc/kmsg */
+ static u64 syslog_seq;
+@@ -2345,7 +2348,8 @@ asmlinkage int vprintk_emit(int facility
+ const struct dev_printk_info *dev_info,
+ const char *fmt, va_list args)
+ {
+- bool do_trylock_unlock = printing_via_unlock;
++ bool do_trylock_unlock = printing_via_unlock &&
++ !force_printkthreads();
+ int printed_len;
+
+ /* Suppress unimportant messages after panic happens */
+@@ -2468,6 +2472,14 @@ EXPORT_SYMBOL(_printk);
+ static bool pr_flush(int timeout_ms, bool reset_on_progress);
+ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress);
+
++static struct task_struct *nbcon_legacy_kthread;
++
++static inline void wake_up_legacy_kthread(void)
++{
++ if (nbcon_legacy_kthread)
++ wake_up_interruptible(&legacy_wait);
++}
++
+ #else /* CONFIG_PRINTK */
+
+ #define printk_time false
+@@ -2481,6 +2493,8 @@ static u64 syslog_seq;
+ static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; }
+ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; }
+
++static inline void nbcon_legacy_kthread_create(void) { }
++static inline void wake_up_legacy_kthread(void) { }
+ #endif /* CONFIG_PRINTK */
+
+ #ifdef CONFIG_EARLY_PRINTK
+@@ -2726,6 +2740,8 @@ void resume_console(void)
+ }
+ console_srcu_read_unlock(cookie);
+
++ wake_up_legacy_kthread();
++
+ pr_flush(1000, true);
+ }
+
+@@ -2740,7 +2756,8 @@ void resume_console(void)
+ */
+ static int console_cpu_notify(unsigned int cpu)
+ {
+- if (!cpuhp_tasks_frozen && printing_via_unlock) {
++ if (!cpuhp_tasks_frozen && printing_via_unlock &&
++ !force_printkthreads()) {
+ /* If trylock fails, someone else is doing the printing */
+ if (console_trylock())
+ console_unlock();
+@@ -3000,31 +3017,43 @@ static bool console_emit_next_record(str
+ con->dropped = 0;
+ }
+
+- /*
+- * While actively printing out messages, if another printk()
+- * were to occur on another CPU, it may wait for this one to
+- * finish. This task can not be preempted if there is a
+- * waiter waiting to take over.
+- *
+- * Interrupts are disabled because the hand over to a waiter
+- * must not be interrupted until the hand over is completed
+- * (@console_waiter is cleared).
+- */
+- printk_safe_enter_irqsave(flags);
+- console_lock_spinning_enable();
++ /* Write everything out to the hardware. */
+
+- /* Do not trace print latency. */
+- stop_critical_timings();
++ if (force_printkthreads()) {
++ /*
++ * With forced threading this function is either in a thread
++ * or panic context. So there is no need for concern about
++ * printk reentrance or handovers.
++ */
+
+- /* Write everything out to the hardware. */
+- con->write(con, outbuf, pmsg.outbuf_len);
++ con->write(con, outbuf, pmsg.outbuf_len);
++ con->seq = pmsg.seq + 1;
++ } else {
++ /*
++ * While actively printing out messages, if another printk()
++ * were to occur on another CPU, it may wait for this one to
++ * finish. This task can not be preempted if there is a
++ * waiter waiting to take over.
++ *
++ * Interrupts are disabled because the hand over to a waiter
++ * must not be interrupted until the hand over is completed
++ * (@console_waiter is cleared).
++ */
++ printk_safe_enter_irqsave(flags);
++ console_lock_spinning_enable();
+
+- start_critical_timings();
++ /* Do not trace print latency. */
++ stop_critical_timings();
+
+- con->seq = pmsg.seq + 1;
++ con->write(con, outbuf, pmsg.outbuf_len);
+
+- *handover = console_lock_spinning_disable_and_check(cookie);
+- printk_safe_exit_irqrestore(flags);
++ start_critical_timings();
++
++ con->seq = pmsg.seq + 1;
++
++ *handover = console_lock_spinning_disable_and_check(cookie);
++ printk_safe_exit_irqrestore(flags);
++ }
+ skip:
+ return true;
+ }
+@@ -3088,12 +3117,13 @@ static bool console_flush_all(bool do_co
+ if ((flags & CON_NBCON) && con->kthread)
+ continue;
+
+- if (!console_is_usable(con, flags, true))
++ if (!console_is_usable(con, flags, !do_cond_resched))
+ continue;
+ any_usable = true;
+
+ if (flags & CON_NBCON) {
+- progress = nbcon_legacy_emit_next_record(con, handover, cookie);
++ progress = nbcon_legacy_emit_next_record(con, handover, cookie,
++ !do_cond_resched);
+ printk_seq = nbcon_seq_read(con);
+ } else {
+ progress = console_emit_next_record(con, handover, cookie);
+@@ -3132,19 +3162,7 @@ static bool console_flush_all(bool do_co
+ return false;
+ }
+
+-/**
+- * console_unlock - unblock the console subsystem from printing
+- *
+- * Releases the console_lock which the caller holds to block printing of
+- * the console subsystem.
+- *
+- * While the console_lock was held, console output may have been buffered
+- * by printk(). If this is the case, console_unlock(); emits
+- * the output prior to releasing the lock.
+- *
+- * console_unlock(); may be called from any context.
+- */
+-void console_unlock(void)
++static void console_flush_and_unlock(void)
+ {
+ bool do_cond_resched;
+ bool handover;
+@@ -3188,6 +3206,32 @@ void console_unlock(void)
+ */
+ } while (prb_read_valid(prb, next_seq, NULL) && console_trylock());
+ }
++
++/**
++ * console_unlock - unblock the console subsystem from printing
++ *
++ * Releases the console_lock which the caller holds to block printing of
++ * the console subsystem.
++ *
++ * While the console_lock was held, console output may have been buffered
++ * by printk(). If this is the case, console_unlock(); emits
++ * the output prior to releasing the lock.
++ *
++ * console_unlock(); may be called from any context.
++ */
++void console_unlock(void)
++{
++ /*
++ * Forced threading relies on kthread and atomic consoles for
++ * printing. It never attempts to print from console_unlock().
++ */
++ if (force_printkthreads()) {
++ __console_unlock();
++ return;
++ }
++
++ console_flush_and_unlock();
++}
+ EXPORT_SYMBOL(console_unlock);
+
+ /**
+@@ -3397,11 +3441,106 @@ void console_start(struct console *conso
+
+ if (flags & CON_NBCON)
+ nbcon_kthread_wake(console);
++ else
++ wake_up_legacy_kthread();
+
+ __pr_flush(console, 1000, true);
+ }
+ EXPORT_SYMBOL(console_start);
+
++#ifdef CONFIG_PRINTK
++static bool printer_should_wake(void)
++{
++ bool available = false;
++ struct console *con;
++ int cookie;
++
++ if (kthread_should_stop())
++ return true;
++
++ cookie = console_srcu_read_lock();
++ for_each_console_srcu(con) {
++ short flags = console_srcu_read_flags(con);
++ u64 printk_seq;
++
++ /*
++ * The legacy printer thread is only for legacy consoles,
++ * unless the nbcon console has no kthread printer.
++ */
++ if ((flags & CON_NBCON) && con->kthread)
++ continue;
++
++ if (!console_is_usable(con, flags, true))
++ continue;
++
++ if (flags & CON_NBCON) {
++ printk_seq = nbcon_seq_read(con);
++ } else {
++ /*
++ * It is safe to read @seq because only this
++ * thread context updates @seq.
++ */
++ printk_seq = con->seq;
++ }
++
++ if (prb_read_valid(prb, printk_seq, NULL)) {
++ available = true;
++ break;
++ }
++ }
++ console_srcu_read_unlock(cookie);
++
++ return available;
++}
++
++static int nbcon_legacy_kthread_func(void *unused)
++{
++ int error;
++
++ for (;;) {
++ error = wait_event_interruptible(legacy_wait, printer_should_wake());
++
++ if (kthread_should_stop())
++ break;
++
++ if (error)
++ continue;
++
++ console_lock();
++ console_flush_and_unlock();
++ }
++
++ return 0;
++}
++
++void nbcon_legacy_kthread_create(void)
++{
++ struct task_struct *kt;
++
++ lockdep_assert_held(&console_mutex);
++
++ if (!force_printkthreads())
++ return;
++
++ if (!printk_threads_enabled || nbcon_legacy_kthread)
++ return;
++
++ kt = kthread_run(nbcon_legacy_kthread_func, NULL, "pr/legacy");
++ if (IS_ERR(kt)) {
++ pr_err("unable to start legacy printing thread\n");
++ return;
++ }
++
++ nbcon_legacy_kthread = kt;
++
++ /*
++ * It is important that console printing threads are scheduled
++ * shortly after a printk call and with generous runtime budgets.
++ */
++ sched_set_normal(nbcon_legacy_kthread, -20);
++}
++#endif /* CONFIG_PRINTK */
++
+ static int __read_mostly keep_bootcon;
+
+ static int __init keep_bootcon_setup(char *str)
+@@ -3690,6 +3829,7 @@ void register_console(struct console *ne
+ } else {
+ have_legacy_console = true;
+ newcon->seq = init_seq;
++ nbcon_legacy_kthread_create();
+ }
+
+ if (newcon->flags & CON_BOOT)
+@@ -3856,6 +3996,13 @@ static int unregister_console_locked(str
+ nbcon_kthread_create(c);
+ }
+
++#ifdef CONFIG_PRINTK
++ if (!printing_via_unlock && nbcon_legacy_kthread) {
++ kthread_stop(nbcon_legacy_kthread);
++ nbcon_legacy_kthread = NULL;
++ }
++#endif
++
+ return res;
+ }
+
+@@ -4014,8 +4161,12 @@ static bool __pr_flush(struct console *c
+
+ seq = prb_next_reserve_seq(prb);
+
+- /* Flush the consoles so that records up to @seq are printed. */
+- if (printing_via_unlock) {
++ /*
++ * Flush the consoles so that records up to @seq are printed.
++ * Otherwise this function will just wait for the threaded printers
++ * to print up to @seq.
++ */
++ if (printing_via_unlock && !force_printkthreads()) {
+ console_lock();
+ console_unlock();
+ }
+@@ -4129,9 +4280,16 @@ static void wake_up_klogd_work_func(stru
+ int pending = this_cpu_xchg(printk_pending, 0);
+
+ if (pending & PRINTK_PENDING_OUTPUT) {
+- /* If trylock fails, someone else is doing the printing */
+- if (console_trylock())
+- console_unlock();
++ if (force_printkthreads()) {
++ wake_up_legacy_kthread();
++ } else {
++ /*
++ * If trylock fails, some other context
++ * will do the printing.
++ */
++ if (console_trylock())
++ console_unlock();
++ }
+ }
+
+ if (pending & PRINTK_PENDING_WAKEUP)