uart_flush_buffer() resets the transmit FIFO before calling the driver's
flush callback. An asynchronous DMA transfer can still access that storage
after the callback returns, while a new write can immediately reuse it.

Add uart_xmit_block() and uart_xmit_unblock() so a driver can reserve the
flushed storage until its transfer and completion callbacks have retired.
Serialize the reservation with FIFO producers using the port lock.

While the storage is reserved, write_room() reports no space and write()
and put_char() accept no data. Defer write-ready notifications until the
reservation is released, preserving one-shot TTY wakeups. Keep the flush
wakeup under the port lock so it cannot race another reservation.

Suppress ordinary TX starts while the FIFO storage is blocked. Preserve
high-priority flow-control characters, which use separate storage.
Releasing the reservation wakes writers through the existing TTY wakeup
path. This preserves immediate logical flushing while applying write
backpressure until the storage is reusable.

Track port readiness separately from DMA ownership. Retirement releases the
reservation even during startup or shutdown, while writes and wakeups stay
gated until successful startup or resume. Reopening or restarting a port
wakes deferred writers with either a new or retained FIFO. Restart queued
output when reusing a FIFO after suspension. Keep its write-ready
notification pending until the retained output drains below the normal
wakeup threshold.

Publish resume readiness under the same port lock as the TX restart,
without clearing any reservation. A concurrent flush can then reserve a
new transfer without resume releasing its storage prematurely.

Signed-off-by: James Hilliard <[email protected]>
---
 drivers/tty/serial/serial_core.c | 100 +++++++++++++++++++++++++++++++++++----
 include/linux/serial_core.h      |  18 ++++++-
 2 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index c0deec0e6af4..30358fe8c341 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -100,6 +100,11 @@ static inline struct uart_port *uart_port_check(struct 
uart_state *state)
        return state->uart_port;
 }
 
+static bool uart_xmit_writable(const struct uart_state *state)
+{
+       return state->xmit_ready && !state->xmit_blocked;
+}
+
 /**
  * uart_write_wakeup - schedule write processing
  * @port: port to be processed
@@ -119,10 +124,71 @@ void uart_write_wakeup(struct uart_port *port)
         * closed.  No cookie for you.
         */
        BUG_ON(!state);
+       if (!uart_xmit_writable(state))
+               return;
        tty_port_tty_wakeup(&state->port);
 }
 EXPORT_SYMBOL(uart_write_wakeup);
 
+/* Publish readiness without releasing any transfer's storage reservation. */
+static void uart_xmit_enable(struct uart_port *port)
+{
+       struct uart_state *state = port->state;
+
+       lockdep_assert_held_once(&port->lock);
+       if (state->xmit_ready)
+               return;
+       state->xmit_ready = true;
+       /* Retained output must drain before consuming a write-ready 
notification. */
+       if (kfifo_len(&state->port.xmit_fifo) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+}
+
+/**
+ * uart_xmit_block - reserve flushed transmit storage
+ * @port: port whose transmit storage is still in use
+ *
+ * A driver may call this from ->flush_buffer() if DMA still owns the old
+ * transmit data. The logical FIFO has already been emptied, but writers
+ * must not overwrite its storage until uart_xmit_unblock() is called.
+ * write_room() reports zero and write()/put_char() accept no data meanwhile.
+ * Write wakeups are deferred until the reservation is released.
+ *
+ * The driver must unblock after retiring the transfer, including on shutdown.
+ * Serial core separately gates writes and wakeups until startup or resume
+ * has made the port ready.
+ * Repeated calls are idempotent, not nested reservations.
+ *
+ * Context: @port->lock held.
+ */
+void uart_xmit_block(struct uart_port *port)
+{
+       lockdep_assert_held_once(&port->lock);
+       port->state->xmit_blocked = true;
+}
+EXPORT_SYMBOL_GPL(uart_xmit_block);
+
+/**
+ * uart_xmit_unblock - release reserved transmit storage and wake writers
+ * @port: port whose storage is available again
+ *
+ * The old transfer must no longer access the FIFO storage, and any required
+ * DMA synchronization must have completed before this call.
+ * Retirement is recorded even while the port is inactive. Writes and wakeups
+ * remain deferred until startup or resume has made the port ready.
+ *
+ * Context: @port->lock held.
+ */
+void uart_xmit_unblock(struct uart_port *port)
+{
+       lockdep_assert_held_once(&port->lock);
+       if (!port->state->xmit_blocked)
+               return;
+       port->state->xmit_blocked = false;
+       uart_write_wakeup(port);
+}
+EXPORT_SYMBOL_GPL(uart_xmit_unblock);
+
 static void uart_stop(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
@@ -141,7 +207,10 @@ static void __uart_start(struct uart_state *state)
        struct serial_port_device *port_dev;
        int err;
 
-       if (!port || port->flags & UPF_DEAD || uart_tx_stopped(port))
+       if (!port || port->flags & UPF_DEAD || uart_tx_stopped(port) || 
!state->xmit_ready)
+               return;
+       /* High-priority flow-control characters have their own storage. */
+       if (state->xmit_blocked && !port->x_char)
                return;
 
        port_dev = port->port_dev;
@@ -375,6 +444,11 @@ static int uart_startup(struct tty_struct *tty, struct 
uart_state *state,
                return -EIO;
 
        serial_base_port_startup(uport);
+       scoped_guard(uart_port_lock_irq, uport) {
+               uart_xmit_enable(uport);
+               if (!kfifo_is_empty(&port->xmit_fifo) || uport->x_char)
+                       __uart_start(state);
+       }
 
        return 0;
 }
@@ -397,8 +471,11 @@ static void uart_shutdown(struct tty_struct *tty, struct 
uart_state *state)
        if (tty)
                set_bit(TTY_IO_ERROR, &tty->flags);
 
-       if (uport)
+       if (uport) {
+               scoped_guard(uart_port_lock_irq, uport)
+                       state->xmit_ready = false;
                serial_base_port_shutdown(uport);
+       }
 
        if (tty_port_initialized(port)) {
                tty_port_set_initialized(port, false);
@@ -593,7 +670,7 @@ static int uart_put_char(struct tty_struct *tty, u8 c)
                return 0;
        }
 
-       if (port)
+       if (port && uart_xmit_writable(state))
                ret = kfifo_put(&state->port.xmit_fifo, c);
        uart_port_unlock_deref(port, flags);
        return ret;
@@ -624,7 +701,7 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 
*buf, size_t count)
                return 0;
        }
 
