diff options
Diffstat (limited to '')
-rw-r--r-- | debian/patches-rt/0014-printk-nbcon-Implement-processing-in-port-lock-wrapp.patch | 478 |
1 files changed, 0 insertions, 478 deletions
diff --git a/debian/patches-rt/0014-printk-nbcon-Implement-processing-in-port-lock-wrapp.patch b/debian/patches-rt/0014-printk-nbcon-Implement-processing-in-port-lock-wrapp.patch deleted file mode 100644 index edf62ea414..0000000000 --- a/debian/patches-rt/0014-printk-nbcon-Implement-processing-in-port-lock-wrapp.patch +++ /dev/null @@ -1,478 +0,0 @@ -From: John Ogness <john.ogness@linutronix.de> -Date: Wed, 13 Sep 2023 08:35:23 +0000 -Subject: [PATCH 14/48] printk: nbcon: Implement processing in port->lock - wrapper -Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.8/older/patches-6.8.2-rt11.tar.xz - -Currently the port->lock wrappers uart_port_lock(), -uart_port_unlock() (and their variants) only lock/unlock -the spin_lock. - -If the port is an nbcon console, the wrappers must also -acquire/release the console and mark the region as unsafe. This -allows general port->lock synchronization to be synchronized -with the nbcon console ownership. - -Introduce a new struct nbcon_drvdata within struct console that -provides the necessary components for the port lock wrappers to -acquire the nbcon console and track its ownership. - -Also introduce uart_port_set_cons() as a wrapper to set @cons -of a uart_port. The wrapper sets @cons under the port lock in -order to prevent @cons from disappearing while another context -owns the port lock via the port lock wrappers. - -Also cleanup the description of the console_srcu_read_flags() -function. It is used by the port lock wrappers to ensure a -console cannot be fully unregistered while another context -owns the port lock via the port lock wrappers. - -Signed-off-by: John Ogness <john.ogness@linutronix.de> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - drivers/tty/serial/8250/8250_core.c | 6 +- - drivers/tty/serial/amba-pl011.c | 2 - drivers/tty/serial/serial_core.c | 16 ++--- - include/linux/console.h | 57 ++++++++++++++++---- - include/linux/printk.h | 13 ++++ - include/linux/serial_core.h | 98 +++++++++++++++++++++++++++++++++++- - kernel/printk/nbcon.c | 52 +++++++++++++++++++ - 7 files changed, 219 insertions(+), 25 deletions(-) - ---- a/drivers/tty/serial/8250/8250_core.c -+++ b/drivers/tty/serial/8250/8250_core.c -@@ -627,11 +627,11 @@ static int univ8250_console_setup(struct - - port = &serial8250_ports[co->index].port; - /* link port to console */ -- port->cons = co; -+ uart_port_set_cons(port, co); - - retval = serial8250_console_setup(port, options, false); - if (retval != 0) -- port->cons = NULL; -+ uart_port_set_cons(port, NULL); - return retval; - } - -@@ -689,7 +689,7 @@ static int univ8250_console_match(struct - continue; - - co->index = i; -- port->cons = co; -+ uart_port_set_cons(port, co); - return serial8250_console_setup(port, options, true); - } - ---- a/drivers/tty/serial/amba-pl011.c -+++ b/drivers/tty/serial/amba-pl011.c -@@ -2488,7 +2488,7 @@ static int pl011_console_match(struct co - continue; - - co->index = i; -- port->cons = co; -+ uart_port_set_cons(port, co); - return pl011_console_setup(co, options); - } - ---- a/drivers/tty/serial/serial_core.c -+++ b/drivers/tty/serial/serial_core.c -@@ -3145,8 +3145,15 @@ static int serial_core_add_one_port(stru - state->uart_port = uport; - uport->state = state; - -+ /* -+ * If this port is in use as a console then the spinlock is already -+ * initialised. -+ */ -+ if (!uart_console_registered(uport)) -+ uart_port_spin_lock_init(uport); -+ - state->pm_state = UART_PM_STATE_UNDEFINED; -- uport->cons = drv->cons; -+ uart_port_set_cons(uport, drv->cons); - uport->minor = drv->tty_driver->minor_start + uport->line; - uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, - drv->tty_driver->name_base + uport->line); -@@ -3155,13 +3162,6 @@ static int serial_core_add_one_port(stru - goto out; - } - -- /* -- * If this port is in use as a console then the spinlock is already -- * initialised. -- */ -- if (!uart_console_registered(uport)) -- uart_port_spin_lock_init(uport); -- - if (uport->cons && uport->dev) - of_console_check(uport->dev->of_node, uport->cons->name, uport->line); - ---- a/include/linux/console.h -+++ b/include/linux/console.h -@@ -283,6 +283,25 @@ struct nbcon_write_context { - }; - - /** -+ * struct nbcon_drvdata - Data to allow nbcon acquire in non-print context -+ * @ctxt: The core console context -+ * @srcu_cookie: Storage for a console_srcu_lock cookie, if needed -+ * @owner_index: Storage for the owning console index, if needed -+ * @locked: Storage for the locked state, if needed -+ * -+ * All fields (except for @ctxt) are available exclusively to the driver to -+ * use as needed. They are not used by the printk subsystem. -+ */ -+struct nbcon_drvdata { -+ struct nbcon_context __private ctxt; -+ -+ /* reserved for driver use */ -+ int srcu_cookie; -+ short owner_index; -+ bool locked; -+}; -+ -+/** - * struct console - The console descriptor structure - * @name: The name of the console driver - * @write: Legacy write callback to output messages (Optional) -@@ -396,6 +415,21 @@ struct console { - - atomic_t __private nbcon_state; - atomic_long_t __private nbcon_seq; -+ -+ /** -+ * @nbcon_drvdata: -+ * -+ * Data for nbcon ownership tracking to allow acquiring nbcon consoles -+ * in non-printing contexts. -+ * -+ * Drivers may need to acquire nbcon consoles in non-printing -+ * contexts. This is achieved by providing a struct nbcon_drvdata. -+ * Then the driver can call nbcon_driver_acquire() and -+ * nbcon_driver_release(). The struct does not require any special -+ * initialization. -+ */ -+ struct nbcon_drvdata *nbcon_drvdata; -+ - struct printk_buffers *pbufs; - }; - -@@ -425,28 +459,29 @@ extern void console_list_unlock(void) __ - extern struct hlist_head console_list; - - /** -- * console_srcu_read_flags - Locklessly read the console flags -+ * console_srcu_read_flags - Locklessly read flags of a possibly registered -+ * console - * @con: struct console pointer of console to read flags from - * -- * This function provides the necessary READ_ONCE() and data_race() -- * notation for locklessly reading the console flags. The READ_ONCE() -- * in this function matches the WRITE_ONCE() when @flags are modified -- * for registered consoles with console_srcu_write_flags(). -+ * Locklessly reading @con->flags provides a consistent read value because -+ * there is at most one CPU modifying @con->flags and that CPU is using only -+ * read-modify-write operations to do so. - * -- * Only use this function to read console flags when locklessly -- * iterating the console list via srcu. -+ * Requires console_srcu_read_lock to be held, which implies that @con might -+ * be a registered console. If the caller is holding the console_list_lock or -+ * it is certain that the console is not registered, the caller may read -+ * @con->flags directly instead. - * - * Context: Any context. -+ * Return: The current value of the @con->flags field. - */ - static inline short console_srcu_read_flags(const struct console *con) - { - WARN_ON_ONCE(!console_srcu_read_lock_is_held()); - - /* -- * Locklessly reading console->flags provides a consistent -- * read value because there is at most one CPU modifying -- * console->flags and that CPU is using only read-modify-write -- * operations to do so. -+ * The READ_ONCE() matches the WRITE_ONCE() when @flags are modified -+ * for registered consoles with console_srcu_write_flags(). - */ - return data_race(READ_ONCE(con->flags)); - } ---- a/include/linux/printk.h -+++ b/include/linux/printk.h -@@ -9,6 +9,8 @@ - #include <linux/ratelimit_types.h> - #include <linux/once_lite.h> - -+struct console; -+ - extern const char linux_banner[]; - extern const char linux_proc_banner[]; - -@@ -193,6 +195,8 @@ void show_regs_print_info(const char *lo - extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; - extern asmlinkage void dump_stack(void) __cold; - void printk_trigger_flush(void); -+extern void nbcon_driver_acquire(struct console *con); -+extern void nbcon_driver_release(struct console *con); - #else - static inline __printf(1, 0) - int vprintk(const char *s, va_list args) -@@ -272,6 +276,15 @@ static inline void dump_stack(void) - static inline void printk_trigger_flush(void) - { - } -+ -+static inline void nbcon_driver_acquire(struct console *con) -+{ -+} -+ -+static inline void nbcon_driver_release(struct console *con) -+{ -+} -+ - #endif - - bool this_cpu_in_panic(void); ---- a/include/linux/serial_core.h -+++ b/include/linux/serial_core.h -@@ -8,10 +8,13 @@ - #define LINUX_SERIAL_CORE_H - - #include <linux/bitops.h> -+#include <linux/bug.h> - #include <linux/compiler.h> - #include <linux/console.h> - #include <linux/interrupt.h> - #include <linux/circ_buf.h> -+#include <linux/lockdep.h> -+#include <linux/printk.h> - #include <linux/spinlock.h> - #include <linux/sched.h> - #include <linux/tty.h> -@@ -607,12 +610,90 @@ static inline void __uart_port_unlock_ir - } - - /** -+ * uart_port_set_cons - Safely set the @cons field for a uart -+ * @up: The uart port to set -+ * @con: The new console to set to -+ * -+ * This function must be used to set @up->cons. It uses the port lock to -+ * synchronize with the port lock wrappers in order to ensure that the console -+ * cannot change or disappear while another context is holding the port lock. -+ */ -+static inline void uart_port_set_cons(struct uart_port *up, struct console *con) -+{ -+ unsigned long flags; -+ -+ __uart_port_lock_irqsave(up, &flags); -+ up->cons = con; -+ __uart_port_unlock_irqrestore(up, flags); -+} -+ -+/* Only for internal port lock wrapper usage. */ -+static inline void __uart_port_nbcon_acquire(struct uart_port *up) -+{ -+ lockdep_assert_held_once(&up->lock); -+ -+ if (likely(!uart_console(up))) -+ return; -+ -+ if (up->cons->nbcon_drvdata) { -+ /* -+ * If @up->cons is registered, prevent it from fully -+ * unregistering until this context releases the nbcon. -+ */ -+ int cookie = console_srcu_read_lock(); -+ -+ /* Ensure console is registered and is an nbcon console. */ -+ if (!hlist_unhashed_lockless(&up->cons->node) && -+ (console_srcu_read_flags(up->cons) & CON_NBCON)) { -+ WARN_ON_ONCE(up->cons->nbcon_drvdata->locked); -+ -+ nbcon_driver_acquire(up->cons); -+ -+ /* -+ * Record @up->line to be used during release because -+ * @up->cons->index can change while the port and -+ * nbcon are locked. -+ */ -+ up->cons->nbcon_drvdata->owner_index = up->line; -+ up->cons->nbcon_drvdata->srcu_cookie = cookie; -+ up->cons->nbcon_drvdata->locked = true; -+ } else { -+ console_srcu_read_unlock(cookie); -+ } -+ } -+} -+ -+/* Only for internal port lock wrapper usage. */ -+static inline void __uart_port_nbcon_release(struct uart_port *up) -+{ -+ lockdep_assert_held_once(&up->lock); -+ -+ /* -+ * uart_console() cannot be used here because @up->cons->index might -+ * have changed. Check against @up->cons->nbcon_drvdata->owner_index -+ * instead. -+ */ -+ -+ if (unlikely(up->cons && -+ up->cons->nbcon_drvdata && -+ up->cons->nbcon_drvdata->locked && -+ up->cons->nbcon_drvdata->owner_index == up->line)) { -+ WARN_ON_ONCE(!up->cons->nbcon_drvdata->locked); -+ -+ up->cons->nbcon_drvdata->locked = false; -+ nbcon_driver_release(up->cons); -+ console_srcu_read_unlock(up->cons->nbcon_drvdata->srcu_cookie); -+ } -+} -+ -+/** - * uart_port_lock - Lock the UART port - * @up: Pointer to UART port structure - */ - static inline void uart_port_lock(struct uart_port *up) - { - spin_lock(&up->lock); -+ __uart_port_nbcon_acquire(up); - } - - /** -@@ -622,6 +703,7 @@ static inline void uart_port_lock(struct - static inline void uart_port_lock_irq(struct uart_port *up) - { - spin_lock_irq(&up->lock); -+ __uart_port_nbcon_acquire(up); - } - - /** -@@ -632,6 +714,7 @@ static inline void uart_port_lock_irq(st - static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) - { - spin_lock_irqsave(&up->lock, *flags); -+ __uart_port_nbcon_acquire(up); - } - - /** -@@ -642,7 +725,11 @@ static inline void uart_port_lock_irqsav - */ - static inline bool uart_port_trylock(struct uart_port *up) - { -- return spin_trylock(&up->lock); -+ if (!spin_trylock(&up->lock)) -+ return false; -+ -+ __uart_port_nbcon_acquire(up); -+ return true; - } - - /** -@@ -654,7 +741,11 @@ static inline bool uart_port_trylock(str - */ - static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long *flags) - { -- return spin_trylock_irqsave(&up->lock, *flags); -+ if (!spin_trylock_irqsave(&up->lock, *flags)) -+ return false; -+ -+ __uart_port_nbcon_acquire(up); -+ return true; - } - - /** -@@ -663,6 +754,7 @@ static inline bool uart_port_trylock_irq - */ - static inline void uart_port_unlock(struct uart_port *up) - { -+ __uart_port_nbcon_release(up); - spin_unlock(&up->lock); - } - -@@ -672,6 +764,7 @@ static inline void uart_port_unlock(stru - */ - static inline void uart_port_unlock_irq(struct uart_port *up) - { -+ __uart_port_nbcon_release(up); - spin_unlock_irq(&up->lock); - } - -@@ -682,6 +775,7 @@ static inline void uart_port_unlock_irq( - */ - static inline void uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) - { -+ __uart_port_nbcon_release(up); - spin_unlock_irqrestore(&up->lock, flags); - } - ---- a/kernel/printk/nbcon.c -+++ b/kernel/printk/nbcon.c -@@ -3,9 +3,12 @@ - // Copyright (C) 2022 Intel, Thomas Gleixner - - #include <linux/kernel.h> -+#include <linux/bug.h> - #include <linux/console.h> - #include <linux/delay.h> -+#include <linux/export.h> - #include <linux/slab.h> -+#include <linux/string.h> - #include "internal.h" - /* - * Printk console printing implementation for consoles which does not depend -@@ -988,3 +991,52 @@ void nbcon_free(struct console *con) - - con->pbufs = NULL; - } -+ -+/** -+ * nbcon_driver_acquire - Acquire nbcon console and enter unsafe section -+ * @con: The nbcon console to acquire -+ * -+ * Context: Any context which could not be migrated to another CPU. -+ * -+ * Console drivers will usually use their own internal synchronization -+ * mechasism to synchronize between console printing and non-printing -+ * activities (such as setting baud rates). However, nbcon console drivers -+ * supporting atomic consoles may also want to mark unsafe sections when -+ * performing non-printing activities. -+ * -+ * This function acquires the nbcon console using priority NBCON_PRIO_NORMAL -+ * and marks it unsafe for handover/takeover. -+ * -+ * Console drivers using this function must have provided @nbcon_drvdata in -+ * their struct console, which is used to track ownership and state -+ * information. -+ */ -+void nbcon_driver_acquire(struct console *con) -+{ -+ struct nbcon_context *ctxt = &ACCESS_PRIVATE(con->nbcon_drvdata, ctxt); -+ -+ cant_migrate(); -+ -+ do { -+ do { -+ memset(ctxt, 0, sizeof(*ctxt)); -+ ctxt->console = con; -+ ctxt->prio = NBCON_PRIO_NORMAL; -+ } while (!nbcon_context_try_acquire(ctxt)); -+ -+ } while (!nbcon_context_enter_unsafe(ctxt)); -+} -+EXPORT_SYMBOL_GPL(nbcon_driver_acquire); -+ -+/** -+ * nbcon_driver_release - Exit unsafe section and release the nbcon console -+ * @con: The nbcon console acquired in nbcon_driver_acquire() -+ */ -+void nbcon_driver_release(struct console *con) -+{ -+ struct nbcon_context *ctxt = &ACCESS_PRIVATE(con->nbcon_drvdata, ctxt); -+ -+ if (nbcon_context_exit_unsafe(ctxt)) -+ nbcon_context_release(ctxt); -+} -+EXPORT_SYMBOL_GPL(nbcon_driver_release); |