On 2020-03-25 11:07 AM, Michael Walle wrote: > Move dma_request_chan() out of the atomic context. First this call > should not be in the atomic context at all and second the > dev_info_once() may cause a hang because because the console takes this > spinlock, too. > > Fixes: 159381df1442f ("tty: serial: fsl_lpuart: fix DMA operation when using IOMMU") > Reported-by: Leonard Crestez <leonard.crestez@xxxxxxx> > Signed-off-by: Michael Walle <michael@xxxxxxxx> > --- > changes since v1: > - instead of just moving the dev_info_once() out of the spinlock protected > section, move the whole dma_request_chan(). Thanks Andy! > > I've tested this on my board. Andy, Leonard, can you double check it? For > all which are not aware, this deadlock happens only if you have the kernel > console output on the lpuart, so if someone wants to test it, make sure you > have something like console=ttyLP0,115200. > > drivers/tty/serial/fsl_lpuart.c | 36 +++++++++++++++++++++------------ > 1 file changed, 23 insertions(+), 13 deletions(-) Tested-by: Leonard Crestez <leonard.crestez@xxxxxxx> Since the original commit only made it into next it might make sense to squash the commits in the tty tree. This way future bisections won't get stuck on a boot failure. > > diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c > index 9c6a018b1390..131018979b77 100644 > --- a/drivers/tty/serial/fsl_lpuart.c > +++ b/drivers/tty/serial/fsl_lpuart.c > @@ -1510,20 +1510,33 @@ static void rx_dma_timer_init(struct lpuart_port *sport) > add_timer(&sport->lpuart_timer); > } > > -static void lpuart_tx_dma_startup(struct lpuart_port *sport) > +static void lpuart_request_dma(struct lpuart_port *sport) > { > - u32 uartbaud; > - int ret; > - > sport->dma_tx_chan = dma_request_chan(sport->port.dev, "tx"); > if (IS_ERR(sport->dma_tx_chan)) { > dev_info_once(sport->port.dev, > "DMA tx channel request failed, operating without tx DMA (%ld)\n", > PTR_ERR(sport->dma_tx_chan)); > sport->dma_tx_chan = NULL; > - goto err; > } > > + sport->dma_rx_chan = dma_request_chan(sport->port.dev, "rx"); > + if (IS_ERR(sport->dma_rx_chan)) { > + dev_info_once(sport->port.dev, > + "DMA rx channel request failed, operating without rx DMA (%ld)\n", > + PTR_ERR(sport->dma_rx_chan)); > + sport->dma_rx_chan = NULL; > + } > +} > + > +static void lpuart_tx_dma_startup(struct lpuart_port *sport) > +{ > + u32 uartbaud; > + int ret; > + > + if (!sport->dma_tx_chan) > + goto err; > + > ret = lpuart_dma_tx_request(&sport->port); > if (!ret) > goto err; > @@ -1549,14 +1562,8 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport) > { > int ret; > > - sport->dma_rx_chan = dma_request_chan(sport->port.dev, "rx"); > - if (IS_ERR(sport->dma_rx_chan)) { > - dev_info_once(sport->port.dev, > - "DMA rx channel request failed, operating without rx DMA (%ld)\n", > - PTR_ERR(sport->dma_rx_chan)); > - sport->dma_rx_chan = NULL; > + if (!sport->dma_rx_chan) > goto err; > - } > > ret = lpuart_start_rx_dma(sport); > if (ret) > @@ -1592,6 +1599,8 @@ static int lpuart_startup(struct uart_port *port) > sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) & > UARTPFIFO_FIFOSIZE_MASK); > > + lpuart_request_dma(sport); > + > spin_lock_irqsave(&sport->port.lock, flags); > > lpuart_setup_watermark_enable(sport); > @@ -1649,11 +1658,12 @@ static int lpuart32_startup(struct uart_port *port) > sport->port.fifosize = sport->txfifo_size; > } > > + lpuart_request_dma(sport); > + > spin_lock_irqsave(&sport->port.lock, flags); > > lpuart32_setup_watermark_enable(sport); > > - > lpuart_rx_dma_startup(sport); > lpuart_tx_dma_startup(sport); > >