Skip to content

Commit 04928b7

Browse files
geertugregkh
authored andcommitted
serial: sh-sci: Fix race condition between RX worker and cleanup
During serial port shutdown, the DMA receive worker function may still be called after the receive DMA cleanup function has been called. Fix this race condition between work_fn_rx() and sci_rx_dma_release() by acquiring the port's spinlock in sci_rx_dma_release(). This requires releasing the spinlock in work_fn_rx() before calling (any function that may call) sci_rx_dma_release(). Terminate all active receive DMA descriptors to release them, and to make sure no more completions come in. Do the same in sci_tx_dma_release() for symmetry, although the serial upper layer will no longer submit more data at this point of time. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0907c10 commit 04928b7

File tree

1 file changed

+14
-5
lines changed

1 file changed

+14
-5
lines changed

drivers/tty/serial/sh-sci.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,9 +1362,13 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
13621362
{
13631363
struct dma_chan *chan = s->chan_rx;
13641364
struct uart_port *port = &s->port;
1365+
unsigned long flags;
13651366

1367+
spin_lock_irqsave(&port->lock, flags);
13661368
s->chan_rx = NULL;
13671369
s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
1370+
spin_unlock_irqrestore(&port->lock, flags);
1371+
dmaengine_terminate_all(chan);
13681372
dma_free_coherent(chan->device->dev, s->buf_len_rx * 2,
13691373
sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0]));
13701374
dma_release_channel(chan);
@@ -1376,9 +1380,13 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
13761380
{
13771381
struct dma_chan *chan = s->chan_tx;
13781382
struct uart_port *port = &s->port;
1383+
unsigned long flags;
13791384

1385+
spin_lock_irqsave(&port->lock, flags);
13801386
s->chan_tx = NULL;
13811387
s->cookie_tx = -EINVAL;
1388+
spin_unlock_irqrestore(&port->lock, flags);
1389+
dmaengine_terminate_all(chan);
13821390
dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
13831391
DMA_TO_DEVICE);
13841392
dma_release_channel(chan);
@@ -1444,7 +1452,8 @@ static void work_fn_rx(struct work_struct *work)
14441452
} else {
14451453
dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__,
14461454
s->active_rx);
1447-
goto out;
1455+
spin_unlock_irqrestore(&port->lock, flags);
1456+
return;
14481457
}
14491458

14501459
status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
@@ -1464,9 +1473,10 @@ static void work_fn_rx(struct work_struct *work)
14641473
if (count)
14651474
tty_flip_buffer_push(&port->state->port);
14661475

1467-
sci_submit_rx(s);
1476+
spin_unlock_irqrestore(&port->lock, flags);
14681477

1469-
goto out;
1478+
sci_submit_rx(s);
1479+
return;
14701480
}
14711481

14721482
desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1,
@@ -1485,14 +1495,13 @@ static void work_fn_rx(struct work_struct *work)
14851495

14861496
dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",
14871497
__func__, s->cookie_rx[new], new, s->active_rx);
1488-
out:
14891498
spin_unlock_irqrestore(&port->lock, flags);
14901499
return;
14911500

14921501
fail:
1502+
spin_unlock_irqrestore(&port->lock, flags);
14931503
dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
14941504
sci_rx_dma_release(s, true);
1495-
spin_unlock_irqrestore(&port->lock, flags);
14961505
}
14971506

14981507
static void work_fn_tx(struct work_struct *work)

0 commit comments

Comments
 (0)