From: John Ogness <john.ogness@linutronix.de> Date: Fri, 3 Mar 2023 12:27:31 +0000 Subject: [PATCH 24/24] serial: 8250: implement non-BKL console Implement the necessary callbacks to allow the 8250 console driver to perform as a non-BKL console. Remove the implementation for the legacy console callback (write) and add implementations for the non-BKL consoles (write_atomic, write_thread, port_lock) and add CON_NO_BKL to the initial flags. Although non-BKL consoles can coexist with legacy consoles, you will only receive all the benefits of the non-BKL consoles if this console driver is the only console. That means no netconsole, no tty1, no earlyprintk, no earlycon. Just the uart8250. For example: console=ttyS0,115200 Signed-off-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- drivers/tty/serial/8250/8250.h | 269 ++++++++++++++++++++ drivers/tty/serial/8250/8250_aspeed_vuart.c | 2 drivers/tty/serial/8250/8250_bcm7271.c | 23 + drivers/tty/serial/8250/8250_core.c | 50 +++ drivers/tty/serial/8250/8250_exar.c | 8 drivers/tty/serial/8250/8250_fsl.c | 3 drivers/tty/serial/8250/8250_mtk.c | 30 ++ drivers/tty/serial/8250/8250_omap.c | 36 +- drivers/tty/serial/8250/8250_port.c | 369 ++++++++++++++++++++-------- drivers/tty/serial/8250/Kconfig | 1 drivers/tty/serial/serial_core.c | 10 include/linux/serial_8250.h | 11 12 files changed, 686 insertions(+), 126 deletions(-) Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h =================================================================== @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:180 @ static inline void serial_dl_write(struc up->dl_write(up, value); } +static inline bool serial8250_is_console(struct uart_port *port) +{ + return uart_console(port) && !hlist_unhashed_lockless(&port->cons->node); +} + +/** + * serial8250_init_wctxt - Initialize a write context for + * non-console-printing usage + * @wctxt: The write context to initialize + * @cons: The console to assign to the write context + * + * In order to mark an unsafe region, drivers must acquire the console. This + * requires providing an initialized write context (even if that driver will + * not be doing any printing). + * + * This function should not be used for console printing contexts. + */ +static inline void serial8250_init_wctxt(struct cons_write_context *wctxt, + struct console *cons) +{ + struct cons_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + + memset(wctxt, 0, sizeof(*wctxt)); + ctxt->console = cons; + ctxt->prio = CONS_PRIO_NORMAL; +} + +/** + * __serial8250_console_acquire - Acquire a console for + * non-console-printing usage + * @wctxt: An uninitialized write context to use for acquiring + * @cons: The console to assign to the write context + * + * The caller is holding the port->lock. + * The caller is holding the console_srcu_read_lock. + * + * This function should not be used for console printing contexts. + */ +static inline void __serial8250_console_acquire(struct cons_write_context *wctxt, + struct console *cons) +{ + for (;;) { + serial8250_init_wctxt(wctxt, cons); + if (console_try_acquire(wctxt)) + break; + cpu_relax(); + } +} + +/** + * serial8250_enter_unsafe - Mark the beginning of an unsafe region for + * non-console-printing usage + * @up: The port that is entering the unsafe state + * + * The caller should ensure @up is a console before calling this function. + * + * The caller is holding the port->lock. + * This function takes the console_srcu_read_lock and becomes owner of the + * console associated with @up. + * + * This function should not be used for console printing contexts. + */ +static inline void serial8250_enter_unsafe(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + lockdep_assert_held_once(&port->lock); + + for (;;) { + up->cookie = console_srcu_read_lock(); + + __serial8250_console_acquire(&up->wctxt, port->cons); + + if (console_enter_unsafe(&up->wctxt)) + break; + + console_srcu_read_unlock(up->cookie); + cpu_relax(); + } +} + +/** + * serial8250_exit_unsafe - Mark the end of an unsafe region for + * non-console-printing usage + * @up: The port that is exiting the unsafe state + * + * The caller is holding the port->lock. + * This function releases ownership of the console associated with @up and + * releases the console_srcu_read_lock. + * + * This function should not be used for console printing contexts. + */ +static inline void serial8250_exit_unsafe(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + lockdep_assert_held_once(&port->lock); + + if (console_exit_unsafe(&up->wctxt)) + console_release(&up->wctxt); + + console_srcu_read_unlock(up->cookie); +} + +/** + * serial8250_in_IER - Read the IER register for + * non-console-printing usage + * @up: The port to work on + * + * Returns: The value read from IER + * + * The caller is holding the port->lock. + * + * This is the top-level function for non-console-printing contexts to + * read the IER register. The caller does not need to care if @up is a + * console before calling this function. + * + * This function should not be used for printing contexts. + */ +static inline int serial8250_in_IER(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + bool is_console; + int ier; + + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + + ier = serial_in(up, UART_IER); + + if (is_console) + serial8250_exit_unsafe(up); + + return ier; +} + +/** + * __serial8250_set_IER - Directly write to the IER register + * @up: The port to work on + * @wctxt: The current write context + * @ier: The value to write + * + * Returns: True if IER was written to. False otherwise + * + * The caller is holding the port->lock. + * The caller is holding the console_srcu_read_unlock. + * The caller is the owner of the console associated with @up. + * + * This function should only be directly called within console printing + * contexts. Other contexts should use serial8250_set_IER(). + */ +static inline bool __serial8250_set_IER(struct uart_8250_port *up, + struct cons_write_context *wctxt, + int ier) +{ + if (wctxt && !console_can_proceed(wctxt)) + return false; + serial_out(up, UART_IER, ier); + return true; +} + +/** + * serial8250_set_IER - Write a new value to the IER register for + * non-console-printing usage + * @up: The port to work on + * @ier: The value to write + * + * The caller is holding the port->lock. + * + * This is the top-level function for non-console-printing contexts to + * write to the IER register. The caller does not need to care if @up is a + * console before calling this function. + * + * This function should not be used for printing contexts. + */ +static inline void serial8250_set_IER(struct uart_8250_port *up, int ier) +{ + struct uart_port *port = &up->port; + bool is_console; + + is_console = serial8250_is_console(port); + + if (is_console) { + serial8250_enter_unsafe(up); + while (!__serial8250_set_IER(up, &up->wctxt, ier)) { + console_srcu_read_unlock(up->cookie); + console_enter_unsafe(&up->wctxt); + } + serial8250_exit_unsafe(up); + } else { + __serial8250_set_IER(up, NULL, ier); + } +} + +/** + * __serial8250_clear_IER - Directly clear the IER register + * @up: The port to work on + * @wctxt: The current write context + * @prior: Gets set to the previous value of IER + * + * Returns: True if IER was cleared and @prior points to the previous + * value of IER. False otherwise and @prior is invalid + * + * The caller is holding the port->lock. + * The caller is holding the console_srcu_read_unlock. + * The caller is the owner of the console associated with @up. + * + * This function should only be directly called within console printing + * contexts. Other contexts should use serial8250_clear_IER(). + */ +static inline bool __serial8250_clear_IER(struct uart_8250_port *up, + struct cons_write_context *wctxt, + int *prior) +{ + unsigned int clearval = 0; + + if (up->capabilities & UART_CAP_UUE) + clearval = UART_IER_UUE; + + *prior = serial_in(up, UART_IER); + if (wctxt && !console_can_proceed(wctxt)) + return false; + serial_out(up, UART_IER, clearval); + return true; +} + +/** + * serial8250_clear_IER - Clear the IER register for + * non-console-printing usage + * @up: The port to work on + * + * Returns: The previous value of IER + * + * The caller is holding the port->lock. + * + * This is the top-level function for non-console-printing contexts to + * clear the IER register. The caller does not need to care if @up is a + * console before calling this function. + * + * This function should not be used for printing contexts. + */ +static inline int serial8250_clear_IER(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + bool is_console; + int prior; + + is_console = serial8250_is_console(port); + + if (is_console) { + serial8250_enter_unsafe(up); + while (!__serial8250_clear_IER(up, &up->wctxt, &prior)) { + console_srcu_read_unlock(up->cookie); + console_enter_unsafe(&up->wctxt); + } + serial8250_exit_unsafe(up); + } else { + __serial8250_clear_IER(up, NULL, &prior); + } + + return prior; +} + static inline bool serial8250_set_THRI(struct uart_8250_port *up) { if (up->ier & UART_IER_THRI) return false; up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); return true; } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:459 @ static inline bool serial8250_clear_THRI if (!(up->ier & UART_IER_THRI)) return false; up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); return true; } Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_aspeed_vuart.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_aspeed_vuart.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:281 @ static void __aspeed_vuart_set_throttle( up->ier &= ~irqs; if (!throttle) up->ier |= irqs; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); } static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) { Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_bcm7271.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_bcm7271.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_bcm7271.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:609 @ static int brcmuart_startup(struct uart_ * Disable the Receive Data Interrupt because the DMA engine * will handle this. */ + spin_lock_irq(&port->lock); up->ier &= ~UART_IER_RDI; - serial_port_out(port, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); + spin_unlock_irq(&port->lock); priv->tx_running = false; priv->dma.rx_dma = NULL; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:792 @ static int brcmuart_handle_irq(struct ua spin_lock_irqsave(&p->lock, flags); status = serial_port_in(p, UART_LSR); if ((status & UART_LSR_DR) == 0) { + bool is_console; + + is_console = serial8250_is_console(p); + + if (is_console) + serial8250_enter_unsafe(up); ier = serial_port_in(p, UART_IER); /* @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:818 @ static int brcmuart_handle_irq(struct ua serial_port_in(p, UART_RX); } + if (is_console) + serial8250_exit_unsafe(up); + handled = 1; } spin_unlock_irqrestore(&p->lock, flags); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:858 @ static enum hrtimer_restart brcmuart_hrt /* re-enable receive unless upper layer has disabled it */ if ((up->ier & (UART_IER_RLSI | UART_IER_RDI)) == (UART_IER_RLSI | UART_IER_RDI)) { + bool is_console; + + is_console = serial8250_is_console(p); + + if (is_console) + serial8250_enter_unsafe(up); + status = serial_port_in(p, UART_IER); status |= (UART_IER_RLSI | UART_IER_RDI); serial_port_out(p, UART_IER, status); status = serial_port_in(p, UART_MCR); status |= UART_MCR_RTS; serial_port_out(p, UART_MCR, status); + + if (is_console) + serial8250_exit_unsafe(up); } spin_unlock_irqrestore(&p->lock, flags); return HRTIMER_NORESTART; Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_core.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_core.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_core.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:259 @ static void serial8250_timeout(struct ti static void serial8250_backup_timeout(struct timer_list *t) { struct uart_8250_port *up = from_timer(up, t, timer); + struct uart_port *port = &up->port; unsigned int iir, ier = 0, lsr; unsigned long flags; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:270 @ static void serial8250_backup_timeout(st * based handler. */ if (up->port.irq) { + bool is_console; + + /* + * Do not use serial8250_clear_IER() because this code + * ignores capabilties. + */ + + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + ier = serial_in(up, UART_IER); serial_out(up, UART_IER, 0); + + if (is_console) + serial8250_exit_unsafe(up); } iir = serial_in(up, UART_IIR); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:309 @ static void serial8250_backup_timeout(st serial8250_tx_chars(up); if (up->port.irq) - serial_out(up, UART_IER, ier); + serial8250_set_IER(up, ier); spin_unlock_irqrestore(&up->port.lock, flags); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:595 @ serial8250_register_ports(struct uart_dr #ifdef CONFIG_SERIAL_8250_CONSOLE -static void univ8250_console_write(struct console *co, const char *s, - unsigned int count) +static void univ8250_console_port_lock(struct console *con, bool do_lock, unsigned long *flags) +{ + struct uart_8250_port *up = &serial8250_ports[con->index]; + + if (do_lock) + spin_lock_irqsave(&up->port.lock, *flags); + else + spin_unlock_irqrestore(&up->port.lock, *flags); +} + +static bool univ8250_console_write_atomic(struct console *co, + struct cons_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + return serial8250_console_write_atomic(up, wctxt); +} + +static bool univ8250_console_write_thread(struct console *co, + struct cons_write_context *wctxt) { struct uart_8250_port *up = &serial8250_ports[co->index]; - serial8250_console_write(up, s, count); + return serial8250_console_write_thread(up, wctxt); } static int univ8250_console_setup(struct console *co, char *options) @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:706 @ static int univ8250_console_match(struct static struct console univ8250_console = { .name = "ttyS", - .write = univ8250_console_write, + .write_atomic = univ8250_console_write_atomic, + .write_thread = univ8250_console_write_thread, + .port_lock = univ8250_console_port_lock, .device = uart_console_device, .setup = univ8250_console_setup, .exit = univ8250_console_exit, .match = univ8250_console_match, - .flags = CON_PRINTBUFFER | CON_ANYTIME, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NO_BKL, .index = -1, .data = &serial8250_reg, }; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1001 @ static void serial_8250_overrun_backoff_ spin_lock_irqsave(&port->lock, flags); up->ier |= UART_IER_RLSI | UART_IER_RDI; up->port.read_status_mask |= UART_LSR_DR; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); spin_unlock_irqrestore(&port->lock, flags); } Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_exar.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_exar.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_exar.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:188 @ static void xr17v35x_set_divisor(struct static int xr17v35x_startup(struct uart_port *port) { + struct uart_8250_port *up = up_to_u8250p(port); + + spin_lock_irq(&port->lock); + /* * First enable access to IER [7:5], ISR [5:4], FCR [5:4], * MCR [7:5] and MSR [7:0] @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:202 @ static int xr17v35x_startup(struct uart_ * Make sure all interrups are masked until initialization is * complete and the FIFOs are cleared */ - serial_port_out(port, UART_IER, 0); + serial8250_set_IER(up, 0); + + spin_unlock_irq(&port->lock); return serial8250_do_startup(port); } Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_fsl.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_fsl.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_fsl.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:61 @ int fsl8250_handle_irq(struct uart_port if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { unsigned long delay; - up->ier = port->serial_in(port, UART_IER); + up->ier = serial8250_in_IER(up); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { port->ops->stop_rx(port); } else { Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_mtk.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_mtk.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_mtk.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:225 @ static void mtk8250_shutdown(struct uart static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) { - serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask)); + struct uart_port *port = &up->port; + bool is_console; + int ier; + + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, ier & (~mask)); + + if (is_console) + serial8250_exit_unsafe(up); } static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask) { - serial_out(up, UART_IER, serial_in(up, UART_IER) | mask); + struct uart_port *port = &up->port; + bool is_console; + int ier; + + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, ier | mask); + + if (is_console) + serial8250_exit_unsafe(up); } static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode) Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_omap.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_omap.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_omap.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:337 @ static void omap8250_restore_regs(struct /* drop TCR + TLR access, we setup XON/XOFF later */ serial8250_out_MCR(up, mcr); - - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_dl_write(up, priv->quot); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:525 @ static void omap_8250_pm(struct uart_por u8 efr; pm_runtime_get_sync(port->dev); + + spin_lock_irq(&port->lock); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); efr = serial_in(up, UART_EFR); serial_out(up, UART_EFR, efr | UART_EFR_ECB); serial_out(up, UART_LCR, 0); - serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial8250_set_IER(up, (state != 0) ? UART_IERX_SLEEP : 0); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, efr); serial_out(up, UART_LCR, 0); + spin_unlock_irq(&port->lock); + pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:656 @ static irqreturn_t omap8250_irq(int irq, if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) { unsigned long delay; - up->ier = port->serial_in(port, UART_IER); + spin_lock(&port->lock); + up->ier = serial8250_in_IER(up); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { port->ops->stop_rx(port); } else { @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:666 @ static irqreturn_t omap8250_irq(int irq, */ cancel_delayed_work(&up->overrun_backoff); } + spin_unlock(&port->lock); delay = msecs_to_jiffies(up->overrun_backoff_time_ms); schedule_delayed_work(&up->overrun_backoff, delay); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:716 @ static int omap_8250_startup(struct uart if (ret < 0) goto err; + spin_lock_irq(&port->lock); up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); + spin_unlock_irq(&port->lock); #ifdef CONFIG_PM up->capabilities |= UART_CAP_RPM; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:759 @ static void omap_8250_shutdown(struct ua if (priv->habit & UART_HAS_EFR2) serial_out(up, UART_OMAP_EFR2, 0x0); + spin_lock_irq(&port->lock); up->ier = 0; - serial_out(up, UART_IER, 0); + serial8250_set_IER(up, 0); + spin_unlock_irq(&port->lock); if (up->dma) serial8250_release_dma(up); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:810 @ static void omap_8250_unthrottle(struct up->dma->rx_dma(up); up->ier |= UART_IER_RLSI | UART_IER_RDI; port->read_status_mask |= UART_LSR_DR; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); spin_unlock_irqrestore(&port->lock, flags); pm_runtime_mark_last_busy(port->dev); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:969 @ static void __dma_rx_complete(void *para __dma_rx_do_complete(p); if (!priv->throttled) { p->ier |= UART_IER_RLSI | UART_IER_RDI; - serial_out(p, UART_IER, p->ier); + serial8250_set_IER(p, p->ier); if (!(priv->habit & UART_HAS_EFR2)) omap_8250_rx_dma(p); } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1026 @ static int omap_8250_rx_dma(struct uart_ * callback to run. */ p->ier &= ~(UART_IER_RLSI | UART_IER_RDI); - serial_out(p, UART_IER, p->ier); + serial8250_set_IER(p, p->ier); } goto out; } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1239 @ static void am654_8250_handle_rx_dma(str * periodic timeouts, re-enable interrupts. */ up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); omap_8250_rx_dma_flush(up); serial_in(up, UART_IIR); serial_out(up, UART_OMAP_EFR2, 0x0); up->ier |= UART_IER_RLSI | UART_IER_RDI; - serial_out(up, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); } } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1730 @ static int omap8250_runtime_resume(struc up = serial8250_get_port(priv->line); + spin_lock_irq(&up->port.lock); + if (omap8250_lost_context(up)) omap8250_restore_regs(up); if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) omap_8250_rx_dma(up); + spin_unlock_irq(&up->port.lock); + priv->latency = priv->calc_latency; schedule_work(&priv->qos_work); return 0; Index: linux-6.3.0-rt11/drivers/tty/serial/8250/8250_port.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/8250_port.c +++ linux-6.3.0-rt11/drivers/tty/serial/8250/8250_port.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:747 @ static void serial8250_set_sleep(struct serial8250_rpm_get(p); if (p->capabilities & UART_CAP_SLEEP) { + spin_lock_irq(&p->port.lock); if (p->capabilities & UART_CAP_EFR) { lcr = serial_in(p, UART_LCR); efr = serial_in(p, UART_EFR); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:755 @ static void serial8250_set_sleep(struct serial_out(p, UART_EFR, UART_EFR_ECB); serial_out(p, UART_LCR, 0); } - serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); + serial8250_set_IER(p, sleep ? UART_IERX_SLEEP : 0); if (p->capabilities & UART_CAP_EFR) { serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(p, UART_EFR, efr); serial_out(p, UART_LCR, lcr); } + spin_unlock_irq(&p->port.lock); } serial8250_rpm_put(p); } -static void serial8250_clear_IER(struct uart_8250_port *up) -{ - if (up->capabilities & UART_CAP_UUE) - serial_out(up, UART_IER, UART_IER_UUE); - else - serial_out(up, UART_IER, 0); -} - #ifdef CONFIG_SERIAL_8250_RSA /* * Attempts to turn on the RSA FIFO. Returns zero on failure. @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1030 @ static int broken_efr(struct uart_8250_p */ static void autoconfig_16550a(struct uart_8250_port *up) { + struct uart_port *port = &up->port; unsigned char status1, status2; unsigned int iersave; + bool is_console; up->port.type = PORT_16550A; up->capabilities |= UART_CAP_FIFO; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1149 @ static void autoconfig_16550a(struct uar return; } + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + /* * Try writing and reading the UART_IER_UUE bit (b6). * If it works, this is probably one of the Xscale platform's @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1189 @ static void autoconfig_16550a(struct uar } serial_out(up, UART_IER, iersave); + if (is_console) + serial8250_exit_unsafe(up); + /* * We distinguish between 16550A and U6 16550A by counting * how many bytes are in the FIFO. @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1233 @ static void autoconfig(struct uart_8250_ up->bugs = 0; if (!(port->flags & UPF_BUGGY_UART)) { + bool is_console; + + is_console = serial8250_is_console(port); + + if (is_console) + serial8250_enter_unsafe(up); + /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1269 @ static void autoconfig(struct uart_8250_ #endif scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; serial_out(up, UART_IER, scratch); + + if (is_console) + serial8250_exit_unsafe(up); + if (scratch2 != 0 || scratch3 != UART_IER_ALL_INTR) { /* * We failed; there's nothing here @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1394 @ static void autoconfig_irq(struct uart_8 unsigned char save_ICP = 0; unsigned int ICP = 0; unsigned long irqs; + bool is_console; int irq; if (port->flags & UPF_FOURPORT) { @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1404 @ static void autoconfig_irq(struct uart_8 inb_p(ICP); } - if (uart_console(port)) + is_console = serial8250_is_console(port); + + if (is_console) { console_lock(); + serial8250_enter_unsafe(up); + } /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1441 @ static void autoconfig_irq(struct uart_8 if (port->flags & UPF_FOURPORT) outb_p(save_ICP, ICP); - if (uart_console(port)) + if (is_console) { + serial8250_exit_unsafe(up); console_unlock(); + } port->irq = (irq > 0) ? irq : 0; } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1457 @ static void serial8250_stop_rx(struct ua up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); up->port.read_status_mask &= ~UART_LSR_DR; - serial_port_out(port, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); serial8250_rpm_put(up); } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1487 @ void serial8250_em485_stop_tx(struct uar serial8250_clear_and_reinit_fifos(p); p->ier |= UART_IER_RLSI | UART_IER_RDI; - serial_port_out(&p->port, UART_IER, p->ier); + serial8250_set_IER(p, p->ier); } } EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1734 @ static void serial8250_disable_ms(struct mctrl_gpio_disable_ms(up->gpios); up->ier &= ~UART_IER_MSI; - serial_port_out(port, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); } static void serial8250_enable_ms(struct uart_port *port) @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:1750 @ static void serial8250_enable_ms(struct up->ier |= UART_IER_MSI; serial8250_rpm_get(up); - serial_port_out(port, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); serial8250_rpm_put(up); } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2196 @ static void serial8250_put_poll_char(str serial8250_rpm_get(up); /* * First save the IER then disable the interrupts + * + * Best-effort IER access because other CPUs are quiesced. */ - ier = serial_port_in(port, UART_IER); - serial8250_clear_IER(up); + __serial8250_clear_IER(up, NULL, &ier); wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); /* @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2212 @ static void serial8250_put_poll_char(str * and restore the IER */ wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); - serial_port_out(port, UART_IER, ier); + __serial8250_set_IER(up, NULL, ier); serial8250_rpm_put(up); } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2223 @ int serial8250_do_startup(struct uart_po struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; unsigned char iir; + bool is_console; int retval; u16 lsr; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2241 @ int serial8250_do_startup(struct uart_po serial8250_rpm_get(up); if (port->type == PORT_16C950) { /* Wake up and initialize UART */ + spin_lock_irqsave(&port->lock, flags); up->acr = 0; serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); serial_port_out(port, UART_EFR, UART_EFR_ECB); - serial_port_out(port, UART_IER, 0); + serial8250_set_IER(up, 0); serial_port_out(port, UART_LCR, 0); serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); serial_port_out(port, UART_EFR, UART_EFR_ECB); serial_port_out(port, UART_LCR, 0); + spin_unlock_irqrestore(&port->lock, flags); } if (port->type == PORT_DA830) { /* Reset the port */ - serial_port_out(port, UART_IER, 0); + spin_lock_irqsave(&port->lock, flags); + serial8250_set_IER(up, 0); serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); + spin_unlock_irqrestore(&port->lock, flags); mdelay(10); /* Enable Tx, Rx and free run mode */ @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2357 @ int serial8250_do_startup(struct uart_po if (retval) goto out; + is_console = serial8250_is_console(port); + if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { unsigned char iir1; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2375 @ int serial8250_do_startup(struct uart_po */ spin_lock_irqsave(&port->lock, flags); + if (is_console) + serial8250_enter_unsafe(up); + wait_for_xmitr(up, UART_LSR_THRE); serial_port_out_sync(port, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2388 @ int serial8250_do_startup(struct uart_po iir = serial_port_in(port, UART_IIR); serial_port_out(port, UART_IER, 0); + if (is_console) + serial8250_exit_unsafe(up); + spin_unlock_irqrestore(&port->lock, flags); if (port->irqflags & IRQF_SHARED) @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2445 @ int serial8250_do_startup(struct uart_po * Do a quick test to see if we receive an interrupt when we enable * the TX irq. */ + if (is_console) + serial8250_enter_unsafe(up); serial_port_out(port, UART_IER, UART_IER_THRI); lsr = serial_port_in(port, UART_LSR); iir = serial_port_in(port, UART_IIR); serial_port_out(port, UART_IER, 0); + if (is_console) + serial8250_exit_unsafe(up); if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { if (!(up->bugs & UART_BUG_TXEN)) { @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2484 @ dont_test_tx_en: if (up->dma) { const char *msg = NULL; - if (uart_console(port)) + if (is_console) msg = "forbid DMA for kernel console"; else if (serial8250_request_dma(up)) msg = "failed to request DMA"; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2535 @ void serial8250_do_shutdown(struct uart_ */ spin_lock_irqsave(&port->lock, flags); up->ier = 0; - serial_port_out(port, UART_IER, 0); + serial8250_set_IER(up, 0); spin_unlock_irqrestore(&port->lock, flags); synchronize_irq(port->irq); @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2901 @ serial8250_do_set_termios(struct uart_po if (up->capabilities & UART_CAP_RTOIE) up->ier |= UART_IER_RTOIE; - serial_port_out(port, UART_IER, up->ier); + serial8250_set_IER(up, up->ier); if (up->capabilities & UART_CAP_EFR) { unsigned char efr = 0; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3366 @ EXPORT_SYMBOL_GPL(serial8250_set_default #ifdef CONFIG_SERIAL_8250_CONSOLE -static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) +static bool serial8250_console_putchar(struct uart_port *port, unsigned char ch, + struct cons_write_context *wctxt) { struct uart_8250_port *up = up_to_u8250p(port); wait_for_xmitr(up, UART_LSR_THRE); + if (!console_can_proceed(wctxt)) + return false; serial_port_out(port, UART_TX, ch); + if (ch == '\n') + up->console_newline_needed = false; + else + up->console_newline_needed = true; + + return true; } /* @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3409 @ static void serial8250_console_restore(s serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); } -/* - * Print a string to the serial port using the device FIFO - * - * It sends fifosize bytes and then waits for the fifo - * to get empty. - */ -static void serial8250_console_fifo_write(struct uart_8250_port *up, - const char *s, unsigned int count) +static bool __serial8250_console_write(struct uart_port *port, struct cons_write_context *wctxt, + const char *s, unsigned int count, + bool (*putchar)(struct uart_port *, unsigned char, struct cons_write_context *)) +{ + bool finished = false; + unsigned int i; + + for (i = 0; i < count; i++, s++) { + if (*s == '\n') { + if (!putchar(port, '\r', wctxt)) + goto out; + } + if (!putchar(port, *s, wctxt)) + goto out; + } + finished = true; +out: + return finished; +} + +static bool serial8250_console_write(struct uart_port *port, struct cons_write_context *wctxt, + const char *s, unsigned int count, + bool (*putchar)(struct uart_port *, unsigned char, struct cons_write_context *)) { - int i; - const char *end = s + count; - unsigned int fifosize = up->tx_loadsz; - bool cr_sent = false; - - while (s != end) { - wait_for_lsr(up, UART_LSR_THRE); - - for (i = 0; i < fifosize && s != end; ++i) { - if (*s == '\n' && !cr_sent) { - serial_out(up, UART_TX, '\r'); - cr_sent = true; - } else { - serial_out(up, UART_TX, *s++); - cr_sent = false; - } + return __serial8250_console_write(port, wctxt, s, count, putchar); +} + +static bool atomic_print_line(struct uart_8250_port *up, + struct cons_write_context *wctxt) +{ + struct uart_port *port = &up->port; + + if (up->console_newline_needed && + !__serial8250_console_write(port, wctxt, "\n", 1, serial8250_console_putchar)) { + return false; + } + + return __serial8250_console_write(port, wctxt, wctxt->outbuf, wctxt->len, + serial8250_console_putchar); +} + +static void atomic_console_reacquire(struct cons_write_context *wctxt, + struct cons_write_context *wctxt_init) +{ + memcpy(wctxt, wctxt_init, sizeof(*wctxt)); + while (!console_try_acquire(wctxt)) { + cpu_relax(); + memcpy(wctxt, wctxt_init, sizeof(*wctxt)); + } +} + +bool serial8250_console_write_atomic(struct uart_8250_port *up, + struct cons_write_context *wctxt) +{ + struct cons_write_context wctxt_init = { }; + struct cons_context *ctxt_init = &ACCESS_PRIVATE(&wctxt_init, ctxt); + struct cons_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + bool finished = false; + unsigned int ier; + + touch_nmi_watchdog(); + + /* With write_atomic, another context may hold the port->lock. */ + + ctxt_init->console = ctxt->console; + ctxt_init->prio = ctxt->prio; + ctxt_init->thread = ctxt->thread; + + /* + * Enter unsafe in order to disable interrupts. If the console is + * lost before the interrupts are disabled, bail out because another + * context took over the printing. If the console is lost after the + * interrutps are disabled, the console must be reacquired in order + * to re-enable the interrupts. However in that case no printing is + * allowed because another context took over the printing. + */ + + if (!console_enter_unsafe(wctxt)) + return false; + + if (!__serial8250_clear_IER(up, wctxt, &ier)) + return false; + + if (!console_exit_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } + + if (!atomic_print_line(up, wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } + + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + finished = true; +enable_irq: + /* + * Enter unsafe in order to enable interrupts. If the console is + * lost before the interrupts are enabled, the console must be + * reacquired in order to re-enable the interrupts. + */ + for (;;) { + if (console_enter_unsafe(wctxt) && + __serial8250_set_IER(up, wctxt, ier)) { + break; } + + /* HW-IRQs still disabled. Reacquire to enable them. */ + atomic_console_reacquire(wctxt, &wctxt_init); } + console_exit_unsafe(wctxt); + + return finished; } /* @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3533 @ static void serial8250_console_fifo_writ * Doing runtime PM is really a bad idea for the kernel console. * Thus, we assume the function is called when device is powered up. */ -void serial8250_console_write(struct uart_8250_port *up, const char *s, - unsigned int count) +bool serial8250_console_write_thread(struct uart_8250_port *up, + struct cons_write_context *wctxt) { + struct cons_write_context wctxt_init = { }; + struct cons_context *ctxt_init = &ACCESS_PRIVATE(&wctxt_init, ctxt); + struct cons_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); struct uart_8250_em485 *em485 = up->em485; struct uart_port *port = &up->port; - unsigned long flags; - unsigned int ier, use_fifo; - int locked = 1; - - touch_nmi_watchdog(); + unsigned int count = wctxt->len; + const char *s = wctxt->outbuf; + bool rs485_started = false; + bool finished = false; + unsigned int ier; - if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); + ctxt_init->console = ctxt->console; + ctxt_init->prio = ctxt->prio; + ctxt_init->thread = ctxt->thread; /* - * First save the IER then disable the interrupts + * Enter unsafe in order to disable interrupts. If the console is + * lost before the interrupts are disabled, bail out because another + * context took over the printing. If the console is lost after the + * interrutps are disabled, the console must be reacquired in order + * to re-enable the interrupts. However in that case no printing is + * allowed because another context took over the printing. */ - ier = serial_port_in(port, UART_IER); - serial8250_clear_IER(up); + + if (!console_enter_unsafe(wctxt)) + return false; + + if (!__serial8250_clear_IER(up, wctxt, &ier)) + return false; + + if (!console_exit_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } /* check scratch reg to see if port powered off during system sleep */ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { + if (!console_enter_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } serial8250_console_restore(up); + if (!console_exit_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } up->canary = 0; } if (em485) { - if (em485->tx_stopped) + if (em485->tx_stopped) { + if (!console_enter_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } up->rs485_start_tx(up); - mdelay(port->rs485.delay_rts_before_send); + rs485_started = true; + if (!console_exit_unsafe(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } + } + if (port->rs485.delay_rts_before_send) { + mdelay(port->rs485.delay_rts_before_send); + if (!console_can_proceed(wctxt)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } + } } - use_fifo = (up->capabilities & UART_CAP_FIFO) && - /* - * BCM283x requires to check the fifo - * after each byte. - */ - !(up->capabilities & UART_CAP_MINI) && - /* - * tx_loadsz contains the transmit fifo size - */ - up->tx_loadsz > 1 && - (up->fcr & UART_FCR_ENABLE_FIFO) && - port->state && - test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) && - /* - * After we put a data in the fifo, the controller will send - * it regardless of the CTS state. Therefore, only use fifo - * if we don't use control flow. - */ - !(up->port.flags & UPF_CONS_FLOW); - - if (likely(use_fifo)) - serial8250_console_fifo_write(up, s, count); - else - uart_console_write(port, s, count, serial8250_console_putchar); + if (!serial8250_console_write(port, wctxt, s, count, serial8250_console_putchar)) { + atomic_console_reacquire(wctxt, &wctxt_init); + goto enable_irq; + } + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + finished = true; +enable_irq: /* - * Finally, wait for transmitter to become empty - * and restore the IER + * Enter unsafe in order to stop rs485_tx. If the console is + * lost before the rs485_tx is stopped, the console must be + * reacquired in order to stop rs485_tx. */ - wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); - if (em485) { mdelay(port->rs485.delay_rts_after_send); - if (em485->tx_stopped) + if (em485->tx_stopped && rs485_started) { + while (!console_enter_unsafe(wctxt)) + atomic_console_reacquire(wctxt, &wctxt_init); up->rs485_stop_tx(up); + if (!console_exit_unsafe(wctxt)) + atomic_console_reacquire(wctxt, &wctxt_init); + } } - serial_port_out(port, UART_IER, ier); + /* + * Enter unsafe in order to enable interrupts. If the console is + * lost before the interrupts are enabled, the console must be + * reacquired in order to re-enable the interrupts. + */ + for (;;) { + if (console_enter_unsafe(wctxt) && + __serial8250_set_IER(up, wctxt, ier)) { + break; + } + atomic_console_reacquire(wctxt, &wctxt_init); + } /* * The receive handling will happen properly because the @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3654 @ void serial8250_console_write(struct uar if (up->msr_saved_flags) serial8250_modem_status(up); - if (locked) - spin_unlock_irqrestore(&port->lock, flags); + console_exit_unsafe(wctxt); + + return finished; } static unsigned int probe_baud(struct uart_port *port) @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3676 @ static unsigned int probe_baud(struct ua int serial8250_console_setup(struct uart_port *port, char *options, bool probe) { + struct uart_8250_port *up = up_to_u8250p(port); int baud = 9600; int bits = 8; int parity = 'n'; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:3686 @ int serial8250_console_setup(struct uart if (!port->iobase && !port->membase) return -ENODEV; + up->console_newline_needed = false; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else if (probe) Index: linux-6.3.0-rt11/drivers/tty/serial/8250/Kconfig =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/8250/Kconfig +++ linux-6.3.0-rt11/drivers/tty/serial/8250/Kconfig @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:12 @ config SERIAL_8250 depends on !S390 select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB + select HAVE_ATOMIC_CONSOLE help This selects whether you want to include the driver for the standard serial ports. The standard answer is Y. People who might say N Index: linux-6.3.0-rt11/drivers/tty/serial/serial_core.c =================================================================== --- linux-6.3.0-rt11.orig/drivers/tty/serial/serial_core.c +++ linux-6.3.0-rt11/drivers/tty/serial/serial_core.c @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2339 @ int uart_suspend_port(struct uart_driver * able to Re-start_rx later. */ if (!console_suspend_enabled && uart_console(uport)) { - if (uport->ops->start_rx) + if (uport->ops->start_rx) { + spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); + spin_unlock_irq(&uport->lock); + } goto unlock; } @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:2436 @ int uart_resume_port(struct uart_driver if (console_suspend_enabled) uart_change_pm(state, UART_PM_STATE_ON); uport->ops->set_termios(uport, &termios, NULL); - if (!console_suspend_enabled && uport->ops->start_rx) + if (!console_suspend_enabled && uport->ops->start_rx) { + spin_lock_irq(&uport->lock); uport->ops->start_rx(uport); + spin_unlock_irq(&uport->lock); + } if (console_suspend_enabled) console_start(uport->cons); } Index: linux-6.3.0-rt11/include/linux/serial_8250.h =================================================================== --- linux-6.3.0-rt11.orig/include/linux/serial_8250.h +++ linux-6.3.0-rt11/include/linux/serial_8250.h @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:128 @ struct uart_8250_port { #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; + bool console_newline_needed; + struct uart_8250_dma *dma; const struct uart_8250_ops *ops; @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:144 @ struct uart_8250_port { /* Serial port overrun backoff */ struct delayed_work overrun_backoff; u32 overrun_backoff_time_ms; + + struct cons_write_context wctxt; + int cookie; }; static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up) @ linux-6.3.0-rt11/drivers/tty/serial/8250/8250.h:186 @ void serial8250_tx_chars(struct uart_825 unsigned int serial8250_modem_status(struct uart_8250_port *up); void serial8250_init_port(struct uart_8250_port *up); void serial8250_set_defaults(struct uart_8250_port *up); -void serial8250_console_write(struct uart_8250_port *up, const char *s, - unsigned int count); +bool serial8250_console_write_atomic(struct uart_8250_port *up, + struct cons_write_context *wctxt); +bool serial8250_console_write_thread(struct uart_8250_port *up, + struct cons_write_context *wctxt); int serial8250_console_setup(struct uart_port *port, char *options, bool probe); int serial8250_console_exit(struct uart_port *port);