From: tianci xie <xiex16070@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jirislaby@kernel.org>
Cc: Krzysztof Kozlowski <krzk@kernel.org>,
Peter Griffin <peter.griffin@linaro.org>,
Alim Akhtar <alim.akhtar@samsung.com>,
Marek Szyprowski <m.szyprowski@samsung.com>,
Samarth Narang <samarth.colleges@gmail.com>,
linux-serial@vger.kernel.org, linux-samsung-soc@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, tianci xie <xiex16070@gmail.com>
Subject: [PATCH] tty: serial: samsung: fix TX fifo underflow on flush race
Date: Sat, 31 Oct 2026 23:26:56 +0800 [thread overview]
Message-ID: <20261031152656.1422-2-xiex16070@gmail.com> (raw)
In-Reply-To: <20261031152656.1422-1-xiex16070@gmail.com>
uart_flush_buffer() resets the xmit kfifo and then calls ->flush_buffer()
with the port lock held. Neither s3c64xx_serial_ops nor
apple_s5l_serial_ops implements that callback, and
s3c24xx_serial_tx_dma_complete() computes the transferred byte count from
dma->tx_bytes_requested before it takes the port lock.
A completion that has already sampled that count can therefore run after
a TCOFLUSH has reset the kfifo, and advance the freshly reset kfifo by
the byte count of the discarded transfer. kfifo_skip_count() does not
clamp, so ->out moves past ->in and kfifo_len() wraps to a huge value.
The driver then keeps transmitting stale buffer contents, and a later
close can wait in tty_wait_until_sent() until it times out.
Handle this the way 8250 does in 9e512eaaf8f4 ("serial: 8250: Fix fifo
underflow on flush"):
- add s3c24xx_serial_flush_buffer(), which clears the outstanding byte
count and terminates the TX channel asynchronously. The port lock is
held here, so dmaengine_terminate_sync() cannot be used;
- sample dma->tx_bytes_requested under the port lock, so that a
completion racing with the flush observes the cleared value and
advances by zero.
The callback may still run after dmaengine_terminate_async(); for the
fifo it is then a no-op, but it does clear tx_in_progress so that a new
transfer can be started. As in 8250, this assumes that a terminated
descriptor is retired before the next one is queued.
Reported-by: Samarth Narang <samarth.colleges@gmail.com>
Link: https://lore.kernel.org/all/20261031004733.3345841-1-samarth.colleges@gmail.com/
Fixes: 29bef7990845 ("serial: samsung: add DMA support for TX")
Signed-off-by: tianci xie <xiex16070@gmail.com>
---
drivers/tty/serial/samsung_tty.c | 40 +++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index 640f85e..e955b7e 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -330,7 +330,6 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
int count;
dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
- count = dma->tx_bytes_requested - state.residue;
async_tx_ack(dma->tx_desc);
dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->tx_chan),
@@ -339,6 +338,17 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
uart_port_lock_irqsave(port, &flags);
+ /*
+ * uart_flush_buffer() resets the xmit fifo and then calls
+ * ->flush_buffer(), which clears tx_bytes_requested. Sample the byte
+ * count under the port lock so that a completion which raced with a
+ * flush cannot advance the already reset fifo past its write index.
+ */
+ if (dma->tx_bytes_requested)
+ count = dma->tx_bytes_requested - state.residue;
+ else
+ count = 0;
+
uart_xmit_advance(port, count);
ourport->tx_in_progress = 0;
@@ -1162,6 +1172,32 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
}
}
+/*
+ * Called from uart_flush_buffer() with the port lock held, after the xmit fifo
+ * has already been reset. A TX DMA transfer still in flight carries data that
+ * no longer exists, so drop its byte count before terminating it: the
+ * completion callback may still run and must not advance the reset fifo.
+ */
+static void s3c24xx_serial_flush_buffer(struct uart_port *port)
+{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+
+ if (!dma || !dma->tx_chan || ourport->tx_in_progress != S3C24XX_TX_DMA)
+ return;
+
+ dma->tx_bytes_requested = 0;
+
+ /*
+ * dmaengine_terminate_sync() cannot be used here because the port
+ * lock is held. The callback may or may not run afterwards; either
+ * way it is a no-op for the fifo because tx_bytes_requested is zero.
+ */
+ dmaengine_terminate_async(dma->tx_chan);
+
+ ourport->tx_in_progress = 0;
+}
+
static void s3c64xx_serial_shutdown(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
@@ -1684,6 +1720,7 @@ static const struct uart_ops s3c64xx_serial_ops = {
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
+ .flush_buffer = s3c24xx_serial_flush_buffer,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c64xx_serial_startup,
.shutdown = s3c64xx_serial_shutdown,
@@ -1705,6 +1742,7 @@ static const struct uart_ops apple_s5l_serial_ops = {
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
+ .flush_buffer = s3c24xx_serial_flush_buffer,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = apple_s5l_serial_startup,
.shutdown = apple_s5l_serial_shutdown,
next prev parent reply other threads:[~2026-10-31 15:27 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 0:47 [BUG] serial: TX kfifo underflow when TCOFLUSH races with TX DMA (samsung, sprd, msm, pch_uart, tegra) Samarth Narang
2026-10-31 8:54 ` Greg Kroah-Hartman
2026-10-31 15:26 ` [PATCH 0/1] tty: serial: samsung: fix TX fifo underflow on flush race tianci xie
2026-10-31 15:26 ` tianci xie [this message]
2026-10-31 15:48 ` [PATCH] " sashiko-bot
2026-10-31 12:39 ` [PATCH 0/5] serial: fix TX kfifo underflow on flush in five drivers Samarth Narang
2026-10-31 12:39 ` [PATCH 1/5] tty: serial: samsung: fix TX kfifo underflow on flush Samarth Narang
2026-10-31 13:02 ` sashiko-bot
2026-10-31 12:39 ` [PATCH 2/5] serial: tegra: don't advance the TX kfifo for a flushed transfer Samarth Narang
2026-10-31 12:55 ` sashiko-bot
2026-10-31 12:39 ` [PATCH 3/5] serial: sprd: fix TX kfifo underflow on flush Samarth Narang
2026-10-31 12:58 ` sashiko-bot
2026-10-31 12:39 ` [PATCH 4/5] tty: serial: msm: " Samarth Narang
2026-10-31 12:55 ` sashiko-bot
2026-10-31 12:39 ` [PATCH 5/5] serial: pch_uart: " Samarth Narang
2026-10-31 12:55 ` sashiko-bot
2026-10-31 12:59 ` [PATCH 0/5] serial: fix TX kfifo underflow on flush in five drivers Greg Kroah-Hartman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261031152656.1422-2-xiex16070@gmail.com \
--to=xiex16070@gmail.com \
--cc=alim.akhtar@samsung.com \
--cc=gregkh@linuxfoundation.org \
--cc=jirislaby@kernel.org \
--cc=krzk@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-samsung-soc@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=m.szyprowski@samsung.com \
--cc=peter.griffin@linaro.org \
--cc=samarth.colleges@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox