All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race
@ 2023-02-07  9:47 Ilpo Järvinen
  2023-02-07  9:47 ` [PATCH backport to 6.1 2/2] serial: 8250_dma: Fix DMA Rx rearm race Ilpo Järvinen
  2023-02-07 11:08 ` [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Greg Kroah-Hartman
  0 siblings, 2 replies; 3+ messages in thread
From: Ilpo Järvinen @ 2023-02-07  9:47 UTC (permalink / raw)
  To: stable; +Cc: Ilpo Järvinen, Gilles BULOZ, Greg Kroah-Hartman

[ Upstream commit 31352811e13dc2313f101b890fd4b1ce760b5fe7 ]

__dma_rx_complete() is called from two places:
  - Through the DMA completion callback dma_rx_complete()
  - From serial8250_rx_dma_flush() after IIR_RLSI or IIR_RX_TIMEOUT
The former does not hold port's lock during __dma_rx_complete() which
allows these two to race and potentially insert the same data twice.

Extend port's lock coverage in dma_rx_complete() to prevent the race
and check if the DMA Rx is still pending completion before calling
into __dma_rx_complete().

Reported-by: Gilles BULOZ <gilles.buloz@kontron.com>
Tested-by: Gilles BULOZ <gilles.buloz@kontron.com>
Fixes: 9ee4b83e51f7 ("serial: 8250: Add support for dmaengine")
Cc: stable@vger.kernel.org
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230130114841.25749-2-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/tty/serial/8250/8250_dma.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index b85c82616e8c..cc521f6232fd 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -57,6 +57,18 @@ static void __dma_rx_complete(void *param)
 	tty_flip_buffer_push(tty_port);
 }
 
+static void dma_rx_complete(void *param)
+{
+	struct uart_8250_port *p = param;
+	struct uart_8250_dma *dma = p->dma;
+	unsigned long flags;
+
+	spin_lock_irqsave(&p->port.lock, flags);
+	if (dma->rx_running)
+		__dma_rx_complete(p);
+	spin_unlock_irqrestore(&p->port.lock, flags);
+}
+
 int serial8250_tx_dma(struct uart_8250_port *p)
 {
 	struct uart_8250_dma		*dma = p->dma;
@@ -130,7 +142,7 @@ int serial8250_rx_dma(struct uart_8250_port *p)
 		return -EBUSY;
 
 	dma->rx_running = 1;
-	desc->callback = __dma_rx_complete;
+	desc->callback = dma_rx_complete;
 	desc->callback_param = p;
 
 	dma->rx_cookie = dmaengine_submit(desc);
-- 
2.30.2


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

* [PATCH backport to 6.1 2/2] serial: 8250_dma: Fix DMA Rx rearm race
  2023-02-07  9:47 [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Ilpo Järvinen
@ 2023-02-07  9:47 ` Ilpo Järvinen
  2023-02-07 11:08 ` [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Greg Kroah-Hartman
  1 sibling, 0 replies; 3+ messages in thread
From: Ilpo Järvinen @ 2023-02-07  9:47 UTC (permalink / raw)
  To: stable; +Cc: Ilpo Järvinen, Gilles BULOZ, Greg Kroah-Hartman

[ Upstream commit 57e9af7831dcf211c5c689c2a6f209f4abdf0bce ]

As DMA Rx can be completed from two places, it is possible that DMA Rx
completes before DMA completion callback had a chance to complete it.
Once the previous DMA Rx has been completed, a new one can be started
on the next UART interrupt. The following race is possible
(uart_unlock_and_check_sysrq_irqrestore() replaced with
spin_unlock_irqrestore() for simplicity/clarity):

CPU0					CPU1
					dma_rx_complete()
serial8250_handle_irq()
  spin_lock_irqsave(&port->lock)
  handle_rx_dma()
    serial8250_rx_dma_flush()
      __dma_rx_complete()
        dma->rx_running = 0
        // Complete DMA Rx
  spin_unlock_irqrestore(&port->lock)

serial8250_handle_irq()
  spin_lock_irqsave(&port->lock)
  handle_rx_dma()
    serial8250_rx_dma()
      dma->rx_running = 1
      // Setup a new DMA Rx
  spin_unlock_irqrestore(&port->lock)

					  spin_lock_irqsave(&port->lock)
					  // sees dma->rx_running = 1
					  __dma_rx_complete()
					    dma->rx_running = 0
					    // Incorrectly complete
					    // running DMA Rx

This race seems somewhat theoretical to occur for real but handle it
correctly regardless. Check what is the DMA status before complething
anything in __dma_rx_complete().

Reported-by: Gilles BULOZ <gilles.buloz@kontron.com>
Tested-by: Gilles BULOZ <gilles.buloz@kontron.com>
Fixes: 9ee4b83e51f7 ("serial: 8250: Add support for dmaengine")
Cc: stable@vger.kernel.org
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230130114841.25749-3-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/tty/serial/8250/8250_dma.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index cc521f6232fd..a442f0dfd28e 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -44,15 +44,23 @@ static void __dma_rx_complete(void *param)
 	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;
 
-	dma->rx_running = 0;
-	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	/*
+	 * 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;
 
 	tty_insert_flip_string(tty_port, dma->rx_buf, count);
 	p->port.icount.rx += count;
+	dma->rx_running = 0;
 
 	tty_flip_buffer_push(tty_port);
 }
-- 
2.30.2


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

* Re: [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race
  2023-02-07  9:47 [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Ilpo Järvinen
  2023-02-07  9:47 ` [PATCH backport to 6.1 2/2] serial: 8250_dma: Fix DMA Rx rearm race Ilpo Järvinen
@ 2023-02-07 11:08 ` Greg Kroah-Hartman
  1 sibling, 0 replies; 3+ messages in thread
From: Greg Kroah-Hartman @ 2023-02-07 11:08 UTC (permalink / raw)
  To: Ilpo Järvinen; +Cc: stable, Gilles BULOZ

On Tue, Feb 07, 2023 at 11:47:16AM +0200, Ilpo Järvinen wrote:
> [ Upstream commit 31352811e13dc2313f101b890fd4b1ce760b5fe7 ]
> 
> __dma_rx_complete() is called from two places:
>   - Through the DMA completion callback dma_rx_complete()
>   - From serial8250_rx_dma_flush() after IIR_RLSI or IIR_RX_TIMEOUT
> The former does not hold port's lock during __dma_rx_complete() which
> allows these two to race and potentially insert the same data twice.
> 
> Extend port's lock coverage in dma_rx_complete() to prevent the race
> and check if the DMA Rx is still pending completion before calling
> into __dma_rx_complete().
> 
> Reported-by: Gilles BULOZ <gilles.buloz@kontron.com>
> Tested-by: Gilles BULOZ <gilles.buloz@kontron.com>
> Fixes: 9ee4b83e51f7 ("serial: 8250: Add support for dmaengine")
> Cc: stable@vger.kernel.org
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> Link: https://lore.kernel.org/r/20230130114841.25749-2-ilpo.jarvinen@linux.intel.com
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
>  drivers/tty/serial/8250/8250_dma.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)

Thanks, both queued up now.

greg k-h

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

end of thread, other threads:[~2023-02-07 11:08 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-07  9:47 [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Ilpo Järvinen
2023-02-07  9:47 ` [PATCH backport to 6.1 2/2] serial: 8250_dma: Fix DMA Rx rearm race Ilpo Järvinen
2023-02-07 11:08 ` [PATCH backport to 6.1 1/2] serial: 8250_dma: Fix DMA Rx completion race Greg Kroah-Hartman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.