-       if (port)
+       if (port && uart_xmit_writable(state))
                ret = kfifo_in(&state->port.xmit_fifo, buf, count);
 
        __uart_start(state);
@@ -640,7 +717,7 @@ static unsigned int uart_write_room(struct tty_struct *tty)
        unsigned int ret;
 
        port = uart_port_ref_lock(state, &flags);
-       if (!state->port.xmit_buf)
+       if (!state->port.xmit_buf || !uart_xmit_writable(state))
                ret = 0;
        else
                ret = kfifo_avail(&state->port.xmit_fifo);
@@ -682,8 +759,9 @@ static void uart_flush_buffer(struct tty_struct *tty)
        kfifo_reset(&state->port.xmit_fifo);
        if (port->ops->flush_buffer)
                port->ops->flush_buffer(port);
+       /* Serialize the wakeup with reservation changes, including another 
flush. */
+       uart_write_wakeup(port);
        uart_port_unlock_deref(port, flags);
-       tty_port_tty_wakeup(&state->port);
 }
 
 /*
@@ -1748,8 +1826,10 @@ static void uart_tty_port_shutdown(struct tty_port *port)
        if (WARN(!uport, "detached port still initialized!\n"))
                return;
 
-       scoped_guard(uart_port_lock_irq, uport)
+       scoped_guard(uart_port_lock_irq, uport) {
+               state->xmit_ready = false;
                uport->ops->stop_rx(uport);
+       }
 
        serial_base_port_shutdown(uport);
        uart_port_shutdown(port);
@@ -2391,9 +2471,10 @@ int uart_suspend_port(struct uart_driver *drv, struct 
uart_port *uport)
                unsigned int mctrl;
 
                tty_port_set_suspended(port, true);
-               tty_port_set_initialized(port, false);
 
                scoped_guard(uart_port_lock_irq, uport) {
+                       state->xmit_ready = false;
+                       tty_port_set_initialized(port, false);
                        ops->stop_tx(uport);
                        if (!(uport->rs485.flags & SER_RS485_ENABLED))
                                ops->set_mctrl(uport, 0);
@@ -2498,8 +2579,9 @@ int uart_resume_port(struct uart_driver *drv, struct 
uart_port *uport)
                                        if (!(uport->rs485.flags & 
SER_RS485_ENABLED))
                                                ops->set_mctrl(uport, 
uport->mctrl);
                                        ops->start_tx(uport);
+                                       tty_port_set_initialized(port, true);
+                                       uart_xmit_enable(uport);
                                }
-                               tty_port_set_initialized(port, true);
                        } else {
                                /*
                                 * Failed to resume - maybe hardware went away?
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 4eeb8253752a..41598a38a408 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -208,8 +208,17 @@ struct gpio_desc;
  *     Flush any write buffers, reset any DMA state and stop any ongoing DMA
  *     transfers.
  *
- *     This will be called whenever the @port->state->xmit circular buffer is
- *     cleared.
+ *     This is called after the transmit FIFO has been cleared, with 
@port->lock
+ *     still held. A driver whose asynchronous transfer can still access the
+ *     flushed storage must call uart_xmit_block() before returning. This keeps
+ *     write_room() at zero and prevents write() and put_char() from reusing 
the
+ *     storage until the driver calls uart_xmit_unblock().
+ *
+ *     Release the reservation only after the transfer and its callbacks have
+ *     retired and any required DMA synchronization has completed. Shutdown 
must
+ *     also retire the transfer and release the reservation before returning,
+ *     since serial core then frees the transmit buffer. Serial core separately
+ *     gates writes and wakeups until startup or resume has made the port 
ready.
  *
  *     Locking: @port->lock taken.
  *     Interrupts: locally disabled.
@@ -834,6 +843,9 @@ enum uart_pm_state {
  */
 struct uart_state {
        struct tty_port         port;
+       /* TX readiness and storage ownership; protected by the UART port lock. 
*/
+       bool                    xmit_ready;
+       bool                    xmit_blocked;
 
        enum uart_pm_state      pm_state;
 
@@ -909,6 +921,8 @@ struct uart_driver {
 };
 
 void uart_write_wakeup(struct uart_port *port);
+void uart_xmit_block(struct uart_port *port);
+void uart_xmit_unblock(struct uart_port *port);
 
 /**
  * enum UART_TX_FLAGS -- flags for uart_port_tx_flags()

-- 
2.53.0


Reply via email to