linux-serial.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] tty/serial: Fix tcsetattr() TCSADRAIN/FLUSH and write() races
@ 2023-03-17 11:33 Ilpo Järvinen
  2023-03-17 11:33 ` [PATCH v2 1/2] tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH Ilpo Järvinen
  2023-03-17 11:33 ` [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx Ilpo Järvinen
  0 siblings, 2 replies; 5+ messages in thread
From: Ilpo Järvinen @ 2023-03-17 11:33 UTC (permalink / raw)
  To: linux-serial, Greg Kroah-Hartman, Jiri Slaby, David Laight
  Cc: linux-kernel, Ilpo Järvinen

Fix two races related TCSADRAIN/FLUSH. One in tty core and the other
that can occur when 8250 UART is using DMA for Tx.

These might be slightly controversial as it requires userspace to race
write() with tcsetattr() to trigger these races. But since the races
still seem fixable on kernel side, I made these patches.

v2:
- Switch DMA Tx running / LSR Tx empty checks order due to fast/slowness
- Calculate the result in serial8250_tx_empty() directly

Ilpo Järvinen (2):
  tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH
  serial: 8250: Fix serial8250_tx_empty() race with DMA Tx

 drivers/tty/serial/8250/8250.h      | 12 ++++++++
 drivers/tty/serial/8250/8250_port.c |  7 +++--
 drivers/tty/tty.h                   |  2 ++
 drivers/tty/tty_io.c                |  4 +--
 drivers/tty/tty_ioctl.c             | 45 +++++++++++++++++++++--------
 5 files changed, 53 insertions(+), 17 deletions(-)

-- 
2.30.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/2] tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH
  2023-03-17 11:33 [PATCH v2 0/2] tty/serial: Fix tcsetattr() TCSADRAIN/FLUSH and write() races Ilpo Järvinen
@ 2023-03-17 11:33 ` Ilpo Järvinen
  2023-03-17 11:33 ` [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx Ilpo Järvinen
  1 sibling, 0 replies; 5+ messages in thread
From: Ilpo Järvinen @ 2023-03-17 11:33 UTC (permalink / raw)
  To: linux-serial, Greg Kroah-Hartman, Jiri Slaby, David Laight, linux-kernel
  Cc: Ilpo Järvinen, stable

If userspace races tcsetattr() with a write, the drained condition
might not be guaranteed by the kernel. There is a race window after
checking Tx is empty before tty_set_termios() takes termios_rwsem for
write. During that race window, more characters can be queued by a
racing writer.

Any ongoing transmission might produce garbage during HW's
->set_termios() call. The intent of TCSADRAIN/FLUSH seems to be
preventing such a character corruption. If those flags are set, take
tty's write lock to stop any writer before performing the lower layer
Tx empty check and wait for the pending characters to be sent (if any).

The initial wait for all-writers-done must be placed outside of tty's
write lock to avoid deadlock which makes it impossible to use
tty_wait_until_sent(). The write lock is retried if a racing write is
detected.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 drivers/tty/tty.h       |  2 ++
 drivers/tty/tty_io.c    |  4 ++--
 drivers/tty/tty_ioctl.c | 45 ++++++++++++++++++++++++++++++-----------
 3 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h
index f45cd683c02e..1e0d80e98d26 100644
--- a/drivers/tty/tty.h
+++ b/drivers/tty/tty.h
@@ -62,6 +62,8 @@ int __tty_check_change(struct tty_struct *tty, int sig);
 int tty_check_change(struct tty_struct *tty);
 void __stop_tty(struct tty_struct *tty);
 void __start_tty(struct tty_struct *tty);
+void tty_write_unlock(struct tty_struct *tty);
+int tty_write_lock(struct tty_struct *tty, int ndelay);
 void tty_vhangup_session(struct tty_struct *tty);
 void tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty);
 int tty_signal_session_leader(struct tty_struct *tty, int exit_session);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 766750e355ac..cfb3da0dee47 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -933,13 +933,13 @@ static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
 	return i;
 }
 
-static void tty_write_unlock(struct tty_struct *tty)
+void tty_write_unlock(struct tty_struct *tty)
 {
 	mutex_unlock(&tty->atomic_write_lock);
 	wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
 }
 
-static int tty_write_lock(struct tty_struct *tty, int ndelay)
+int tty_write_lock(struct tty_struct *tty, int ndelay)
 {
 	if (!mutex_trylock(&tty->atomic_write_lock)) {
 		if (ndelay)
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 12983ce4e43e..a13e3797c477 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -500,21 +500,42 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
 	tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
 	tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
 
-	ld = tty_ldisc_ref(tty);
+	if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
+retry_write_wait:
+		retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));
+		if (retval < 0)
+			return retval;
 
