]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blobdiff - queue-6.8/serial-core-fix-missing-shutdown-and-startup-for-serial-base-port.patch
6.8-stable patches
[thirdparty/kernel/stable-queue.git] / queue-6.8 / serial-core-fix-missing-shutdown-and-startup-for-serial-base-port.patch
diff --git a/queue-6.8/serial-core-fix-missing-shutdown-and-startup-for-serial-base-port.patch b/queue-6.8/serial-core-fix-missing-shutdown-and-startup-for-serial-base-port.patch
new file mode 100644 (file)
index 0000000..f83ac8f
--- /dev/null
@@ -0,0 +1,158 @@
+From 1aa4ad4eb695bac1b0a7ba542a16d6833c9c8dd8 Mon Sep 17 00:00:00 2001
+From: Tony Lindgren <tony@atomide.com>
+Date: Thu, 11 Apr 2024 08:58:45 +0300
+Subject: serial: core: Fix missing shutdown and startup for serial base port
+
+From: Tony Lindgren <tony@atomide.com>
+
+commit 1aa4ad4eb695bac1b0a7ba542a16d6833c9c8dd8 upstream.
+
+We are seeing start_tx being called after port shutdown as noted by Jiri.
+This happens because we are missing the startup and shutdown related
+functions for the serial base port.
+
+Let's fix the issue by adding startup and shutdown functions for the
+serial base port to block tx flushing for the serial base port when the
+port is not in use.
+
+Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM")
+Cc: stable <stable@kernel.org>
+Reported-by: Jiri Slaby <jirislaby@kernel.org>
+Signed-off-by: Tony Lindgren <tony@atomide.com>
+Link: https://lore.kernel.org/r/20240411055848.38190-1-tony@atomide.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/serial_base.h |    4 ++++
+ drivers/tty/serial/serial_core.c |   20 +++++++++++++++++---
+ drivers/tty/serial/serial_port.c |   34 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 55 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/serial_base.h
++++ b/drivers/tty/serial/serial_base.h
+@@ -22,6 +22,7 @@ struct serial_ctrl_device {
+ struct serial_port_device {
+       struct device dev;
+       struct uart_port *port;
++      unsigned int tx_enabled:1;
+ };
+ int serial_base_ctrl_init(void);
+@@ -30,6 +31,9 @@ void serial_base_ctrl_exit(void);
+ int serial_base_port_init(void);
+ void serial_base_port_exit(void);
++void serial_base_port_startup(struct uart_port *port);
++void serial_base_port_shutdown(struct uart_port *port);
++
+ int serial_base_driver_register(struct device_driver *driver);
+ void serial_base_driver_unregister(struct device_driver *driver);
+--- a/drivers/tty/serial/serial_core.c
++++ b/drivers/tty/serial/serial_core.c
+@@ -323,16 +323,26 @@ static int uart_startup(struct tty_struc
+                       bool init_hw)
+ {
+       struct tty_port *port = &state->port;
++      struct uart_port *uport;
+       int retval;
+       if (tty_port_initialized(port))
+-              return 0;
++              goto out_base_port_startup;
+       retval = uart_port_startup(tty, state, init_hw);
+-      if (retval)
++      if (retval) {
+               set_bit(TTY_IO_ERROR, &tty->flags);
++              return retval;
++      }
+-      return retval;
++out_base_port_startup:
++      uport = uart_port_check(state);
++      if (!uport)
++              return -EIO;
++
++      serial_base_port_startup(uport);
++
++      return 0;
+ }
+ /*
+@@ -355,6 +365,9 @@ static void uart_shutdown(struct tty_str
+       if (tty)
+               set_bit(TTY_IO_ERROR, &tty->flags);
++      if (uport)
++              serial_base_port_shutdown(uport);
++
+       if (tty_port_initialized(port)) {
+               tty_port_set_initialized(port, false);
+@@ -1775,6 +1788,7 @@ static void uart_tty_port_shutdown(struc
+       uport->ops->stop_rx(uport);
+       uart_port_unlock_irq(uport);
++      serial_base_port_shutdown(uport);
+       uart_port_shutdown(port);
+       /*
+--- a/drivers/tty/serial/serial_port.c
++++ b/drivers/tty/serial/serial_port.c
+@@ -36,8 +36,12 @@ static int serial_port_runtime_resume(st
+       /* Flush any pending TX for the port */
+       uart_port_lock_irqsave(port, &flags);
++      if (!port_dev->tx_enabled)
++              goto unlock;
+       if (__serial_port_busy(port))
+               port->ops->start_tx(port);
++
++unlock:
+       uart_port_unlock_irqrestore(port, flags);
+ out:
+@@ -57,6 +61,11 @@ static int serial_port_runtime_suspend(s
+               return 0;
+       uart_port_lock_irqsave(port, &flags);
++      if (!port_dev->tx_enabled) {
++              uart_port_unlock_irqrestore(port, flags);
++              return 0;
++      }
++
+       busy = __serial_port_busy(port);
+       if (busy)
+               port->ops->start_tx(port);
+@@ -68,6 +77,31 @@ static int serial_port_runtime_suspend(s
+       return busy ? -EBUSY : 0;
+ }
++static void serial_base_port_set_tx(struct uart_port *port,
++                                  struct serial_port_device *port_dev,
++                                  bool enabled)
++{
++      unsigned long flags;
++
++      uart_port_lock_irqsave(port, &flags);
++      port_dev->tx_enabled = enabled;
++      uart_port_unlock_irqrestore(port, flags);
++}
++
++void serial_base_port_startup(struct uart_port *port)
++{
++      struct serial_port_device *port_dev = port->port_dev;
++
++      serial_base_port_set_tx(port, port_dev, true);
++}
++
++void serial_base_port_shutdown(struct uart_port *port)
++{
++      struct serial_port_device *port_dev = port->port_dev;
++
++      serial_base_port_set_tx(port, port_dev, false);
++}
++
+ static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm,
+                                serial_port_runtime_suspend,
+                                serial_port_runtime_resume, NULL);