* [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse
@ 2026-10-24 6:40 James Hilliard
2026-10-24 6:40 ` [PATCH v5 1/5] serial: 8250_omap: separate locked TX DMA completion processing James Hilliard
` (4 more replies)
0 siblings, 5 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
Reserve canceled transmit and receive storage until DMA and its completion
callbacks have retired. Serial-core write backpressure protects a flushed
TX FIFO while normal transfers continue to DMA directly from that FIFO.
The series separates OMAP's locked completion processing and serializes
asynchronous write wakeups with the port lock before adding the serial-core
reservation helpers and applying them to 8250 TX. Flushing
empties the logical queue immediately. While the old transfer owns its
storage, write_room() reports zero and write-ready notifications wait for
retirement. Failed termination retains the reservation until normal
completion, without advancing the reset FIFO. DMA retirement releases the
reservation independently of port readiness. Serial core keeps writes and
wakeups gated during shutdown until successful startup or resume.
RX delivers paused data before the PIO fallback and retires callbacks in
process context. Track FIFO access separately from buffer retirement so
PIO and the DesignWare empty-FIFO timeout handling can run once DMA has
released the FIFO. On failure, retain the requested interrupt enables in
the existing IER shadow and mask receive interrupts in hardware until DMA
has stopped. Recovery then leaves RX on PIO until reopen. Shutdown excludes
new retirement work before releasing the channels.
Based on tty-next with the posted shutdown guard [0], channel
configuration error handling [1], RX TTY accounting fix [2], RZ/N1
transfer-length fix [3], transmit-PM shutdown fix [4], serport
write-wakeup deferral [5], DesignWare BUSY-safe fractional-divisor
programming [6], and Intel MID RX DMA timeout locking [7]. The serport fix
is needed before serializing the remaining write-wakeup callers with the
port lock.
[0] https://lore.kernel.org/r/20261021-submit-serial-8250-dma-shutdown-v1-v1-1-3d3ce383c614@gmail.com
[1] https://lore.kernel.org/r/20261021-submit-serial-8250-dma-config-errors-v1-v1-1-c067587d1ef2@gmail.com
[2] https://lore.kernel.org/r/20261021-submit-serial-8250-dma-rx-accounting-v1-v1-1-c11795914ab4@gmail.com
[3] https://lore.kernel.org/r/20261023-submit-serial-8250-dma-tx-length-v1-v2-1-c3106ef4d2df@gmail.com
[4] https://lore.kernel.org/r/20261022-submit-serial-8250-tx-pm-shutdown-v1-v1-1-0e894afa3bb8@gmail.com
[5] https://lore.kernel.org/r/20261023-submit-serport-write-wakeup-v1-v1-1-74e7311638a2@gmail.com
[6] https://lore.kernel.org/r/20261023-submit-serial-8250-dw-divisor-v1-v1-1-f452d4bb1b1f@gmail.com
[7] https://lore.kernel.org/r/20261024-submit-serial-8250-mid-rx-lock-v1-v1-1-2b9afdf869d7@gmail.com
Assisted-by: Codex:gpt-6-astra
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes in v5:
- Add the posted serport write-wakeup deferral as a prerequisite for the
caller-locking preparation.
- Add a preparatory patch serializing asynchronous write-wakeup callers
with the port lock, including indirect CTS and transmit-helper calls.
Keep sleeping register accesses outside the lock and defer SCCNXP
notifications until its controller lock is released.
- Use the separately posted DesignWare BUSY-safe fractional-divisor fix
and Intel MID RX DMA timeout locking fix as prerequisites, leaving five
patches in this series.
- Separate serial-core write readiness from DMA storage reservations. Record
retirement during startup and shutdown while keeping writes and wakeups
gated until the port is ready.
- Wake deferred writers after successful startup or resume, including when
reopening with a retained FIFO, and restart any retained queued output.
- Keep the write-ready notification pending while retained output is at or
above the normal wakeup threshold, until TX completion drains it.
- Publish resume readiness under the TX restart lock without releasing a
reservation that may belong to a newly flushed transfer.
- Release 8250's TX reservation after shutdown has stopped DMA and unmapped
the source; serial core retains control of write readiness.
- Link to v4: https://patch.msgid.link/20261023-submit-serial-8250-dma-cancel-v1-v4-0-e759de1839de@gmail.com
Changes in v4:
- Retain flushed TX reservations through shutdown and failed resume. Release
them after successful resume, or when allocating a new FIFO on reopen,
so retirement cannot wake writers into a closing port.
- Treat RX flush as complete once the channel is inactive, and clear the
shutdown guard before startup configures the UART and tests interrupts.
- Check completion after a failed pause so a transfer that finished before
its callback acquired the port lock is delivered and retired normally.
- Keep DesignWare's BUSY-safe divisor setter after fractional-divisor
discovery, writing DLF and DLL/DLH only after confirming DLAB.
- Link to v3: https://patch.msgid.link/20261023-submit-serial-8250-dma-cancel-v1-v3-0-d78f9e4c4932@gmail.com
Changes in v3:
- Defer write-ready wakeups while flushed TX storage is reserved, and
serialize the flush wakeup with the reservation under the port lock.
- Suppress ordinary TX starts during the reservation while preserving
high-priority flow-control characters, which use separate storage.
- Separate RX FIFO access from callback retirement, allowing PIO and the
DesignWare empty-FIFO timeout workaround after a successful pause and
termination while keeping the receive buffer reserved.
- Use the existing IER shadow for requested enables and a shared register
writer to keep receive interrupts masked throughout DMA error recovery,
including termios, RS485, modem-status and overrun-backoff updates.
- Preserve the timeout-enable bit across stop_rx(), exclude late shutdown
interrupts from PIO, and avoid FIFO resets during RX DMA recovery.
- Preserve the completion-path LSR read after stop_rx(), while gating DMA
resubmission on the receive interrupt enable.
- Pack the cancellation flags after moving all their accesses under the
port lock, including the recovery worker's snapshot and initialization.
- Apply the early-return, shared-helper and line-wrapping cleanups.
- Link to v2: https://patch.msgid.link/20261022-submit-serial-8250-dma-cancel-v1-v2-0-088ea51e3891@gmail.com
Changes in v2:
- Add serial-core helpers to reserve flushed transmit storage until DMA
retirement. Apply write backpressure during that interval and keep normal
TX using the FIFO directly, replacing the private transmit buffer in v1.
- Document the flush callback's reservation, writer backpressure and
retirement requirements, including release before shutdown returns.
- Split the OMAP completion refactor into a preparatory patch and move
the RZ/N1 preparation-order and shutdown PM fixes to independent
submissions, keeping four patches in this series.
- Take runtime PM references around worker UART accesses, and leave normal
RX retirement to release ownership without reading LSR or restarting DMA.
- Synchronize the queued TX ranges before preparing the descriptor and
return the mapping to CPU ownership before releasing the reservation.
- Keep the TX transfer length separate from FIFO cancellation, so a failed
termination can account completion without advancing the reset FIFO.
- Exclude late RX flush work during shutdown, including Intel MID DMA
timeout interrupts which are independent of UART_IER.
- Accept DMA_IN_PROGRESS after successful pause, and recover from failed
pause, termination or DMA_ERROR before handing the receiver to PIO.
- Preserve stop_rx() and shutdown across recovery, and retain the
DesignWare empty-FIFO timeout workaround when receiving through PIO.
- Apply the requested guard spacing and container_of() formatting.
- Link to v1: https://patch.msgid.link/20261021-submit-serial-8250-dma-cancel-v1-v1-0-efe2b89f2303@gmail.com
---
James Hilliard (5):
serial: 8250_omap: separate locked TX DMA completion processing
serial: serialize asynchronous write wakeups with the port lock
serial: core: allow drivers to reserve flushed transmit storage
serial: 8250: drain canceled TX DMA before reusing its buffer
serial: 8250: retire canceled RX DMA before reusing the receive buffer
drivers/tty/serial/21285.c | 2 +
drivers/tty/serial/8250/8250.h | 59 +++++++-
drivers/tty/serial/8250/8250_core.c | 2 +-
drivers/tty/serial/8250/8250_dma.c | 284 +++++++++++++++++++++++++++---------
drivers/tty/serial/8250/8250_dw.c | 27 +++-
drivers/tty/serial/8250/8250_omap.c | 38 +++--
drivers/tty/serial/8250/8250_port.c | 31 ++--
drivers/tty/serial/atmel_serial.c | 2 +
drivers/tty/serial/clps711x.c | 6 +-
drivers/tty/serial/cpm_uart.c | 2 +
drivers/tty/serial/fsl_lpuart.c | 2 +-
drivers/tty/serial/jsm/jsm_cls.c | 3 +-
drivers/tty/serial/jsm/jsm_neo.c | 9 +-
drivers/tty/serial/lantiq.c | 3 +-
drivers/tty/serial/ma35d1_serial.c | 6 +-
drivers/tty/serial/max3100.c | 6 +-
drivers/tty/serial/max310x.c | 9 +-
drivers/tty/serial/mux.c | 13 +-
drivers/tty/serial/mvebu-uart.c | 2 +
drivers/tty/serial/mxs-auart.c | 2 +
drivers/tty/serial/sb1250-duart.c | 10 +-
drivers/tty/serial/sccnxp.c | 45 ++++--
drivers/tty/serial/serial_core.c | 100 +++++++++++--
drivers/tty/serial/ucc_uart.c | 6 +-
include/linux/serial_core.h | 18 ++-
25 files changed, 532 insertions(+), 155 deletions(-)
---
base-commit: 1184f87e2790e8af9be3116f608d2e86b4792d7d
change-id: 20261021-submit-serial-8250-dma-cancel-v1-b208f84e89c1
prerequisite-patch-id: 3604bfaa3a1c214997de1f11162b81a4541af5ab
prerequisite-patch-id: 79dd093ffb35e254e790dffb8887faf51b64f947
prerequisite-patch-id: 80a5061d84c055a64c760268febed179c687b99f
prerequisite-patch-id: 7dbb404baf1f861982cf6a4b755d39cfec548223
prerequisite-patch-id: 40881d81a27b3a1aa45203d171214faaf2197768
prerequisite-patch-id: 4456eb7f800cb5d50a70c898e6d5a452a7f0bca4
prerequisite-patch-id: 2876a1809e5fe29b8825950a32796aef1d885054
prerequisite-patch-id: 48659e5ef3f5fb377d0b751f0830c0de4699262c
Best regards,
--
James Hilliard <james.hilliard1@gmail.com>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 1/5] serial: 8250_omap: separate locked TX DMA completion processing
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
@ 2026-10-24 6:40 ` James Hilliard
2026-10-24 6:40 ` [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock James Hilliard
` (3 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
Separate the port-locked completion processing from the DMA callback
wrapper. Keep the shutdown check and locking in the callback, and retain
the existing FIFO accounting, delayed register restore and THRI handling
in the helper.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/tty/serial/8250/8250_omap.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 4805b644ccb1..b6f108d34854 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1080,18 +1080,13 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
static int omap_8250_tx_dma(struct uart_8250_port *p);
-static void omap_8250_dma_tx_complete(void *param)
+static void omap_8250_tx_dma_complete(struct uart_8250_port *p)
{
- struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct tty_port *tport = &p->port.state->port;
bool en_thri = false;
struct omap8250_priv *priv = p->port.private_data;
- guard(uart_port_lock_irqsave)(&p->port);
- if (dma->shutting_down)
- return;
-
dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
@@ -1123,6 +1118,18 @@ static void omap_8250_dma_tx_complete(void *param)
}
}
+static void omap_8250_dma_tx_complete(void *param)
+{
+ struct uart_8250_port *p = param;
+ struct uart_8250_dma *dma = p->dma;
+
+ guard(uart_port_lock_irqsave)(&p->port);
+
+ if (dma->shutting_down)
+ return;
+ omap_8250_tx_dma_complete(p);
+}
+
static int omap_8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
2026-10-24 6:40 ` [PATCH v5 1/5] serial: 8250_omap: separate locked TX DMA completion processing James Hilliard
@ 2026-10-24 6:40 ` James Hilliard
2026-10-25 5:10 ` Maciej W. Rozycki
2026-10-24 6:40 ` [PATCH v5 3/5] serial: core: allow drivers to reserve flushed transmit storage James Hilliard
` (2 subsequent siblings)
4 siblings, 1 reply; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
uart_write_wakeup() expects the UART port lock to be held. Several worker,
DMA completion and interrupt paths call it without that lock, including
indirect calls from uart_handle_cts_change() and the transmit helpers.
Take the port lock around these notifications or their transmit helper.
Keep sleeping SPI and regmap accesses outside the locked region, and keep
the FSL LPUART completion lock held through its wakeup. Acquire the port
lock after releasing private channel locks to preserve the ordering used
by UART callbacks. In the MUX timer, publish available FIFO space under
the port lock and keep the hardware FIFO-drain wait outside it.
SCCNXP handles interrupts under a controller lock which its UART callbacks
acquire with the port lock already held. Collect the ports needing wakeups
while servicing the controller, then release that lock before taking each
port lock and rechecking the wakeup threshold. Removal drains the IRQ or
timer before unregistering the ports.
This serializes write notifications with port-locked serial-core state
changes, allowing readiness and transmit-storage reservations to gate the
notification without a check-versus-shutdown race.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/tty/serial/21285.c | 2 ++
drivers/tty/serial/atmel_serial.c | 2 ++
drivers/tty/serial/clps711x.c | 6 +++--
drivers/tty/serial/cpm_uart.c | 2 ++
drivers/tty/serial/fsl_lpuart.c | 2 +-
drivers/tty/serial/jsm/jsm_cls.c | 3 ++-
drivers/tty/serial/jsm/jsm_neo.c | 9 +++++---
drivers/tty/serial/lantiq.c | 3 ++-
drivers/tty/serial/ma35d1_serial.c | 6 +++--
drivers/tty/serial/max3100.c | 6 +++--
drivers/tty/serial/max310x.c | 9 +++++---
drivers/tty/serial/mux.c | 13 ++++++-----
drivers/tty/serial/mvebu-uart.c | 2 ++
drivers/tty/serial/mxs-auart.c | 2 ++
drivers/tty/serial/sb1250-duart.c | 10 +++++----
drivers/tty/serial/sccnxp.c | 45 +++++++++++++++++++++++++++++---------
drivers/tty/serial/ucc_uart.c | 6 +++--
17 files changed, 92 insertions(+), 36 deletions(-)
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 4de0c975ebdc..e4149aea4f66 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -157,6 +157,8 @@ static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)
struct uart_port *port = dev_id;
u8 ch;
+ guard(uart_port_lock_irqsave)(port);
+
uart_port_tx_limited(port, ch, 256,
!(*CSR_UARTFLG & 0x20),
*CSR_UARTDR = ch,
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index c3880259e3bc..fb12cef3f719 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1371,6 +1371,8 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,
if (status_change & (ATMEL_US_RI | ATMEL_US_DSR
| ATMEL_US_DCD | ATMEL_US_CTS)) {
+ guard(uart_port_lock_irqsave)(port);
+
/* TODO: All reads to CSR will clear these interrupts! */
if (status_change & ATMEL_US_RI)
port->icount.rng++;
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index 83186bf50002..054b9d8359de 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -174,8 +174,10 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
break;
}
- if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ scoped_guard(uart_port_lock_irqsave, port) {
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ }
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c
index 39f54bb7b485..028a01fa02a9 100644
--- a/drivers/tty/serial/cpm_uart.c
+++ b/drivers/tty/serial/cpm_uart.c
@@ -214,6 +214,8 @@ static void cpm_uart_int_tx(struct uart_port *port)
{
pr_debug("CPM uart[%d]:TX INT\n", port->line);
+ guard(uart_port_lock_irqsave)(port);
+
cpm_uart_tx_pump(port);
}
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index c8575c965203..cb04571c3d0e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -540,10 +540,10 @@ static void lpuart_dma_tx_complete(void *arg)
uart_xmit_advance(&sport->port, sport->dma_tx_bytes);
sport->dma_tx_in_progress = false;
- uart_port_unlock_irqrestore(&sport->port, flags);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
+ uart_port_unlock_irqrestore(&sport->port, flags);
if (waitqueue_active(&sport->dma_wait)) {
wake_up(&sport->dma_wait);
diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c
index 6e40792f92cf..7720796331c9 100644
--- a/drivers/tty/serial/jsm/jsm_cls.c
+++ b/drivers/tty/serial/jsm/jsm_cls.c
@@ -571,7 +571,8 @@ static inline void cls_parse_isr(struct jsm_board *brd, uint port)
spin_lock_irqsave(&ch->ch_lock, flags);
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
spin_unlock_irqrestore(&ch->ch_lock, flags);
- cls_copy_data_from_queue_to_uart(ch);
+ scoped_guard(uart_port_lock_irqsave, &ch->uart_port)
+ cls_copy_data_from_queue_to_uart(ch);
}
/*
diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index e8e13bf056e2..cbd9af43c68b 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -740,7 +740,8 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port)
spin_lock_irqsave(&ch->ch_lock, lock_flags);
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
- neo_copy_data_from_queue_to_uart(ch);
+ scoped_guard(uart_port_lock_irqsave, &ch->uart_port)
+ neo_copy_data_from_queue_to_uart(ch);
}
if (isr & UART_17158_IIR_XONXOFF) {
@@ -895,7 +896,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
/* Transfer data (if any) from Write Queue -> UART. */
- neo_copy_data_from_queue_to_uart(ch);
+ scoped_guard(uart_port_lock_irqsave, &ch->uart_port)
+ neo_copy_data_from_queue_to_uart(ch);
}
else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
spin_lock_irqsave(&ch->ch_lock, lock_flags);
@@ -903,7 +905,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
/* Transfer data (if any) from Write Queue -> UART. */
- neo_copy_data_from_queue_to_uart(ch);
+ scoped_guard(uart_port_lock_irqsave, &ch->uart_port)
+ neo_copy_data_from_queue_to_uart(ch);
}
}
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index 62cd9e0bb377..0d7e06c7d790 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -239,7 +239,8 @@ lqasc_tx_int(int irq, void *_port)
spin_lock_irqsave(<q_port->lock, flags);
__raw_writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
spin_unlock_irqrestore(<q_port->lock, flags);
- lqasc_start_tx(port);
+ scoped_guard(uart_port_lock_irqsave, port)
+ lqasc_start_tx(port);
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
index 3be30fb05513..ac44681bcb73 100644
--- a/drivers/tty/serial/ma35d1_serial.c
+++ b/drivers/tty/serial/ma35d1_serial.c
@@ -295,8 +295,10 @@ static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
if (isr & (MA35_ISR_RDA_IF | MA35_ISR_RXTO_IF))
receive_chars(up);
- if (isr & MA35_ISR_THRE_INT)
- transmit_chars(up);
+ if (isr & MA35_ISR_THRE_INT) {
+ scoped_guard(uart_port_lock_irqsave, port)
+ transmit_chars(up);
+ }
if (fsr & MA35_FSR_TX_OVER_IF)
serial_out(up, MA35_FSR_REG, MA35_FSR_TX_OVER_IF);
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index f858a6669011..5a8817eb82ad 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -287,8 +287,10 @@ static void max3100_work(struct work_struct *w)
tty_flip_buffer_push(&s->port.state->port);
rxchars = 0;
}
- if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
+ scoped_guard(uart_port_lock_irqsave, &s->port) {
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+ }
} while (!s->force_end_work &&
!freezing(current) &&
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index a0ed70185555..d9ce6ba53c7f 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -950,8 +950,10 @@ static void max310x_handle_tx(struct uart_port *port)
uart_xmit_advance(port, to_send);
}
- if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ scoped_guard(uart_port_lock_irqsave, port) {
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ }
}
/*
@@ -1043,7 +1045,8 @@ static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno)
if (ists & MAX310X_IRQ_CTS_BIT) {
lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
- uart_handle_cts_change(port, lsr & MAX310X_LSR_CTS_BIT);
+ scoped_guard(uart_port_lock_irqsave, port)
+ uart_handle_cts_change(port, lsr & MAX310X_LSR_CTS_BIT);
}
if (rxlen)
max310x_handle_rx(port, rxlen);
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 3a77a7e5c7bc..8be5b3031749 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -189,11 +189,14 @@ static void mux_write(struct uart_port *port)
{
u8 ch;
- uart_port_tx_limited(port, ch,
- port->fifosize - UART_GET_FIFO_CNT(port),
- true,
- UART_PUT_CHAR(port, ch),
- mux_tx_done(port));
+ scoped_guard(uart_port_lock_irqsave, port) {
+ uart_port_tx_limited(port, ch,
+ port->fifosize - UART_GET_FIFO_CNT(port),
+ true,
+ UART_PUT_CHAR(port, ch),
+ ({}));
+ }
+ mux_tx_done(port);
}
/**
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index 33ea496c93b2..7bc0d0d3760e 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -334,6 +334,8 @@ static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
{
u8 ch;
+ guard(uart_port_lock_irqsave)(port);
+
uart_port_tx_limited(port, ch, port->fifosize,
!(readl(port->membase + UART_STAT) & STAT_TX_FIFO_FUL),
writel(ch, port->membase + UART_TSH(port)),
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 697318dbb146..6a310309b55d 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -521,6 +521,8 @@ static void dma_tx_callback(void *param)
dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE);
+ guard(uart_port_lock_irqsave)(&s->port);
+
/* clear the bit used to serialize the DMA tx. */
clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
smp_mb__after_atomic();
diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
index b4e1b90e5960..b15f76acd06b 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -454,10 +454,12 @@ static irqreturn_t sbd_interrupt(int irq, void *dev_id)
if (intstat & M_DUART_ISR_RX)
sbd_receive_chars(sport);
- if (intstat & M_DUART_ISR_IN)
- sbd_status_handle(sport);
- if (intstat & M_DUART_ISR_TX)
- sbd_transmit_chars(sport);
+ scoped_guard(uart_port_lock_irqsave, uport) {
+ if (intstat & M_DUART_ISR_IN)
+ sbd_status_handle(sport);
+ if (intstat & M_DUART_ISR_TX)
+ sbd_transmit_chars(sport);
+ }
status = IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index cb31e3aceb62..f89efc4a2d77 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -435,7 +435,7 @@ static void sccnxp_handle_rx(struct uart_port *port)
tty_flip_buffer_push(&port->state->port);
}
-static void sccnxp_handle_tx(struct uart_port *port)
+static bool sccnxp_handle_tx(struct uart_port *port)
{
u8 sr;
struct tty_port *tport = &port->state->port;
@@ -445,7 +445,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char);
port->icount.tx++;
port->x_char = 0;
- return;
+ return false;
}
if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
@@ -457,7 +457,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
if (s->chip->flags & SCCNXP_HAVE_IO)
sccnxp_set_bit(port, DIR_OP, 0);
}
- return;
+ return false;
}
while (1) {
@@ -473,12 +473,12 @@ static void sccnxp_handle_tx(struct uart_port *port)
sccnxp_port_write(port, SCCNXP_THR_REG, ch);
}
- if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ return kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS;
}
-static void sccnxp_handle_events(struct sccnxp_port *s)
+static unsigned int sccnxp_handle_events(struct sccnxp_port *s)
{
+ unsigned int wakeup = 0;
int i;
u8 isr;
@@ -491,20 +491,43 @@ static void sccnxp_handle_events(struct sccnxp_port *s)
for (i = 0; i < s->uart.nr; i++) {
if (s->opened[i] && (isr & ISR_RXRDY(i)))
sccnxp_handle_rx(&s->port[i]);
- if (s->opened[i] && (isr & ISR_TXRDY(i)))
- sccnxp_handle_tx(&s->port[i]);
+ if (s->opened[i] && (isr & ISR_TXRDY(i)) &&
+ sccnxp_handle_tx(&s->port[i]))
+ wakeup |= BIT(i);
}
} while (1);
+
+ return wakeup;
+}
+
+static void sccnxp_write_wakeup(struct sccnxp_port *s, unsigned int wakeup)
+{
+ int i;
+
+ /* UART callbacks take s->lock with the port lock already held. */
+ for (i = 0; i < s->uart.nr; i++) {
+ struct uart_port *port = &s->port[i];
+
+ if (!(wakeup & BIT(i)))
+ continue;
+
+ scoped_guard(uart_port_lock_irqsave, port) {
+ if (kfifo_len(&port->state->port.xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ }
+ }
}
static void sccnxp_timer(struct timer_list *t)
{
struct sccnxp_port *s = timer_container_of(s, t, timer);
unsigned long flags;
+ unsigned int wakeup;
spin_lock_irqsave(&s->lock, flags);
- sccnxp_handle_events(s);
+ wakeup = sccnxp_handle_events(s);
spin_unlock_irqrestore(&s->lock, flags);
+ sccnxp_write_wakeup(s, wakeup);
mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us));
}
@@ -513,10 +536,12 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id)
{
struct sccnxp_port *s = (struct sccnxp_port *)dev_id;
unsigned long flags;
+ unsigned int wakeup;
spin_lock_irqsave(&s->lock, flags);
- sccnxp_handle_events(s);
+ wakeup = sccnxp_handle_events(s);
spin_unlock_irqrestore(&s->lock, flags);
+ sccnxp_write_wakeup(s, wakeup);
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 6b7dcbbacd13..60f72c19d67c 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -570,8 +570,10 @@ static irqreturn_t qe_uart_int(int irq, void *data)
if (events & UCC_UART_UCCE_RX)
qe_uart_int_rx(qe_port);
- if (events & UCC_UART_UCCE_TX)
- qe_uart_tx_pump(qe_port);
+ if (events & UCC_UART_UCCE_TX) {
+ scoped_guard(uart_port_lock_irqsave, &qe_port->port)
+ qe_uart_tx_pump(qe_port);
+ }
return events ? IRQ_HANDLED : IRQ_NONE;
}
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 3/5] serial: core: allow drivers to reserve flushed transmit storage
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
2026-10-24 6:40 ` [PATCH v5 1/5] serial: 8250_omap: separate locked TX DMA completion processing James Hilliard
2026-10-24 6:40 ` [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock James Hilliard
@ 2026-10-24 6:40 ` James Hilliard
2026-10-24 6:40 ` [PATCH v5 4/5] serial: 8250: drain canceled TX DMA before reusing its buffer James Hilliard
2026-10-24 6:40 ` [PATCH v5 5/5] serial: 8250: retire canceled RX DMA before reusing the receive buffer James Hilliard
4 siblings, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
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 <james.hilliard1@gmail.com>
---
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
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 4/5] serial: 8250: drain canceled TX DMA before reusing its buffer
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
` (2 preceding siblings ...)
2026-10-24 6:40 ` [PATCH v5 3/5] serial: core: allow drivers to reserve flushed transmit storage James Hilliard
@ 2026-10-24 6:40 ` James Hilliard
2026-10-24 6:40 ` [PATCH v5 5/5] serial: 8250: retire canceled RX DMA before reusing the receive buffer James Hilliard
4 siblings, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
uart_flush_buffer() resets the transmit kfifo before calling the driver.
serial8250_tx_dma_flush() terminates DMA asynchronously and clears
tx_running, allowing a new write to replace tx_size while the old callback
is still waiting for the port lock. That callback can then consume bytes
from the new transmission. Reusing the reset kfifo can also overwrite the
source of a transfer that has not stopped yet.
Reserve the flushed FIFO storage through serial core before returning
from the flush callback. Keep tx_running set until a worker synchronizes
the terminated channel, suppressing old callbacks and new submissions
during that interval. Return the mapping to CPU ownership before releasing
the reservation and waking writers. Normal transfers continue to use the
FIFO directly.
Reuse the locked completion processing after retirement, including OMAP
delayed register restoration and transmitter-empty handling. Take a runtime
PM reference around worker UART accesses and drain the worker before
releasing the channels. Shutdown releases the FIFO reservation after
stopping DMA and unmapping its source. Serial core keeps writes and wakeups
gated until startup or resume makes the port ready again.
Track a flushed FIFO separately from the transfer length. If termination
fails, retain the reservation until normal completion and account the
completed bytes without advancing the reset FIFO. After successful
cancellation, process a zero-length completion once callbacks have drained.
The race was found by inspecting cancellation and restart while preparing
H616 UART DMA support.
Fixes: a424a34b8fad ("serial: 8250: Fix TX deadlock when using DMA")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/tty/serial/8250/8250.h | 20 +++++-
drivers/tty/serial/8250/8250_dma.c | 130 ++++++++++++++++++++++++++----------
drivers/tty/serial/8250/8250_omap.c | 21 ++++--
3 files changed, 125 insertions(+), 46 deletions(-)
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 974a47f24084..9f01fa36fa3c 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -11,6 +11,7 @@
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
#include <linux/dmaengine.h>
+#include <linux/workqueue.h>
#include "../serial_mctrl_gpio.h"
@@ -19,6 +20,10 @@ struct uart_8250_dma {
int (*rx_dma)(struct uart_8250_port *p);
void (*prepare_tx_dma)(struct uart_8250_port *p);
void (*prepare_rx_dma)(struct uart_8250_port *p);
+ /* Completion processing with the port lock held. */
+ void (*tx_complete)(struct uart_8250_port *p);
+ struct work_struct tx_flush_work;
+ struct uart_8250_port *port;
/* Filter function */
dma_filter_fn fn;
@@ -52,7 +57,9 @@ struct uart_8250_dma {
unsigned char tx_err;
unsigned char rx_running;
/* Protected by the UART port lock. */
- bool shutting_down;
+ bool shutting_down:1;
+ bool tx_terminating:1;
+ bool tx_flushed:1;
};
struct old_serial_port {
@@ -400,8 +407,15 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt)
#endif
#ifdef CONFIG_SERIAL_8250_DMA
+static inline bool serial8250_tx_dma_blocked(struct uart_8250_dma *dma)
+{
+ return dma->shutting_down || dma->tx_terminating;
+}
+
extern int serial8250_tx_dma(struct uart_8250_port *);
extern void serial8250_tx_dma_flush(struct uart_8250_port *);
+void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p,
+ struct scatterlist *sgl, unsigned int nents);
extern int serial8250_rx_dma(struct uart_8250_port *);
extern void serial8250_rx_dma_flush(struct uart_8250_port *);
extern int serial8250_request_dma(struct uart_8250_port *);
@@ -434,7 +448,7 @@ static inline void serial8250_tx_dma_pause(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
- if (!dma->tx_running)
+ if (!dma->tx_running || serial8250_tx_dma_blocked(dma))
return;
dmaengine_pause(dma->txchan);
@@ -444,7 +458,7 @@ static inline void serial8250_tx_dma_resume(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
- if (!dma->tx_running)
+ if (!dma->tx_running || serial8250_tx_dma_blocked(dma))
return;
dmaengine_resume(dma->txchan);
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 57fc7915adac..1a070967bf57 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -11,24 +11,23 @@
#include "8250.h"
-static void __dma_tx_complete(void *param)
+static void serial8250_tx_dma_complete(struct uart_8250_port *p)
{
- struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct tty_port *tport = &p->port.state->port;
- unsigned long flags;
int ret;
- uart_port_lock_irqsave(&p->port, &flags);
- if (dma->shutting_down)
- goto out;
-
dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma->tx_running = 0;
- uart_xmit_advance(&p->port, dma->tx_size);
+ if (dma->tx_flushed) {
+ p->port.icount.tx += dma->tx_size;
+ uart_xmit_unblock(&p->port);
+ } else {
+ uart_xmit_advance(&p->port, dma->tx_size);
+ }
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
@@ -36,11 +35,57 @@ static void __dma_tx_complete(void *param)
ret = serial8250_tx_dma(p);
if (ret || !dma->tx_running)
serial8250_set_THRI(p);
+}
-out:
- uart_port_unlock_irqrestore(&p->port, flags);
+static void __dma_tx_complete(void *param)
+{
+ struct uart_8250_port *p = param;
+ struct uart_8250_dma *dma = p->dma;
+
+ guard(uart_port_lock_irqsave)(&p->port);
+
+ if (serial8250_tx_dma_blocked(dma))
+ return;
+ serial8250_tx_dma_complete(p);
+}
+
+static void serial8250_tx_dma_flush_work(struct work_struct *work)
+{
+ struct uart_8250_dma *dma = container_of(work, struct uart_8250_dma, tx_flush_work);
+ struct uart_8250_port *p = dma->port;
+
+ /* Neither the channel nor its buffer may be reused before this returns. */
+ dmaengine_synchronize(dma->txchan);
+
+ guard(serial8250_rpm)(p);
+
+ guard(uart_port_lock_irqsave)(&p->port);
+
+ dma->tx_terminating = false;
+ dma->tx_size = 0;
+ if (dma->shutting_down)
+ return;
+ dma->tx_complete(p);
}
+void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p,
+ struct scatterlist *sgl, unsigned int nents)
+{
+ struct uart_8250_dma *dma = p->dma;
+ struct device *dev = dmaengine_get_dma_device(dma->txchan);
+ struct scatterlist *sg;
+ unsigned int i;
+
+ /* DMA reads the FIFO directly; its occupied ranges remain reserved. */
+ for_each_sg(sgl, sg, nents, i) {
+ size_t offset = sg_dma_address(sg) - dma->tx_addr;
+
+ dma_sync_single_range_for_device(dev, dma->tx_addr, offset,
+ sg_dma_len(sg), DMA_TO_DEVICE);
+ }
+}
+EXPORT_SYMBOL_GPL(serial8250_tx_dma_sync_buffer);
+
static void __dma_rx_complete(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
@@ -101,7 +146,7 @@ int serial8250_tx_dma(struct uart_8250_port *p)
int i;
int ret;
- if (dma->shutting_down)
+ if (serial8250_tx_dma_blocked(dma))
return 0;
if (dma->tx_running) {
@@ -131,24 +176,25 @@ int serial8250_tx_dma(struct uart_8250_port *p)
dma->tx_size += sg_dma_len(sg);
serial8250_do_prepare_tx_dma(p);
+ serial8250_tx_dma_sync_buffer(p, sgl, ret);
desc = dmaengine_prep_slave_sg(dma->txchan, sgl, ret,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
+ dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
ret = -EBUSY;
goto err;
}
dma->tx_running = 1;
+ dma->tx_flushed = false;
+ dma->tx_complete = serial8250_tx_dma_complete;
desc->callback = __dma_tx_complete;
desc->callback_param = p;
dma->tx_cookie = dmaengine_submit(desc);
-
- dma_sync_single_for_device(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
-
dma_async_issue_pending(dma->txchan);
serial8250_clear_THRI(p);
dma->tx_err = 0;
@@ -162,32 +208,31 @@ int serial8250_tx_dma(struct uart_8250_port *p)
void serial8250_tx_dma_flush(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
+ int ret;
- if (!dma->tx_running)
+ if (!dma->tx_running || serial8250_tx_dma_blocked(dma))
return;
/*
* kfifo_reset() has been called by the serial core, avoid
- * advancing and underflowing in __dma_tx_complete().
+ * advancing the reset FIFO when the old transfer completes.
*/
- dma->tx_size = 0;
+ dma->tx_flushed = true;
+ /* The core reset the FIFO indices, but DMA may still read its storage. */
+ uart_xmit_block(&p->port);
/*
* We can't use `dmaengine_terminate_sync` because `uart_flush_buffer` is
* holding the uart port spinlock.
*/
- dmaengine_terminate_async(dma->txchan);
-
- /*
- * The callback might or might not run. If it doesn't run, we need to ensure
- * that `tx_running` is cleared so that we can schedule new transactions.
- * If it does run, then the zombie callback will clear `tx_running` again
- * and perform a no-op since `tx_size` was cleared above.
- *
- * In either case, we ASSUME the DMA transaction will terminate before we
- * issue a new `serial8250_tx_dma`.
- */
- dma->tx_running = 0;
+ dma->tx_terminating = true;
+ ret = dmaengine_terminate_async(dma->txchan);
+ if (ret) {
+ /* Keep the buffer owned by the transfer until its callback runs. */
+ dma->tx_terminating = false;
+ return;
+ }
+ schedule_work(&dma->tx_flush_work);
}
int serial8250_rx_dma(struct uart_8250_port *p)
@@ -312,21 +357,28 @@ int serial8250_request_dma(struct uart_8250_port *p)
goto err;
}
- /* TX buffer */
+ /* Normal TX uses serial core's FIFO; flush reserves it until retirement. */
dma->tx_addr = dma_map_single(tx_dev, p->port.state->port.xmit_buf,
UART_XMIT_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(tx_dev, dma->tx_addr)) {
- dma_free_coherent(rx_dev, dma->rx_size,
- dma->rx_buf, dma->rx_addr);
ret = -ENOMEM;
- goto err;
+ goto free_rx;
}
+ dma_sync_single_for_cpu(tx_dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ dma->port = p;
+ INIT_WORK(&dma->tx_flush_work, serial8250_tx_dma_flush_work);
- scoped_guard(uart_port_lock_irqsave, &p->port)
+ scoped_guard(uart_port_lock_irqsave, &p->port) {
+ dma->tx_terminating = false;
+ dma->tx_flushed = false;
dma->shutting_down = false;
+ }
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
return 0;
+free_rx:
+ dma_free_coherent(rx_dev, dma->rx_size, dma->rx_buf, dma->rx_addr);
err:
dma_release_channel(dma->txchan);
release_rx:
@@ -342,6 +394,8 @@ void serial8250_release_dma(struct uart_8250_port *p)
if (!dma)
return;
+ cancel_work_sync(&dma->tx_flush_work);
+
/* Release RX resources */
dmaengine_terminate_sync(dma->rxchan);
dma->rx_running = 0;
@@ -356,7 +410,11 @@ void serial8250_release_dma(struct uart_8250_port *p)
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma_release_channel(dma->txchan);
dma->txchan = NULL;
- dma->tx_running = 0;
+ scoped_guard(uart_port_lock_irqsave, &p->port) {
+ dma->tx_running = 0;
+ dma->tx_terminating = false;
+ uart_xmit_unblock(&p->port);
+ }
dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
}
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index b6f108d34854..0dcf63bd3d78 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1092,7 +1092,12 @@ static void omap_8250_tx_dma_complete(struct uart_8250_port *p)
dma->tx_running = 0;
- uart_xmit_advance(&p->port, dma->tx_size);
+ if (dma->tx_flushed) {
+ p->port.icount.tx += dma->tx_size;
+ uart_xmit_unblock(&p->port);
+ } else {
+ uart_xmit_advance(&p->port, dma->tx_size);
+ }
if (priv->delayed_restore) {
priv->delayed_restore = 0;
@@ -1125,7 +1130,7 @@ static void omap_8250_dma_tx_complete(void *param)
guard(uart_port_lock_irqsave)(&p->port);
- if (dma->shutting_down)
+ if (serial8250_tx_dma_blocked(dma))
return;
omap_8250_tx_dma_complete(p);
}
@@ -1140,7 +1145,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
int skip_byte = -1;
int ret;
- if (dma->shutting_down || dma->tx_running)
+ if (serial8250_tx_dma_blocked(dma) || dma->tx_running)
return 0;
if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
@@ -1199,24 +1204,26 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
goto err;
}
+ serial8250_tx_dma_sync_buffer(p, &sg, 1);
+
desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
+ dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
ret = -EBUSY;
goto err;
}
dma->tx_size = sg_dma_len(&sg);
dma->tx_running = 1;
+ dma->tx_flushed = false;
+ dma->tx_complete = omap_8250_tx_dma_complete;
desc->callback = omap_8250_dma_tx_complete;
desc->callback_param = p;
dma->tx_cookie = dmaengine_submit(desc);
-
- dma_sync_single_for_device(dmaengine_get_dma_device(dma->txchan), dma->tx_addr,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
-
dma_async_issue_pending(dma->txchan);
if (dma->tx_err)
dma->tx_err = 0;
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 5/5] serial: 8250: retire canceled RX DMA before reusing the receive buffer
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
` (3 preceding siblings ...)
2026-10-24 6:40 ` [PATCH v5 4/5] serial: 8250: drain canceled TX DMA before reusing its buffer James Hilliard
@ 2026-10-24 6:40 ` James Hilliard
4 siblings, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-24 6:40 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Maciej W. Rozycki,
Timur Tabi
Cc: linux-kernel, linux-serial, linux-arm-kernel, imx, linuxppc-dev,
James Hilliard
The RX timeout path pauses a transfer, copies its data and terminates it
asynchronously. Clearing rx_running at that point lets another receive
reuse the buffer and cookie while the old callback is still waiting for
the port lock. Inspecting the new cookie cannot distinguish that callback
from the completion of a new transfer.
Reserve the receive buffer until a worker has drained the canceled
callbacks. Keep FIFO access separate from callback retirement: after a
successful pause and termination, clear rx_running while rx_terminating
prevents another descriptor from reusing the buffer. Deliver the paused
data before allowing the interrupt handler to drain the FIFO through PIO.
Normal retirement only releases ownership and leaves subsequent receive
handling to the UART interrupt.
Preserve the normal completion-path LSR read and gate DMA resubmission on
the receive interrupt enable, so stop_rx() prevents another transfer while
the status read retains its existing side effects.
Reject new flush work and PIO receive handling during shutdown, including
late Intel MID DMA timeouts. Stop and drain retirement work before freeing
the channels and buffer. Once RX has stopped, allow the idle flush used by
DesignWare LCR recovery during shutdown. Clear the shutdown guard before
startup configures the UART and tests its interrupts.
Check pause failures before accessing the buffer. A successful pause is
the quiescence guarantee, even when a provider still reports IN_PROGRESS.
Also accept DMA_COMPLETE after a failed pause: completion can stop the
channel before its callback acquires the port lock, leaving nothing for
the provider to pause. Retire that completed transfer normally.
On pause, status or termination failure, mask receive interrupts and let
the worker stop DMA before handing the FIFO to PIO. Keep the requested
enables in the existing IER shadow and filter hardware writes throughout
recovery, including termios, RS485 and overrun-backoff changes. Apply the
current enables after recovery so stop_rx() and shutdown remain effective.
Hold a runtime PM reference around worker register accesses and keep RX
on PIO until reopen.
Abort the DesignWare idle sequence when RX cannot be stopped. Allow
the empty-FIFO timeout workaround once DMA has released the FIFO, including
while callbacks retire. Exclude FIFO resets during RX DMA recovery. If the
provider still cannot terminate the channel in process context, retain
its buffer and leave receive interrupts masked.
Fixes: 9ee4b83e51f7 ("serial: 8250: Add support for dmaengine")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/tty/serial/8250/8250.h | 39 ++++++++-
drivers/tty/serial/8250/8250_core.c | 2 +-
drivers/tty/serial/8250/8250_dma.c | 156 +++++++++++++++++++++++++++---------
drivers/tty/serial/8250/8250_dw.c | 27 +++++--
drivers/tty/serial/8250/8250_port.c | 31 ++++---
5 files changed, 197 insertions(+), 58 deletions(-)
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 9f01fa36fa3c..35a71d4780a5 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -23,6 +23,7 @@ struct uart_8250_dma {
/* Completion processing with the port lock held. */
void (*tx_complete)(struct uart_8250_port *p);
struct work_struct tx_flush_work;
+ struct work_struct rx_flush_work;
struct uart_8250_port *port;
/* Filter function */
@@ -55,11 +56,14 @@ struct uart_8250_dma {
unsigned char tx_running;
unsigned char tx_err;
+ /* RX DMA may access the FIFO; independent of callback retirement. */
unsigned char rx_running;
/* Protected by the UART port lock. */
bool shutting_down:1;
bool tx_terminating:1;
bool tx_flushed:1;
+ bool rx_terminating:1;
+ bool rx_err:1;
};
struct old_serial_port {
@@ -148,6 +152,28 @@ static inline void serial_out(struct uart_8250_port *up, int offset, int value)
up->port.serial_out(&up->port, offset, value);
}
+static inline bool serial8250_rx_dma_running(struct uart_8250_port *up)
+{
+ return up->dma && up->dma->rx_running;
+}
+
+static inline bool serial8250_rx_dma_recovering(struct uart_8250_port *up)
+{
+ return up->dma && up->dma->rx_err && up->dma->rx_terminating;
+}
+
+/* Keep the desired enables in up->ier while RX DMA recovery masks hardware. */
+static inline void serial8250_out_IER(struct uart_8250_port *up, u8 ier)
+{
+ lockdep_assert_held_once(&up->port.lock);
+
+ if (up->dma && up->dma->shutting_down)
+ ier = 0;
+ else if (serial8250_rx_dma_recovering(up))
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE);
+ serial_out(up, UART_IER, ier);
+}
+
/**
* serial_lsr_in - Read LSR register and preserve flags across reads
* @up: uart 8250 port
@@ -221,7 +247,7 @@ 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_out_IER(up, up->ier);
return true;
}
@@ -233,7 +259,7 @@ static inline bool serial8250_clear_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_out_IER(up, up->ier);
return true;
}
@@ -417,7 +443,7 @@ extern void serial8250_tx_dma_flush(struct uart_8250_port *);
void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p,
struct scatterlist *sgl, unsigned int nents);
extern int serial8250_rx_dma(struct uart_8250_port *);
-extern void serial8250_rx_dma_flush(struct uart_8250_port *);
+int serial8250_rx_dma_flush(struct uart_8250_port *p);
extern int serial8250_request_dma(struct uart_8250_port *);
extern void serial8250_release_dma(struct uart_8250_port *);
@@ -473,7 +499,12 @@ static inline int serial8250_rx_dma(struct uart_8250_port *p)
{
return -1;
}
-static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { }
+
+static inline int serial8250_rx_dma_flush(struct uart_8250_port *p)
+{
+ return 0;
+}
+
static inline int serial8250_request_dma(struct uart_8250_port *p)
{
return -1;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 42319843cb5b..ca06288038ed 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -738,7 +738,7 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work)
guard(uart_port_lock_irqsave)(&up->port);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
- serial_out(up, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
static int __serial8250_register_8250_port(struct uart_8250_port *uart,
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 1a070967bf57..801221baded0 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -86,53 +86,96 @@ void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p,
}
EXPORT_SYMBOL_GPL(serial8250_tx_dma_sync_buffer);
-static void __dma_rx_complete(struct uart_8250_port *p)
+static void serial8250_rx_dma_copy(struct uart_8250_port *p,
+ const struct dma_tx_state *state)
{
struct uart_8250_dma *dma = p->dma;
struct tty_port *tty_port = &p->port.state->port;
- struct dma_tx_state state;
- enum dma_status dma_status;
- int count;
-
- /*
- * New DMA Rx can be started during the completion handler before it
- * could acquire port's lock and it might still be ongoing. Don't to
- * anything in such case.
- */
- dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
- if (dma_status == DMA_IN_PROGRESS)
- return;
-
- count = dma->rx_size - state.residue;
+ size_t count = dma->rx_size - state->residue;
if (tty_insert_flip_string(tty_port, dma->rx_buf, count) != count)
p->port.icount.buf_overrun++;
p->port.icount.rx += count;
- dma->rx_running = 0;
tty_flip_buffer_push(tty_port);
}
+static void serial8250_rx_dma_recover(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ /* Keep PIO and new descriptors out until the worker stops the channel. */
+ dma->rx_err = true;
+ dma->rx_terminating = true;
+ serial8250_out_IER(p, p->ier);
+ schedule_work(&dma->rx_flush_work);
+}
+
static void dma_rx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
- unsigned long flags;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ guard(uart_port_lock_irqsave)(&p->port);
+
+ if (dma->shutting_down || dma->rx_terminating)
+ return;
+ if (dma->rx_running) {
+ status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ if (status == DMA_ERROR) {
+ serial8250_rx_dma_recover(p);
+ return;
+ }
+ if (status != DMA_COMPLETE)
+ return;
+ serial8250_rx_dma_copy(p, &state);
+ dma->rx_running = 0;
+ }
+
+ /* Preserve LSR read side effects when stop_rx() has disabled resubmission. */
+ if ((serial_lsr_in(p) & UART_LSR_DR) && (p->ier & UART_IER_RDI))
+ dma->rx_dma(p);
+}
+
+static void serial8250_rx_dma_flush_work(struct work_struct *work)
+{
+ struct uart_8250_dma *dma = container_of(work, struct uart_8250_dma, rx_flush_work);
+ struct uart_8250_port *p = dma->port;
+ bool recover;
+ int ret;
+
+ /* All packed flags share the port lock, including this worker snapshot. */
+ scoped_guard(uart_port_lock_irqsave, &p->port)
+ recover = dma->rx_err;
+
+ if (recover) {
+ ret = dmaengine_terminate_sync(dma->rxchan);
+ if (ret) {
+ /* Retain ownership and keep RX masked if DMA cannot be stopped. */
+ dev_err(p->port.dev, "failed to stop RX DMA: %d\n", ret);
+ return;
+ }
+ } else {
+ dmaengine_synchronize(dma->rxchan);
+ }
+
+ guard(serial8250_rpm)(p);
- uart_port_lock_irqsave(&p->port, &flags);
+ guard(uart_port_lock_irqsave)(&p->port);
+
+ dma->rx_terminating = false;
+ dma->rx_running = 0;
if (dma->shutting_down)
- goto out;
- if (dma->rx_running)
- __dma_rx_complete(p);
+ return;
- /*
- * Cannot be combined with the previous check because __dma_rx_complete()
- * changes dma->rx_running.
- */
- if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR))
- p->dma->rx_dma(p);
-out:
- uart_port_unlock_irqrestore(&p->port, flags);
+ if (!recover)
+ return;
+
+ /* Apply the current enables, including any intervening stop_rx(). */
+ serial8250_out_IER(p, p->ier);
+ dev_warn(p->port.dev, "RX DMA failed, using PIO\n");
}
int serial8250_tx_dma(struct uart_8250_port *p)
@@ -242,6 +285,9 @@ int serial8250_rx_dma(struct uart_8250_port *p)
if (dma->shutting_down || dma->rx_running)
return 0;
+ /* A retiring transfer has released the FIFO, but not its buffer. */
+ if (dma->rx_err || dma->rx_terminating)
+ return -EIO;
serial8250_do_prepare_rx_dma(p);
@@ -262,16 +308,47 @@ int serial8250_rx_dma(struct uart_8250_port *p)
return 0;
}
-void serial8250_rx_dma_flush(struct uart_8250_port *p)
+int serial8250_rx_dma_flush(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
+ struct dma_tx_state state;
+ enum dma_status status;
+ int ret;
- if (!dma || !dma->rxchan || !dma->rx_running)
- return;
+ lockdep_assert_held_once(&p->port.lock);
- dmaengine_pause(dma->rxchan);
- __dma_rx_complete(p);
- dmaengine_terminate_async(dma->rxchan);
+ if (!dma || !dma->rx_running)
+ return 0;
+ /* A late interrupt must not hand a still-active receiver to PIO. */
+ if (dma->shutting_down)
+ return -ESHUTDOWN;
+ if (dma->rx_terminating)
+ return -EIO;
+
+ ret = dmaengine_pause(dma->rxchan);
+ status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ /* A completed transfer may reject pause before its callback gets the lock. */
+ if (ret && status != DMA_COMPLETE)
+ goto recover;
+ if (status == DMA_ERROR) {
+ ret = -EIO;
+ goto recover;
+ }
+ /* Some providers still report IN_PROGRESS after a successful pause. */
+ serial8250_rx_dma_copy(p, &state);
+ dma->rx_terminating = true;
+ ret = dmaengine_terminate_async(dma->rxchan);
+ if (ret)
+ goto recover;
+
+ /* DMA released the FIFO; retain the buffer until callbacks retire. */
+ dma->rx_running = 0;
+ schedule_work(&dma->rx_flush_work);
+ return 0;
+
+recover:
+ serial8250_rx_dma_recover(p);
+ return ret;
}
EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush);
@@ -368,10 +445,13 @@ int serial8250_request_dma(struct uart_8250_port *p)
dma->port = p;
INIT_WORK(&dma->tx_flush_work, serial8250_tx_dma_flush_work);
+ INIT_WORK(&dma->rx_flush_work, serial8250_rx_dma_flush_work);
scoped_guard(uart_port_lock_irqsave, &p->port) {
dma->tx_terminating = false;
dma->tx_flushed = false;
+ dma->rx_terminating = false;
+ dma->rx_err = false;
dma->shutting_down = false;
}
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
@@ -395,10 +475,14 @@ void serial8250_release_dma(struct uart_8250_port *p)
return;
cancel_work_sync(&dma->tx_flush_work);
+ cancel_work_sync(&dma->rx_flush_work);
/* Release RX resources */
dmaengine_terminate_sync(dma->rxchan);
- dma->rx_running = 0;
+ scoped_guard(uart_port_lock_irqsave, &p->port) {
+ dma->rx_running = 0;
+ dma->rx_terminating = false;
+ }
dma_free_coherent(dmaengine_get_dma_device(dma->rxchan), dma->rx_size,
dma->rx_buf, dma->rx_addr);
dma_release_channel(dma->rxchan);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 7b7dfad583fe..0cd0ab048a8a 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -112,7 +112,7 @@ static void dw8250_idle_exit(struct uart_port *p)
if (up->capabilities & UART_CAP_FIFO)
serial_port_out(p, UART_FCR, up->fcr);
serial_port_out(p, UART_MCR, up->mcr);
- serial_port_out(p, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
/* DMA Rx is restarted by IRQ handler as needed. */
if (up->dma)
@@ -134,6 +134,7 @@ static int dw8250_idle_enter(struct uart_port *p)
unsigned int usr_reg = d->pdata ? d->pdata->usr_reg : DW_UART_USR;
struct uart_8250_port *up = up_to_u8250p(p);
int retries;
+ int ret;
u32 lsr;
lockdep_assert_held_once(&p->lock);
@@ -147,7 +148,12 @@ static int dw8250_idle_enter(struct uart_port *p)
serial_port_out(p, UART_IER, 0);
if (up->dma) {
- serial8250_rx_dma_flush(up);
+ ret = serial8250_rx_dma_flush(up);
+ if (ret) {
+ serial8250_out_IER(up, up->ier);
+ d->in_idle = 0;
+ return ret;
+ }
if (serial8250_tx_dma_running(up))
serial8250_tx_dma_pause(up);
}
@@ -392,9 +398,9 @@ static void dw8250_quirk_ier_kick(struct uart_port *p)
if (!(lsr & UART_LSR_THRE))
return;
- serial_port_out(p, UART_IER, up->ier | UART_IER_THRI);
+ serial8250_out_IER(up, up->ier | UART_IER_THRI);
serial_port_in(p, UART_LCR); /* safe, no side-effects */
- serial_port_out(p, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
static int dw8250_handle_irq(struct uart_port *p)
@@ -431,6 +437,9 @@ static int dw8250_handle_irq(struct uart_port *p)
d->no_int_count = 0;
+ if (up->dma && up->dma->shutting_down)
+ return 1;
+
/*
* There are ways to get Designware-based UARTs into a state where
* they are asserting UART_IIR_RX_TIMEOUT but there is no actual
@@ -438,10 +447,11 @@ static int dw8250_handle_irq(struct uart_port *p)
* read. If we don't do this then the "RX TIMEOUT" interrupt will
* fire forever.
*
- * This problem has only been observed so far when not in DMA mode
- * so we limit the workaround only to non-DMA mode.
+ * Only read the FIFO when DMA no longer owns it, including PIO fallback
+ * after a DMA error.
*/
- if (!(quirks & DW_UART_QUIRK_SKIP_EMPTY_FIFO_READ) && !up->dma && rx_timeout) {
+ if (!(quirks & DW_UART_QUIRK_SKIP_EMPTY_FIFO_READ) &&
+ !serial8250_rx_dma_running(up) && rx_timeout) {
status = serial_lsr_in(up);
/*
@@ -456,7 +466,8 @@ static int dw8250_handle_irq(struct uart_port *p)
}
/* Manually stop the Rx DMA transfer when acting as flow controller */
- if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) {
+ if (quirks & DW_UART_QUIRK_IS_DMA_FC && rx_timeout &&
+ serial8250_rx_dma_running(up) && !up->dma->rx_terminating) {
status = serial_lsr_in(up);
if (status & (UART_LSR_DR | UART_LSR_BI)) {
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index f36f7e18ab95..24964acc87f8 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1306,7 +1306,7 @@ static void serial8250_stop_rx(struct uart_port *port)
guard(serial8250_rpm)(up);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
- serial_port_out(port, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
/**
@@ -1332,7 +1332,8 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier)
* Enable previously disabled RX interrupts.
*/
if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
- serial8250_clear_and_reinit_fifos(p);
+ if (!serial8250_rx_dma_recovering(p))
+ serial8250_clear_and_reinit_fifos(p);
if (toggle_ier) {
/*
@@ -1346,7 +1347,7 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier)
lockdep_assert_held_once(&p->port.lock);
p->ier |= UART_IER_RLSI | UART_IER_RDI;
- serial_port_out(&p->port, UART_IER, p->ier);
+ serial8250_out_IER(p, p->ier);
}
}
}
@@ -1600,7 +1601,7 @@ static void serial8250_disable_ms(struct uart_port *port)
mctrl_gpio_disable_ms_no_sync(up->gpios);
up->ier &= ~UART_IER_MSI;
- serial_port_out(port, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
static void serial8250_enable_ms(struct uart_port *port)
@@ -1619,7 +1620,7 @@ static void serial8250_enable_ms(struct uart_port *port)
up->ier |= UART_IER_MSI;
guard(serial8250_rpm)(up);
- serial_port_out(port, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
void serial8250_read_char(struct uart_8250_port *up, u16 lsr)
@@ -1816,8 +1817,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
fallthrough;
case UART_IIR_RLSI:
case UART_IIR_RX_TIMEOUT:
- serial8250_rx_dma_flush(up);
- return true;
+ return !serial8250_rx_dma_flush(up);
}
return up->dma->rx_dma(up);
}
@@ -1837,15 +1837,22 @@ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir)
lockdep_assert_held_once(&port->lock);
+ if (up->dma && up->dma->shutting_down)
+ return;
+
status = serial_lsr_in(up);
/*
* Recover from no-data-ready and FIFO error condition to avoid getting
* stuck in the ISR.
*/
- if (!(status & UART_LSR_DR) && (status & UART_LSR_FIFOE))
+ if (!(status & UART_LSR_DR) && (status & UART_LSR_FIFOE) &&
+ !serial8250_rx_dma_running(up))
serial8250_clear_and_reinit_fifos(up);
+ if (serial8250_rx_dma_recovering(up))
+ skip_rx = true;
+
/*
* If port is stopped and there are no error conditions in the
* FIFO, then don't drain the FIFO, as this may lead to TTY buffer
@@ -2345,6 +2352,12 @@ int serial8250_do_startup(struct uart_port *port)
guard(serial8250_rpm)(up);
+ /* The previous shutdown has drained DMA and its callbacks. */
+ scoped_guard(uart_port_lock_irqsave, port) {
+ if (up->dma)
+ up->dma->shutting_down = false;
+ }
+
serial8250_startup_special(port);
/*
@@ -2786,7 +2799,7 @@ static void serial8250_set_ier(struct uart_port *port, struct ktermios *termios)
if (up->capabilities & UART_CAP_RTOIE)
up->ier |= UART_IER_RTOIE;
- serial_port_out(port, UART_IER, up->ier);
+ serial8250_out_IER(up, up->ier);
}
static void serial8250_set_efr(struct uart_port *port, struct ktermios *termios)
--
2.53.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-24 6:40 ` [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock James Hilliard
@ 2026-10-25 5:10 ` Maciej W. Rozycki
2026-10-25 15:09 ` Andy Shevchenko
2026-10-25 18:47 ` James Hilliard
0 siblings, 2 replies; 16+ messages in thread
From: Maciej W. Rozycki @ 2026-10-25 5:10 UTC (permalink / raw)
To: James Hilliard
Cc: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Timur Tabi, linux-kernel,
linux-serial, linux-arm-kernel, imx, linuxppc-dev
On Sat, 24 Oct 2026, James Hilliard wrote:
> uart_write_wakeup() expects the UART port lock to be held. Several worker,
> DMA completion and interrupt paths call it without that lock, including
> indirect calls from uart_handle_cts_change() and the transmit helpers.
Fair enough, and thank you for the cleanup and spotting the issue in the
first place, however...
> diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
> index b4e1b90e5960..b15f76acd06b 100644
> --- a/drivers/tty/serial/sb1250-duart.c
> +++ b/drivers/tty/serial/sb1250-duart.c
> @@ -454,10 +454,12 @@ static irqreturn_t sbd_interrupt(int irq, void *dev_id)
>
> if (intstat & M_DUART_ISR_RX)
> sbd_receive_chars(sport);
> - if (intstat & M_DUART_ISR_IN)
> - sbd_status_handle(sport);
> - if (intstat & M_DUART_ISR_TX)
> - sbd_transmit_chars(sport);
> + scoped_guard(uart_port_lock_irqsave, uport) {
> + if (intstat & M_DUART_ISR_IN)
> + sbd_status_handle(sport);
> + if (intstat & M_DUART_ISR_TX)
> + sbd_transmit_chars(sport);
> + }
... why do you think the whole functions need to be called with the lock
held while they may or may not call the handlers in question even? I
think we should avoid excessive locking where possible to avoid holding
execution elsewhere unnecessarily, shouldn't we?
Maciej
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 5:10 ` Maciej W. Rozycki
@ 2026-10-25 15:09 ` Andy Shevchenko
2026-10-25 20:25 ` James Hilliard
2026-10-25 18:47 ` James Hilliard
1 sibling, 1 reply; 16+ messages in thread
From: Andy Shevchenko @ 2026-10-25 15:09 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: James Hilliard, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Sun, Oct 25, 2026 at 05:10:03AM +0000, Maciej W. Rozycki wrote:
> On Sat, 24 Oct 2026, James Hilliard wrote:
>
> > uart_write_wakeup() expects the UART port lock to be held. Several worker,
> > DMA completion and interrupt paths call it without that lock, including
> > indirect calls from uart_handle_cts_change() and the transmit helpers.
>
> Fair enough, and thank you for the cleanup and spotting the issue in the
> first place, however...
He already issues a v6. I think the tactics against AI contributions should be
to not answer for a week or so until there will be a quiescent week. At least
that's what I learn from this patch storm.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 5:10 ` Maciej W. Rozycki
2026-10-25 15:09 ` Andy Shevchenko
@ 2026-10-25 18:47 ` James Hilliard
1 sibling, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-25 18:47 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Andy Shevchenko, Heikki Krogerus,
Tony Lindgren, Sebastian Andrzej Siewior, Richard Genoud,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jacky Huang,
Shan-Chun Hung, Pali Rohár, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Timur Tabi, linux-kernel,
linux-serial, linux-arm-kernel, imx, linuxppc-dev
On Sat, Oct 24, 2026 at 11:10 PM Maciej W. Rozycki <macro@orcam.me.uk> wrote:
>
> On Sat, 24 Oct 2026, James Hilliard wrote:
>
> > uart_write_wakeup() expects the UART port lock to be held. Several worker,
> > DMA completion and interrupt paths call it without that lock, including
> > indirect calls from uart_handle_cts_change() and the transmit helpers.
>
> Fair enough, and thank you for the cleanup and spotting the issue in the
> first place, however...
>
> > diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
> > index b4e1b90e5960..b15f76acd06b 100644
> > --- a/drivers/tty/serial/sb1250-duart.c
> > +++ b/drivers/tty/serial/sb1250-duart.c
> > @@ -454,10 +454,12 @@ static irqreturn_t sbd_interrupt(int irq, void *dev_id)
> >
> > if (intstat & M_DUART_ISR_RX)
> > sbd_receive_chars(sport);
> > - if (intstat & M_DUART_ISR_IN)
> > - sbd_status_handle(sport);
> > - if (intstat & M_DUART_ISR_TX)
> > - sbd_transmit_chars(sport);
> > + scoped_guard(uart_port_lock_irqsave, uport) {
> > + if (intstat & M_DUART_ISR_IN)
> > + sbd_status_handle(sport);
> > + if (intstat & M_DUART_ISR_TX)
> > + sbd_transmit_chars(sport);
> > + }
>
> ... why do you think the whole functions need to be called with the lock
> held while they may or may not call the handlers in question even?
For sbd_transmit_chars(), locking only uart_write_wakeup() would leave the
empty-FIFO decision and TX interrupt masking racing with start_tx(). A
writer could queue data and enable TX after the empty check, then the
interrupt handler disables TX interrupts and leaves that output stalled.
The same lock protects x_char handling and FIFO consumption against
concurrent updates and flushing. These races predate this series, but
wakeup-only locking would leave them unresolved. I'd keep the complete TX
operation locked; each invocation sends at most one character without a
FIFO-drain wait.
> I
> think we should avoid excessive locking where possible to avoid holding
> execution elsewhere unnecessarily, shouldn't we?
For sbd_status_handle(), I'd read and filter the change status first, take
the lock around the CTS/DSR updates, and release it before waking
modem-status waiters.
I'd also move the TX lock into sbd_transmit_chars(), so RX-only interrupts
don't acquire it. That means two acquisitions when both status and TX need
servicing, in exchange for narrower critical sections.
>
> Maciej
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 15:09 ` Andy Shevchenko
@ 2026-10-25 20:25 ` James Hilliard
2026-10-25 20:44 ` Andy Shevchenko
0 siblings, 1 reply; 16+ messages in thread
From: James Hilliard @ 2026-10-25 20:25 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Sun, Oct 25, 2026 at 9:10 AM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Sun, Oct 25, 2026 at 05:10:03AM +0000, Maciej W. Rozycki wrote:
> > On Sat, 24 Oct 2026, James Hilliard wrote:
> >
> > > uart_write_wakeup() expects the UART port lock to be held. Several worker,
> > > DMA completion and interrupt paths call it without that lock, including
> > > indirect calls from uart_handle_cts_change() and the transmit helpers.
> >
> > Fair enough, and thank you for the cleanup and spotting the issue in the
> > first place, however...
>
> He already issues a v6. I think the tactics against AI contributions should be
> to not answer for a week or so until there will be a quiescent week.
At least with the workflow I've been using, a good signal for quiescence is
simply whether or not the Sashiko reviewer found issues.
I am feeding the Sashiko prompts into my local agent but it seems to
sometimes miss issues that get flagged by the public Sashiko reviewer
still.
I've been attempting to peel off independently reviewable fixes where
feasible as well to minimize the amount of patches/code during respins
as well.
> At least that's what I learn from this patch storm.
Whenever my local branch accumulates significant refactoring/changes
due to Sashiko or manual reviews I generally try and send out the new
revision fairly quickly in an attempt to avoid reviewers wasting time
reviewing code that is obsoleted by the new fixes/changes in my local
branch.
Locally I actually have far more UART related patches in an integration
tree that I've held off on sending for now since they are dependent upon
the prerequisite fixes I have already sent. My unposted patches actually
have an even larger amount of code changes than all the UART related
patches that I've posted so far, at least when measured in terms of total
lines of code changed.
The large amount of patches I ended up with seems to have mostly come
about due to the AI agent identifying most of these bugs while iterating on
and debugging new features(most of which are still largely unsent so far).
The strategy I've been using is to send small isolated fixes separately and
then reference them as needed in the larger series that depend on them.
I figure this should make the changes easier to review than having them
all in a larger series. I contributed the changes[0] to make this compatible
with the Sashiko reviewer actually.
I also use a custom AI agent controlled hardware in the loop test harness
which makes iterating/testing changes much faster since the AI agent can
autonomously test and iterate on changes far faster than is possible to do
manually. This test setup also allows much more thorough testing as a lot
of error handling or uncommonly hit code paths can then be tested using
AI generated fault injection tests on real hardware tailored to the specific
fix being validated. I manually review the final changes before sending as
well of course.
I'm constantly refining these patch development/review workflows as well
so feel free to give me any suggestions for anything I should change to
make review easier.
[0] https://github.com/sashiko-dev/sashiko/pull/389
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 20:25 ` James Hilliard
@ 2026-10-25 20:44 ` Andy Shevchenko
2026-10-25 22:11 ` James Hilliard
0 siblings, 1 reply; 16+ messages in thread
From: Andy Shevchenko @ 2026-10-25 20:44 UTC (permalink / raw)
To: James Hilliard
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
> On Sun, Oct 25, 2026 at 9:10 AM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Sun, Oct 25, 2026 at 05:10:03AM +0000, Maciej W. Rozycki wrote:
> > > On Sat, 24 Oct 2026, James Hilliard wrote:
> > >
> > > > uart_write_wakeup() expects the UART port lock to be held. Several worker,
> > > > DMA completion and interrupt paths call it without that lock, including
> > > > indirect calls from uart_handle_cts_change() and the transmit helpers.
> > >
> > > Fair enough, and thank you for the cleanup and spotting the issue in the
> > > first place, however...
> >
> > He already issues a v6. I think the tactics against AI contributions should be
> > to not answer for a week or so until there will be a quiescent week.
>
> At least with the workflow I've been using, a good signal for quiescence is
> simply whether or not the Sashiko reviewer found issues.
>
> I am feeding the Sashiko prompts into my local agent but it seems to
> sometimes miss issues that get flagged by the public Sashiko reviewer
> still.
>
> I've been attempting to peel off independently reviewable fixes where
> feasible as well to minimize the amount of patches/code during respins
> as well.
>
> > At least that's what I learn from this patch storm.
>
> Whenever my local branch accumulates significant refactoring/changes
> due to Sashiko or manual reviews I generally try and send out the new
> revision fairly quickly in an attempt to avoid reviewers wasting time
> reviewing code that is obsoleted by the new fixes/changes in my local
> branch.
>
> Locally I actually have far more UART related patches in an integration
> tree that I've held off on sending for now since they are dependent upon
> the prerequisite fixes I have already sent. My unposted patches actually
> have an even larger amount of code changes than all the UART related
> patches that I've posted so far, at least when measured in terms of total
> lines of code changed.
>
> The large amount of patches I ended up with seems to have mostly come
> about due to the AI agent identifying most of these bugs while iterating on
> and debugging new features(most of which are still largely unsent so far).
>
> The strategy I've been using is to send small isolated fixes separately and
> then reference them as needed in the larger series that depend on them.
> I figure this should make the changes easier to review than having them
> all in a larger series. I contributed the changes[0] to make this compatible
> with the Sashiko reviewer actually.
>
> I also use a custom AI agent controlled hardware in the loop test harness
> which makes iterating/testing changes much faster since the AI agent can
> autonomously test and iterate on changes far faster than is possible to do
> manually. This test setup also allows much more thorough testing as a lot
> of error handling or uncommonly hit code paths can then be tested using
> AI generated fault injection tests on real hardware tailored to the specific
> fix being validated. I manually review the final changes before sending as
> well of course.
>
> I'm constantly refining these patch development/review workflows as well
> so feel free to give me any suggestions for anything I should change to
> make review easier.
I already gave one: slow down. Better think than loop through AI and keep
Sashiko busy. Your patches are valuable, no questions, but they are way
too complicated to catch on withing an hours or a couple of days time frame.
Some people might want to test the stuff. And sending too often you actually
will get the opposite results, people will be tired to even start reviewing
and/or testing your code as it's changed way too quickly. Settle down first
with something, put on the cover letter the answer why not all Sashiko's
comments are being addressed (for example, due to amount of patches or the
series being focused on a certain issue).
> [0] https://github.com/sashiko-dev/sashiko/pull/389
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 20:44 ` Andy Shevchenko
@ 2026-10-25 22:11 ` James Hilliard
2026-10-26 9:31 ` Andy Shevchenko
0 siblings, 1 reply; 16+ messages in thread
From: James Hilliard @ 2026-10-25 22:11 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Sun, Oct 25, 2026 at 2:44 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
> > On Sun, Oct 25, 2026 at 9:10 AM Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> wrote:
> > > On Sun, Oct 25, 2026 at 05:10:03AM +0000, Maciej W. Rozycki wrote:
> > > > On Sat, 24 Oct 2026, James Hilliard wrote:
> > > >
> > > > > uart_write_wakeup() expects the UART port lock to be held. Several worker,
> > > > > DMA completion and interrupt paths call it without that lock, including
> > > > > indirect calls from uart_handle_cts_change() and the transmit helpers.
> > > >
> > > > Fair enough, and thank you for the cleanup and spotting the issue in the
> > > > first place, however...
> > >
> > > He already issues a v6. I think the tactics against AI contributions should be
> > > to not answer for a week or so until there will be a quiescent week.
> >
> > At least with the workflow I've been using, a good signal for quiescence is
> > simply whether or not the Sashiko reviewer found issues.
> >
> > I am feeding the Sashiko prompts into my local agent but it seems to
> > sometimes miss issues that get flagged by the public Sashiko reviewer
> > still.
> >
> > I've been attempting to peel off independently reviewable fixes where
> > feasible as well to minimize the amount of patches/code during respins
> > as well.
> >
> > > At least that's what I learn from this patch storm.
> >
> > Whenever my local branch accumulates significant refactoring/changes
> > due to Sashiko or manual reviews I generally try and send out the new
> > revision fairly quickly in an attempt to avoid reviewers wasting time
> > reviewing code that is obsoleted by the new fixes/changes in my local
> > branch.
> >
> > Locally I actually have far more UART related patches in an integration
> > tree that I've held off on sending for now since they are dependent upon
> > the prerequisite fixes I have already sent. My unposted patches actually
> > have an even larger amount of code changes than all the UART related
> > patches that I've posted so far, at least when measured in terms of total
> > lines of code changed.
> >
> > The large amount of patches I ended up with seems to have mostly come
> > about due to the AI agent identifying most of these bugs while iterating on
> > and debugging new features(most of which are still largely unsent so far).
> >
> > The strategy I've been using is to send small isolated fixes separately and
> > then reference them as needed in the larger series that depend on them.
> > I figure this should make the changes easier to review than having them
> > all in a larger series. I contributed the changes[0] to make this compatible
> > with the Sashiko reviewer actually.
> >
> > I also use a custom AI agent controlled hardware in the loop test harness
> > which makes iterating/testing changes much faster since the AI agent can
> > autonomously test and iterate on changes far faster than is possible to do
> > manually. This test setup also allows much more thorough testing as a lot
> > of error handling or uncommonly hit code paths can then be tested using
> > AI generated fault injection tests on real hardware tailored to the specific
> > fix being validated. I manually review the final changes before sending as
> > well of course.
> >
> > I'm constantly refining these patch development/review workflows as well
> > so feel free to give me any suggestions for anything I should change to
> > make review easier.
>
> I already gave one: slow down. Better think than loop through AI and keep
> Sashiko busy. Your patches are valuable, no questions, but they are way
> too complicated to catch on withing an hours or a couple of days time frame.
Yeah, I was already slowing down quite a bit. I probably should have started
sending some of these earlier but I wanted to see how my final integration tree
would look like first so that I could better peel out what was actually needed
as preparatory fixes in ways that would fit well with subsequent features.
> Some people might want to test the stuff. And sending too often you actually
> will get the opposite results, people will be tired to even start reviewing
> and/or testing your code as it's changed way too quickly.
My thought process was that people wouldn't want to be testing code that
had known bugs flagged in Sashiko reviews.
> Settle down first
> with something, put on the cover letter the answer why not all Sashiko's
> comments are being addressed (for example, due to amount of patches or the
> series being focused on a certain issue).
So, in general I've been always attempting to resolve known Sashiko issues
in every subsequent re-spin, so that would be a bit tricky since I wouldn't be
aware of the Sashiko issues until after the series has already been sent.
Usually the subsequent issues that get flagged are new issues only discovered
after each new re-spin.
I do have a bunch of UART/DMA related patches that don't have flagged Sashiko
issues so it probably makes sense to try and review those first while the larger
series that have Sashiko flagged issues are still stabilizing.
>
> > [0] https://github.com/sashiko-dev/sashiko/pull/389
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-25 22:11 ` James Hilliard
@ 2026-10-26 9:31 ` Andy Shevchenko
2026-10-26 16:45 ` James Hilliard
0 siblings, 1 reply; 16+ messages in thread
From: Andy Shevchenko @ 2026-10-26 9:31 UTC (permalink / raw)
To: James Hilliard
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Sun, Oct 25, 2026 at 04:11:08PM -0600, James Hilliard wrote:
> On Sun, Oct 25, 2026 at 2:44 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
...
> > Some people might want to test the stuff. And sending too often you actually
> > will get the opposite results, people will be tired to even start reviewing
> > and/or testing your code as it's changed way too quickly.
>
> My thought process was that people wouldn't want to be testing code that
> had known bugs flagged in Sashiko reviews.
Sashiko reviews are not visible in my or any other mailbox. To see them I have
to go to some URL and so on, meaning to waste time to see why on the earth the
new version has been sent. It's better before addressing Sashiko comments,
reply to all people involved and to Sashiko's original email, so it will be
visible in the previous version why you issued (actually about to issue) the
next version. And before even issuing it, wait for a while, people might
disagree with Sashiko judgement or your replies on how to address that.
TL;DR: this mailing list is *not* a playground with you and Sashiko, the whole
community is behind it.
> > Settle down first
> > with something, put on the cover letter the answer why not all Sashiko's
> > comments are being addressed (for example, due to amount of patches or the
> > series being focused on a certain issue).
>
> So, in general I've been always attempting to resolve known Sashiko issues
> in every subsequent re-spin, so that would be a bit tricky since I wouldn't be
> aware of the Sashiko issues until after the series has already been sent.
>
> Usually the subsequent issues that get flagged are new issues only discovered
> after each new re-spin.
>
> I do have a bunch of UART/DMA related patches that don't have flagged Sashiko
> issues so it probably makes sense to try and review those first while the larger
> series that have Sashiko flagged issues are still stabilizing.
Yes.
> > > [0] https://github.com/sashiko-dev/sashiko/pull/389
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-26 9:31 ` Andy Shevchenko
@ 2026-10-26 16:45 ` James Hilliard
2026-10-26 19:53 ` Andy Shevchenko
0 siblings, 1 reply; 16+ messages in thread
From: James Hilliard @ 2026-10-26 16:45 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Mon, Oct 26, 2026 at 3:33 AM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Sun, Oct 25, 2026 at 04:11:08PM -0600, James Hilliard wrote:
> > On Sun, Oct 25, 2026 at 2:44 PM Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> wrote:
> > > On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
>
> ...
>
> > > Some people might want to test the stuff. And sending too often you actually
> > > will get the opposite results, people will be tired to even start reviewing
> > > and/or testing your code as it's changed way too quickly.
> >
> > My thought process was that people wouldn't want to be testing code that
> > had known bugs flagged in Sashiko reviews.
>
> Sashiko reviews are not visible in my or any other mailbox. To see them I have
> to go to some URL and so on, meaning to waste time to see why on the earth the
> new version has been sent.
Maybe the Sashiko reviews are getting caught in spam filtering or something?
They are showing up for me and on the mailing list[0] so I'm a bit confused why
you aren't seeing them. You shouldn't have to go to the web interface[1] to look
up the reviews.
[0] https://lore.kernel.org/all/sashiko-outbox-183574@kernel.org/
[1] https://sashiko.dev/
> It's better before addressing Sashiko comments,
> reply to all people involved and to Sashiko's original email, so it will be
> visible in the previous version why you issued (actually about to issue) the
> next version.
Hmm, maybe it makes sense to enable recipients in the reply_to config[2]?
[2] https://github.com/sashiko-dev/sashiko/blob/v.0.6.3/projects/linux/mailing_lists.toml#L290
> And before even issuing it, wait for a while, people might
> disagree with Sashiko judgement or your replies on how to address that.
I was assuming everyone was seeing the Sashiko reviews getting posted to
the mailing list already. The are ending up in lore so unless someone isn't
subscribed to the list I'm not sure why they wouldn't be seeing those reviews
already.
>
> TL;DR: this mailing list is *not* a playground with you and Sashiko, the whole
> community is behind it.
>
> > > Settle down first
> > > with something, put on the cover letter the answer why not all Sashiko's
> > > comments are being addressed (for example, due to amount of patches or the
> > > series being focused on a certain issue).
> >
> > So, in general I've been always attempting to resolve known Sashiko issues
> > in every subsequent re-spin, so that would be a bit tricky since I wouldn't be
> > aware of the Sashiko issues until after the series has already been sent.
> >
> > Usually the subsequent issues that get flagged are new issues only discovered
> > after each new re-spin.
> >
> > I do have a bunch of UART/DMA related patches that don't have flagged Sashiko
> > issues so it probably makes sense to try and review those first while the larger
> > series that have Sashiko flagged issues are still stabilizing.
>
> Yes.
>
> > > > [0] https://github.com/sashiko-dev/sashiko/pull/389
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-26 16:45 ` James Hilliard
@ 2026-10-26 19:53 ` Andy Shevchenko
2026-10-26 19:59 ` James Hilliard
0 siblings, 1 reply; 16+ messages in thread
From: Andy Shevchenko @ 2026-10-26 19:53 UTC (permalink / raw)
To: James Hilliard
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Mon, Oct 26, 2026 at 10:45:54AM -0600, James Hilliard wrote:
> On Mon, Oct 26, 2026 at 3:33 AM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Sun, Oct 25, 2026 at 04:11:08PM -0600, James Hilliard wrote:
> > > On Sun, Oct 25, 2026 at 2:44 PM Andy Shevchenko
> > > <andriy.shevchenko@linux.intel.com> wrote:
> > > > On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
...
> > > > Some people might want to test the stuff. And sending too often you actually
> > > > will get the opposite results, people will be tired to even start reviewing
> > > > and/or testing your code as it's changed way too quickly.
> > >
> > > My thought process was that people wouldn't want to be testing code that
> > > had known bugs flagged in Sashiko reviews.
> >
> > Sashiko reviews are not visible in my or any other mailbox. To see them I have
> > to go to some URL and so on, meaning to waste time to see why on the earth the
> > new version has been sent.
>
> Maybe the Sashiko reviews are getting caught in spam filtering or something?
> They are showing up for me and on the mailing list[0] so I'm a bit confused why
> you aren't seeing them. You shouldn't have to go to the web interface[1] to look
> up the reviews.
>
> [0] https://lore.kernel.org/all/sashiko-outbox-183574@kernel.org/
>
> [1] https://sashiko.dev/
>
> > It's better before addressing Sashiko comments,
> > reply to all people involved and to Sashiko's original email, so it will be
> > visible in the previous version why you issued (actually about to issue) the
> > next version.
>
> Hmm, maybe it makes sense to enable recipients in the reply_to config[2]?
It's up to the list owners / subsystem maintainers.
> [2] https://github.com/sashiko-dev/sashiko/blob/v.0.6.3/projects/linux/mailing_lists.toml#L290
>
> > And before even issuing it, wait for a while, people might
> > disagree with Sashiko judgement or your replies on how to address that.
>
> I was assuming everyone was seeing the Sashiko reviews getting posted to
> the mailing list already. The are ending up in lore so unless someone isn't
> subscribed to the list I'm not sure why they wouldn't be seeing those reviews
> already.
I assume you already answered your Q. mailing list in this sense doesn't mean
recipients. I think it simply goes to the mailing list without forwarding.
You can ask at helpdesk@ how this is configured on the kernel.org side.
> > TL;DR: this mailing list is *not* a playground with you and Sashiko, the whole
> > community is behind it.
> > > > Settle down first
> > > > with something, put on the cover letter the answer why not all Sashiko's
> > > > comments are being addressed (for example, due to amount of patches or the
> > > > series being focused on a certain issue).
> > >
> > > So, in general I've been always attempting to resolve known Sashiko issues
> > > in every subsequent re-spin, so that would be a bit tricky since I wouldn't be
> > > aware of the Sashiko issues until after the series has already been sent.
> > >
> > > Usually the subsequent issues that get flagged are new issues only discovered
> > > after each new re-spin.
> > >
> > > I do have a bunch of UART/DMA related patches that don't have flagged Sashiko
> > > issues so it probably makes sense to try and review those first while the larger
> > > series that have Sashiko flagged issues are still stabilizing.
> >
> > Yes.
> >
> > > > > [0] https://github.com/sashiko-dev/sashiko/pull/389
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock
2026-10-26 19:53 ` Andy Shevchenko
@ 2026-10-26 19:59 ` James Hilliard
0 siblings, 0 replies; 16+ messages in thread
From: James Hilliard @ 2026-10-26 19:59 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Maciej W. Rozycki, Greg Kroah-Hartman, Jiri Slaby, Raul E Rangel,
Ilpo Järvinen, Heikki Krogerus, Tony Lindgren,
Sebastian Andrzej Siewior, Richard Genoud, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Jacky Huang, Shan-Chun Hung,
Pali Rohár, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Timur Tabi, linux-kernel, linux-serial,
linux-arm-kernel, imx, linuxppc-dev
On Mon, Oct 26, 2026 at 1:53 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Mon, Oct 26, 2026 at 10:45:54AM -0600, James Hilliard wrote:
> > On Mon, Oct 26, 2026 at 3:33 AM Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> wrote:
> > > On Sun, Oct 25, 2026 at 04:11:08PM -0600, James Hilliard wrote:
> > > > On Sun, Oct 25, 2026 at 2:44 PM Andy Shevchenko
> > > > <andriy.shevchenko@linux.intel.com> wrote:
> > > > > On Sun, Oct 25, 2026 at 02:25:46PM -0600, James Hilliard wrote:
>
> ...
>
> > > > > Some people might want to test the stuff. And sending too often you actually
> > > > > will get the opposite results, people will be tired to even start reviewing
> > > > > and/or testing your code as it's changed way too quickly.
> > > >
> > > > My thought process was that people wouldn't want to be testing code that
> > > > had known bugs flagged in Sashiko reviews.
> > >
> > > Sashiko reviews are not visible in my or any other mailbox. To see them I have
> > > to go to some URL and so on, meaning to waste time to see why on the earth the
> > > new version has been sent.
> >
> > Maybe the Sashiko reviews are getting caught in spam filtering or something?
> > They are showing up for me and on the mailing list[0] so I'm a bit confused why
> > you aren't seeing them. You shouldn't have to go to the web interface[1] to look
> > up the reviews.
> >
> > [0] https://lore.kernel.org/all/sashiko-outbox-183574@kernel.org/
> >
> > [1] https://sashiko.dev/
> >
> > > It's better before addressing Sashiko comments,
> > > reply to all people involved and to Sashiko's original email, so it will be
> > > visible in the previous version why you issued (actually about to issue) the
> > > next version.
> >
> > Hmm, maybe it makes sense to enable recipients in the reply_to config[2]?
>
> It's up to the list owners / subsystem maintainers.
>
> > [2] https://github.com/sashiko-dev/sashiko/blob/v.0.6.3/projects/linux/mailing_lists.toml#L290
> >
> > > And before even issuing it, wait for a while, people might
> > > disagree with Sashiko judgement or your replies on how to address that.
> >
> > I was assuming everyone was seeing the Sashiko reviews getting posted to
> > the mailing list already. The are ending up in lore so unless someone isn't
> > subscribed to the list I'm not sure why they wouldn't be seeing those reviews
> > already.
>
> I assume you already answered your Q. mailing list in this sense doesn't mean
> recipients. I think it simply goes to the mailing list without forwarding.
Do we want those reviews going to recipients as well as the mailing list?
>
> You can ask at helpdesk@ how this is configured on the kernel.org side.
>
> > > TL;DR: this mailing list is *not* a playground with you and Sashiko, the whole
> > > community is behind it.
>
> > > > > Settle down first
> > > > > with something, put on the cover letter the answer why not all Sashiko's
> > > > > comments are being addressed (for example, due to amount of patches or the
> > > > > series being focused on a certain issue).
> > > >
> > > > So, in general I've been always attempting to resolve known Sashiko issues
> > > > in every subsequent re-spin, so that would be a bit tricky since I wouldn't be
> > > > aware of the Sashiko issues until after the series has already been sent.
> > > >
> > > > Usually the subsequent issues that get flagged are new issues only discovered
> > > > after each new re-spin.
> > > >
> > > > I do have a bunch of UART/DMA related patches that don't have flagged Sashiko
> > > > issues so it probably makes sense to try and review those first while the larger
> > > > series that have Sashiko flagged issues are still stabilizing.
> > >
> > > Yes.
> > >
> > > > > > [0] https://github.com/sashiko-dev/sashiko/pull/389
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-10-26 19:59 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-24 6:40 [PATCH v5 0/5] serial: 8250: serialize DMA cancellation with buffer reuse James Hilliard
2026-10-24 6:40 ` [PATCH v5 1/5] serial: 8250_omap: separate locked TX DMA completion processing James Hilliard
2026-10-24 6:40 ` [PATCH v5 2/5] serial: serialize asynchronous write wakeups with the port lock James Hilliard
2026-10-25 5:10 ` Maciej W. Rozycki
2026-10-25 15:09 ` Andy Shevchenko
2026-10-25 20:25 ` James Hilliard
2026-10-25 20:44 ` Andy Shevchenko
2026-10-25 22:11 ` James Hilliard
2026-10-26 9:31 ` Andy Shevchenko
2026-10-26 16:45 ` James Hilliard
2026-10-26 19:53 ` Andy Shevchenko
2026-10-26 19:59 ` James Hilliard
2026-10-25 18:47 ` James Hilliard
2026-10-24 6:40 ` [PATCH v5 3/5] serial: core: allow drivers to reserve flushed transmit storage James Hilliard
2026-10-24 6:40 ` [PATCH v5 4/5] serial: 8250: drain canceled TX DMA before reusing its buffer James Hilliard
2026-10-24 6:40 ` [PATCH v5 5/5] serial: 8250: retire canceled RX DMA before reusing the receive buffer James Hilliard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®