-	if (ld != NULL) {
-		if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
-			ld->ops->flush_buffer(tty);
-		tty_ldisc_deref(ld);
-	}
+		if (tty_write_lock(tty, 0) < 0)
+			goto retry_write_wait;
 
-	if (opt & TERMIOS_WAIT) {
-		tty_wait_until_sent(tty, 0);
-		if (signal_pending(current))
-			return -ERESTARTSYS;
-	}
+		/* Racing writer? */
+		if (tty_chars_in_buffer(tty)) {
+			tty_write_unlock(tty);
+			goto retry_write_wait;
+		}
 
-	tty_set_termios(tty, &tmp_termios);
+		ld = tty_ldisc_ref(tty);
+		if (ld != NULL) {
+			if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
+				ld->ops->flush_buffer(tty);
+			tty_ldisc_deref(ld);
+		}
+
+		if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
+			tty->ops->wait_until_sent(tty, 0);
+			if (signal_pending(current)) {
+				tty_write_unlock(tty);
+				return -ERESTARTSYS;
+			}
+		}
+
+		tty_set_termios(tty, &tmp_termios);
+
+		tty_write_unlock(tty);
+	} else {
+		tty_set_termios(tty, &tmp_termios);
+	}
 
 	/* FIXME: Arguably if tmp_termios == tty->termios AND the
 	   actual requested termios was not tmp_termios then we may
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx
  2023-03-17 11:33 [PATCH v2 0/2] tty/serial: Fix tcsetattr() TCSADRAIN/FLUSH and write() races Ilpo Järvinen
  2023-03-17 11:33 ` [PATCH v2 1/2] tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH Ilpo Järvinen
@ 2023-03-17 11:33 ` Ilpo Järvinen
  2023-03-18 11:47   ` David Laight
  1 sibling, 1 reply; 5+ messages in thread
From: Ilpo Järvinen @ 2023-03-17 11:33 UTC (permalink / raw)
  To: linux-serial, Greg Kroah-Hartman, Jiri Slaby, David Laight,
	Heikki Krogerus, linux-kernel
  Cc: Ilpo Järvinen, stable

There's a potential race before THRE/TEMT deasserts when DMA Tx is
starting up (or the next batch of continuous Tx is being submitted).
This can lead to misdetecting Tx empty condition.

It is entirely normal for THRE/TEMT to be set for some time after the
DMA Tx had been setup in serial8250_tx_dma(). As Tx side is definitely
not empty at that point, it seems incorrect for serial8250_tx_empty()
claim Tx is empty.

Fix the race by also checking in serial8250_tx_empty() whether there's
DMA Tx active.

Note: This fix only addresses in-kernel race mainly to make using
TCSADRAIN/FLUSH robust. Userspace can still cause other races but they
seem userspace concurrency control problems.

Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
Cc: stable@vger.kernel.org
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 drivers/tty/serial/8250/8250.h      | 12 ++++++++++++
 drivers/tty/serial/8250/8250_port.c |  7 ++++---
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 287153d32536..1e8fe44a7099 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -365,6 +365,13 @@ static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p)
 	if (dma->prepare_rx_dma)
 		dma->prepare_rx_dma(p);
 }
+
+static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
+{
+	struct uart_8250_dma *dma = p->dma;
+
+	return dma && dma->tx_running;
+}
 #else
 static inline int serial8250_tx_dma(struct uart_8250_port *p)
 {
@@ -380,6 +387,11 @@ static inline int serial8250_request_dma(struct uart_8250_port *p)
 	return -1;
 }
 static inline void serial8250_release_dma(struct uart_8250_port *p) { }
+
+static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
+{
+	return false;
+}
 #endif
 
 static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index fa43df05342b..107bcdfb119c 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2005,18 +2005,19 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port)
 static unsigned int serial8250_tx_empty(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
+	unsigned int result = 0;
 	unsigned long flags;
-	u16 lsr;
 
 	serial8250_rpm_get(up);
 
 	spin_lock_irqsave(&port->lock, flags);
-	lsr = serial_lsr_in(up);
+	if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up)))
+		result = TIOCSER_TEMT;
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	serial8250_rpm_put(up);
 
-	return uart_lsr_tx_empty(lsr) ? TIOCSER_TEMT : 0;
+	return result;
 }
 
 unsigned int serial8250_do_get_mctrl(struct uart_port *port)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* RE: [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx
  2023-03-17 11:33 ` [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx Ilpo Järvinen
@ 2023-03-18 11:47   ` David Laight
  2023-03-20  9:00     ` Ilpo Järvinen
  0 siblings, 1 reply; 5+ messages in thread
From: David Laight @ 2023-03-18 11:47 UTC (permalink / raw)
  To: 'Ilpo Järvinen',
	linux-serial, Greg Kroah-Hartman, Jiri Slaby, Heikki Krogerus,
	linux-kernel
  Cc: stable

From: Ilpo Järvinen
> Sent: 17 March 2023 11:33
> To: linux-serial@vger.kernel.org; Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jiri Slaby
> 
> There's a potential race before THRE/TEMT deasserts when DMA Tx is
> starting up (or the next batch of continuous Tx is being submitted).
> This can lead to misdetecting Tx empty condition.
> 
> It is entirely normal for THRE/TEMT to be set for some time after the
> DMA Tx had been setup in serial8250_tx_dma(). As Tx side is definitely
> not empty at that point, it seems incorrect for serial8250_tx_empty()
> claim Tx is empty.
> 
> Fix the race by also checking in serial8250_tx_empty() whether there's
> DMA Tx active.
> 
> Note: This fix only addresses in-kernel race mainly to make using
> TCSADRAIN/FLUSH robust. Userspace can still cause other races but they
> seem userspace concurrency control problems.

Looks better, but I'm not sure it actually works.

If interrupts are being used to copy data to the tx fifo then
(depending on interrupt latency and exactly when the interrupt
is requested) the code might report 'tx empty' when the ISR
is about to copy in more data.

Now the drain/flush code might already have checked there is
no more data queued in the driver before calling this,
but more generally shouldn't it be checking:
	no_data_queued_in_driver && hardware_fifo_empty.

Any 'no_data_queued_in_driver' check would probably include
data that dma is copying - so the explicit dma check might
not be needed.

	David

> 
> Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
> Cc: stable@vger.kernel.org
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> ---
>  drivers/tty/serial/8250/8250.h      | 12 ++++++++++++
>  drivers/tty/serial/8250/8250_port.c |  7 ++++---
>  2 files changed, 16 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
> index 287153d32536..1e8fe44a7099 100644
> --- a/drivers/tty/serial/8250/8250.h
> +++ b/drivers/tty/serial/8250/8250.h
> @@ -365,6 +365,13 @@ static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p)
>  	if (dma->prepare_rx_dma)
>  		dma->prepare_rx_dma(p);
>  }
> +
> +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
> +{
> +	struct uart_8250_dma *dma = p->dma;
> +
> +	return dma && dma->tx_running;
> +}
>  #else
>  static inline int serial8250_tx_dma(struct uart_8250_port *p)
>  {
> @@ -380,6 +387,11 @@ static inline int serial8250_request_dma(struct uart_8250_port *p)
>  	return -1;
>  }
>  static inline void serial8250_release_dma(struct uart_8250_port *p) { }
> +
> +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
> +{
> +	return false;
> +}
>  #endif
> 
>  static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index fa43df05342b..107bcdfb119c 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -2005,18 +2005,19 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port)
>  static unsigned int serial8250_tx_empty(struct uart_port *port)
>  {
>  	struct uart_8250_port *up = up_to_u8250p(port);
> +	unsigned int result = 0;
>  	unsigned long flags;
> -	u16 lsr;
> 
>  	serial8250_rpm_get(up);
> 
>  	spin_lock_irqsave(&port->lock, flags);
> -	lsr = serial_lsr_in(up);
> +	if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up)))
> +		result = TIOCSER_TEMT;
>  	spin_unlock_irqrestore(&port->lock, flags);
> 
>  	serial8250_rpm_put(up);
> 
> -	return uart_lsr_tx_empty(lsr) ? TIOCSER_TEMT : 0;
> +	return result;
>  }
> 
>  unsigned int serial8250_do_get_mctrl(struct uart_port *port)
> --
> 2.30.2

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx
  2023-03-18 11:47   ` David Laight
@ 2023-03-20  9:00     ` Ilpo Järvinen
  0 siblings, 0 replies; 5+ messages in thread
From: Ilpo Järvinen @ 2023-03-20  9:00 UTC (permalink / raw)
  To: David Laight
  Cc: linux-serial, Greg Kroah-Hartman, Jiri Slaby, Heikki Krogerus,
	linux-kernel, stable

[-- Attachment #1: Type: text/plain, Size: 4905 bytes --]

On Sat, 18 Mar 2023, David Laight wrote:

> From: Ilpo Järvinen
> > Sent: 17 March 2023 11:33
> > To: linux-serial@vger.kernel.org; Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jiri Slaby
> > 
> > There's a potential race before THRE/TEMT deasserts when DMA Tx is
> > starting up (or the next batch of continuous Tx is being submitted).
> > This can lead to misdetecting Tx empty condition.
> > 
> > It is entirely normal for THRE/TEMT to be set for some time after the
> > DMA Tx had been setup in serial8250_tx_dma(). As Tx side is definitely
> > not empty at that point, it seems incorrect for serial8250_tx_empty()
> > claim Tx is empty.
> > 
> > Fix the race by also checking in serial8250_tx_empty() whether there's
> > DMA Tx active.
> > 
> > Note: This fix only addresses in-kernel race mainly to make using
> > TCSADRAIN/FLUSH robust. Userspace can still cause other races but they
> > seem userspace concurrency control problems.
> 
> Looks better, but I'm not sure it actually works.
> 
> If interrupts are being used to copy data to the tx fifo then
> (depending on interrupt latency and exactly when the interrupt
> is requested) the code might report 'tx empty' when the ISR
> is about to copy in more data.
>
> Now the drain/flush code might already have checked there is
> no more data queued in the driver before calling this,

Thanks for taking a look, it's really appreciated.

Yes, set_termios() checks for tty_chars_in_buffer() which calls into 
serial_core's ->chars_in_buffer(). This does check 
uart_circ_chars_pending() so it's not possible to have such chars in the 
circular buffer in the drain/flush case.

> but more generally shouldn't it be checking:
> 	no_data_queued_in_driver && hardware_fifo_empty.
> 
> Any 'no_data_queued_in_driver' check would probably include
> data that dma is copying - so the explicit dma check might
> not be needed.

What for you'd want this change? Refactor the code? uart_get_lsr_info() 
already does check for uart_circ_chars_pending() so what you'd want more?

I suppose uart_get_lsr_info() should hold port's lock across the checks 
though, having that wishful comment about a racing interrupt messing 
things up doesn't really help that much :-).

Also, now that I looked into uart_get_lsr_info() I guess 
uart_chars_in_buffer() should also consider x_char like 
uart_get_lsr_info() does.


-- 
 i.

> > Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> > ---
> >  drivers/tty/serial/8250/8250.h      | 12 ++++++++++++
> >  drivers/tty/serial/8250/8250_port.c |  7 ++++---
> >  2 files changed, 16 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
> > index 287153d32536..1e8fe44a7099 100644
> > --- a/drivers/tty/serial/8250/8250.h
> > +++ b/drivers/tty/serial/8250/8250.h
> > @@ -365,6 +365,13 @@ static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p)
> >  	if (dma->prepare_rx_dma)
> >  		dma->prepare_rx_dma(p);
> >  }
> > +
> > +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
> > +{
> > +	struct uart_8250_dma *dma = p->dma;
> > +
> > +	return dma && dma->tx_running;
> > +}
> >  #else
> >  static inline int serial8250_tx_dma(struct uart_8250_port *p)
> >  {
> > @@ -380,6 +387,11 @@ static inline int serial8250_request_dma(struct uart_8250_port *p)
> >  	return -1;
> >  }
> >  static inline void serial8250_release_dma(struct uart_8250_port *p) { }
> > +
> > +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
> > +{
> > +	return false;
> > +}
> >  #endif
> > 
> >  static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
> > diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> > index fa43df05342b..107bcdfb119c 100644
> > --- a/drivers/tty/serial/8250/8250_port.c
> > +++ b/drivers/tty/serial/8250/8250_port.c
> > @@ -2005,18 +2005,19 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port)
> >  static unsigned int serial8250_tx_empty(struct uart_port *port)
> >  {
> >  	struct uart_8250_port *up = up_to_u8250p(port);
> > +	unsigned int result = 0;
> >  	unsigned long flags;
> > -	u16 lsr;
> > 
> >  	serial8250_rpm_get(up);
> > 
> >  	spin_lock_irqsave(&port->lock, flags);
> > -	lsr = serial_lsr_in(up);
> > +	if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up)))
> > +		result = TIOCSER_TEMT;
> >  	spin_unlock_irqrestore(&port->lock, flags);
> > 
> >  	serial8250_rpm_put(up);
> > 
> > -	return uart_lsr_tx_empty(lsr) ? TIOCSER_TEMT : 0;
> > +	return result;
> >  }
> > 
> >  unsigned int serial8250_do_get_mctrl(struct uart_port *port)
> > --
> > 2.30.2
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-03-20  9:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-17 11:33 [PATCH v2 0/2] tty/serial: Fix tcsetattr() TCSADRAIN/FLUSH and write() races Ilpo Järvinen
2023-03-17 11:33 ` [PATCH v2 1/2] tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH Ilpo Järvinen
2023-03-17 11:33 ` [PATCH v2 2/2] serial: 8250: Fix serial8250_tx_empty() race with DMA Tx Ilpo Järvinen
2023-03-18 11:47   ` David Laight
2023-03-20  9:00     ` Ilpo Järvinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).