summaryrefslogtreecommitdiff
path: root/patches/0009-printk-add-kthread-console-printers.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/0009-printk-add-kthread-console-printers.patch')
-rw-r--r--patches/0009-printk-add-kthread-console-printers.patch277
1 files changed, 277 insertions, 0 deletions
diff --git a/patches/0009-printk-add-kthread-console-printers.patch b/patches/0009-printk-add-kthread-console-printers.patch
new file mode 100644
index 000000000000..1fb69d5c3dd4
--- /dev/null
+++ b/patches/0009-printk-add-kthread-console-printers.patch
@@ -0,0 +1,277 @@
+From: John Ogness <john.ogness@linutronix.de>
+Date: Mon, 13 Dec 2021 21:22:17 +0106
+Subject: [PATCH 09/15] printk: add kthread console printers
+
+Create a kthread for each console to perform console printing. During
+normal operation (@system_state == SYSTEM_RUNNING), the kthread
+printers are responsible for all printing on their respective
+consoles.
+
+During non-normal operation, console printing is done as it has been:
+within the context of the printk caller or within irq work triggered
+by the printk caller.
+
+Console printers synchronize against each other and against console
+lockers by taking the console lock for each message that is printed.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ include/linux/console.h | 2
+ kernel/printk/printk.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 157 insertions(+), 2 deletions(-)
+
+--- a/include/linux/console.h
++++ b/include/linux/console.h
+@@ -153,6 +153,8 @@ struct console {
+ uint ospeed;
+ u64 seq;
+ unsigned long dropped;
++ struct task_struct *thread;
++
+ void *data;
+ struct console *next;
+ };
+--- a/kernel/printk/printk.c
++++ b/kernel/printk/printk.c
+@@ -348,6 +348,20 @@ static int console_msg_format = MSG_FORM
+ /* syslog_lock protects syslog_* variables and write access to clear_seq. */
+ static DEFINE_MUTEX(syslog_lock);
+
++/*
++ * A flag to signify if printk_late_init() has already started the kthread
++ * printers. If true, any later registered consoles must start their own
++ * kthread directly. The flag is write protected by the console_lock.
++ */
++static bool kthreads_started;
++
++static inline bool kthread_printers_active(void)
++{
++ return (kthreads_started &&
++ system_state == SYSTEM_RUNNING &&
++ !oops_in_progress);
++}
++
+ #ifdef CONFIG_PRINTK
+ DECLARE_WAIT_QUEUE_HEAD(log_wait);
+ /* All 3 protected by @syslog_lock. */
+@@ -2201,7 +2215,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 && !kthread_printers_active()) {
+ /*
+ * Disable preemption to avoid being preempted while holding
+ * console_sem which would prevent anyone from printing to
+@@ -2242,6 +2256,8 @@ asmlinkage __visible int _printk(const c
+ }
+ EXPORT_SYMBOL(_printk);
+
++static void start_printk_kthread(struct console *con);
++
+ #else /* CONFIG_PRINTK */
+
+ #define CONSOLE_LOG_MAX 0
+@@ -2273,6 +2289,7 @@ static void call_console_driver(struct c
+ char *dropped_text) {}
+ static bool suppress_message_printing(int level) { return false; }
+ static void printk_delay(int level) {}
++static void start_printk_kthread(struct console *con) {}
+
+ #endif /* CONFIG_PRINTK */
+
+@@ -2461,6 +2478,10 @@ void resume_console(void)
+ down_console_sem();
+ console_suspended = 0;
+ console_unlock();
++
++ /* Wake the kthread printers. */
++ wake_up_klogd();
++
+ pr_flush(1000, true);
+ }
+
+@@ -2676,6 +2697,10 @@ static bool console_flush_all(bool do_co
+ *handover = false;
+
+ do {
++ /* Let the kthread printers do the work if they can. */
++ if (kthread_printers_active())
++ return false;
++
+ any_progress = false;
+
+ for_each_console(con) {
+@@ -2884,6 +2909,10 @@ void console_start(struct console *conso
+ console_lock();
+ console->flags |= CON_ENABLED;
+ console_unlock();
++
++ /* Wake the kthread printers. */
++ wake_up_klogd();
++
+ pr_flush(1000, true);
+ }
+ EXPORT_SYMBOL(console_start);
+@@ -3088,6 +3117,8 @@ void register_console(struct console *ne
+ /* Begin with next message. */
+ newcon->seq = prb_next_seq(prb);
+ }
++ if (kthreads_started)
++ start_printk_kthread(newcon);
+ console_unlock();
+ console_sysfs_notify();
+
+@@ -3144,6 +3175,11 @@ int unregister_console(struct console *c
+ }
+ }
+
++ if (console->thread) {
++ kthread_stop(console->thread);
++ console->thread = NULL;
++ }
++
+ if (res)
+ goto out_disable_unlock;
+
+@@ -3250,6 +3286,13 @@ static int __init printk_late_init(void)
+ console_cpu_notify, NULL);
+ WARN_ON(ret < 0);
+ printk_sysctl_init();
++
++ console_lock();
++ for_each_console(con)
++ start_printk_kthread(con);
++ kthreads_started = true;
++ console_unlock();
++
+ return 0;
+ }
+ late_initcall(printk_late_init);
+@@ -3320,6 +3363,116 @@ bool pr_flush(int timeout_ms, bool reset
+ }
+ EXPORT_SYMBOL(pr_flush);
+
++static bool printer_should_wake(struct console *con, u64 seq)
++{
++ short flags;
++
++ if (kthread_should_stop())
++ return true;
++
++ if (console_suspended)
++ return false;
++
++ /*
++ * This is an unsafe read to con->flags, but false positives
++ * are not an issue as long as they are rare.
++ */
++ flags = data_race(READ_ONCE(con->flags));
++ if (!(flags & CON_ENABLED))
++ return false;
++
++ return prb_read_valid(prb, seq, NULL);
++}
++
++static int printk_kthread_func(void *data)
++{
++ struct console *con = data;
++ char *dropped_text = NULL;
++ char *ext_text = NULL;
++ bool progress;
++ bool handover;
++ u64 seq = 0;
++ char *text;
++ int error;
++
++ pr_info("%sconsole [%s%d]: printing thread started\n",
++ (con->flags & CON_BOOT) ? "boot" : "",
++ con->name, con->index);
++
++ text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL);
++ if (!text)
++ goto out;
++
++ if (con->flags & CON_EXTENDED) {
++ ext_text = kmalloc(CONSOLE_EXT_LOG_MAX, GFP_KERNEL);
++ if (!ext_text)
++ goto out;
++ } else {
++ dropped_text = kmalloc(DROPPED_TEXT_MAX, GFP_KERNEL);
++ if (!dropped_text)
++ goto out;
++ }
++
++ for (;;) {
++ error = wait_event_interruptible(log_wait, printer_should_wake(con, seq));
++
++ if (kthread_should_stop())
++ break;
++
++ if (error)
++ continue;
++
++ do {
++ console_lock();
++ if (console_suspended) {
++ console_unlock();
++ break;
++ }
++
++ /*
++ * Even though the printk kthread is always preemptible, it is
++ * still not allowed to call cond_resched() from within
++ * console drivers. The task may become non-preemptible in the
++ * console driver call chain. For example, vt_console_print()
++ * takes a spinlock and then can call into fbcon_redraw(),
++ * which can conditionally invoke cond_resched().
++ */
++ console_may_schedule = 0;
++ progress = console_emit_next_record(con, text, ext_text,
++ dropped_text, &handover);
++ if (handover)
++ break;
++
++ seq = con->seq;
++
++ /* Unlock console without invoking direct printing. */
++ __console_unlock();
++ } while (progress);
++ }
++out:
++ kfree(dropped_text);
++ kfree(ext_text);
++ kfree(text);
++ pr_info("%sconsole [%s%d]: printing thread stopped\n",
++ (con->flags & CON_BOOT) ? "boot" : "",
++ con->name, con->index);
++ return 0;
++}
++
++/* Must be called within console_lock(). */
++static void start_printk_kthread(struct console *con)
++{
++ con->thread = kthread_run(printk_kthread_func, con,
++ "pr/%s%d", con->name, con->index);
++ if (IS_ERR(con->thread)) {
++ con->thread = NULL;
++ pr_err("%sconsole [%s%d]: unable to start printing thread\n",
++ (con->flags & CON_BOOT) ? "boot" : "",
++ con->name, con->index);
++ return;
++ }
++}
++
+ /*
+ * Delayed printk version, for scheduler-internal messages:
+ */
+@@ -3339,7 +3492,7 @@ static void wake_up_klogd_work_func(stru
+ }
+
+ if (pending & PRINTK_PENDING_WAKEUP)
+- wake_up_interruptible(&log_wait);
++ wake_up_interruptible_all(&log_wait);
+ }
+
+ static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) =